Compare commits
97 Commits
version/20
...
version/20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
489573329e | ||
|
|
843df4b334 | ||
|
|
ef1821c025 | ||
|
|
ed5372f439 | ||
|
|
bc63381fb0 | ||
|
|
c59f2a0749 | ||
|
|
5c30ca5dc6 | ||
|
|
37bc43c7aa | ||
|
|
4edd3f45b7 | ||
|
|
087ea4c5f3 | ||
|
|
40534d6316 | ||
|
|
8621925e75 | ||
|
|
5cb2360985 | ||
|
|
b831d2b64a | ||
|
|
e81597a176 | ||
|
|
7df4f32de6 | ||
|
|
db3725880e | ||
|
|
af5833cbc5 | ||
|
|
9078a085c3 | ||
|
|
396a7d7f09 | ||
|
|
8a128a57cd | ||
|
|
3dca9b8a68 | ||
|
|
5f8f5a1c33 | ||
|
|
5a72a7d9f4 | ||
|
|
51a91bfa8a | ||
|
|
4cedd0a346 | ||
|
|
b989f4085d | ||
|
|
19cc797c41 | ||
|
|
99ee3d9dec | ||
|
|
455753c774 | ||
|
|
da099d4312 | ||
|
|
a88b9181b6 | ||
|
|
7f7c632914 | ||
|
|
e7356223bb | ||
|
|
7547ad0391 | ||
|
|
ca30d6bb3d | ||
|
|
ebd2cdb7a7 | ||
|
|
52b8f60953 | ||
|
|
ab1e2d49ab | ||
|
|
401bd1c643 | ||
|
|
53e6bec425 | ||
|
|
f6ded69fa3 | ||
|
|
68053d64b5 | ||
|
|
1f12966628 | ||
|
|
8d3723c91f | ||
|
|
7dcc359110 | ||
|
|
854881bba2 | ||
|
|
ddb7958f40 | ||
|
|
fe0d3fd448 | ||
|
|
15d5c878f3 | ||
|
|
4f63c3f8a8 | ||
|
|
b766ce76ff | ||
|
|
0b4b09958f | ||
|
|
e12fc5a89d | ||
|
|
bd3c351f1f | ||
|
|
6064be33e5 | ||
|
|
1540d6f472 | ||
|
|
1310092c0c | ||
|
|
e485fac8ed | ||
|
|
55d75f18de | ||
|
|
e8648a3f71 | ||
|
|
0c7cabe46f | ||
|
|
a9ec3be2fd | ||
|
|
abaaee1af2 | ||
|
|
6283a515b9 | ||
|
|
ce7d463710 | ||
|
|
bc3404fcbe | ||
|
|
bd87d3963a | ||
|
|
fed449a801 | ||
|
|
19dd92d3e0 | ||
|
|
f22b9ba9f1 | ||
|
|
fedafb9352 | ||
|
|
18f0484249 | ||
|
|
10956056b3 | ||
|
|
e482f04123 | ||
|
|
db89f0d4d1 | ||
|
|
2ac97a9f1f | ||
|
|
c03359a189 | ||
|
|
84b636debc | ||
|
|
2642299d77 | ||
|
|
2a60e5e338 | ||
|
|
880c063d04 | ||
|
|
7a374c43dc | ||
|
|
7be1fcc32e | ||
|
|
b57dca66be | ||
|
|
d455f5f445 | ||
|
|
2200fad30e | ||
|
|
e5e112c3c2 | ||
|
|
dd3cdf63e6 | ||
|
|
a800189c25 | ||
|
|
b342245619 | ||
|
|
6cd5ac2d0d | ||
|
|
872a48dacb | ||
|
|
721aa544c9 | ||
|
|
7bfbda7188 | ||
|
|
602244979b | ||
|
|
8e29cae309 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -14,3 +14,4 @@ CTestTestfile.cmake
|
||||
install_manifest.txt
|
||||
build*
|
||||
Build
|
||||
CMakeLists.txt.user
|
||||
|
||||
2
3rdparty/CMakeLists.txt
vendored
2
3rdparty/CMakeLists.txt
vendored
@@ -2,8 +2,6 @@ if (NOT SYSTEM_EXPAT)
|
||||
add_subdirectory(expat)
|
||||
endif()
|
||||
|
||||
add_subdirectory(utf8)
|
||||
|
||||
if (ENABLE_DNS AND NOT SYSTEM_UDNS)
|
||||
add_subdirectory(udns)
|
||||
endif()
|
||||
|
||||
17
3rdparty/utf8/CMakeLists.txt
vendored
17
3rdparty/utf8/CMakeLists.txt
vendored
@@ -1,17 +0,0 @@
|
||||
include (SimGearComponent)
|
||||
|
||||
set(HEADERS
|
||||
source/utf8.h
|
||||
)
|
||||
|
||||
set(HEADERS_utf8
|
||||
source/utf8/checked.h
|
||||
source/utf8/core.h
|
||||
source/utf8/unchecked.h
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
)
|
||||
|
||||
simgear_component(utf8 3rdparty/utf8 "${SOURCES}" "${HEADERS}")
|
||||
simgear_component(utf8-internal 3rdparty/utf8/utf8 "" "${HEADERS_utf8}")
|
||||
12
3rdparty/utf8/doc/ReleaseNotes
vendored
12
3rdparty/utf8/doc/ReleaseNotes
vendored
@@ -1,12 +0,0 @@
|
||||
utf8 cpp library
|
||||
Release 2.3.4
|
||||
|
||||
A minor bug fix release. Thanks to all who reported bugs.
|
||||
|
||||
Note: Version 2.3.3 contained a regression, and therefore was removed.
|
||||
|
||||
Changes from version 2.3.2
|
||||
- Bug fix [39]: checked.h Line 273 and unchecked.h Line 182 have an extra ';'
|
||||
- Bug fix [36]: replace_invalid() only works with back_inserter
|
||||
|
||||
Files included in the release: utf8.h, core.h, checked.h, unchecked.h, utf8cpp.html, ReleaseNotes
|
||||
1789
3rdparty/utf8/doc/utf8cpp.html
vendored
1789
3rdparty/utf8/doc/utf8cpp.html
vendored
File diff suppressed because it is too large
Load Diff
34
3rdparty/utf8/source/utf8.h
vendored
34
3rdparty/utf8/source/utf8.h
vendored
@@ -1,34 +0,0 @@
|
||||
// Copyright 2006 Nemanja Trifunovic
|
||||
|
||||
/*
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
#define UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
|
||||
#include "utf8/checked.h"
|
||||
#include "utf8/unchecked.h"
|
||||
|
||||
#endif // header guard
|
||||
327
3rdparty/utf8/source/utf8/checked.h
vendored
327
3rdparty/utf8/source/utf8/checked.h
vendored
@@ -1,327 +0,0 @@
|
||||
// Copyright 2006 Nemanja Trifunovic
|
||||
|
||||
/*
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
#define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
|
||||
#include "core.h"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace utf8
|
||||
{
|
||||
// Base for the exceptions that may be thrown from the library
|
||||
class exception : public ::std::exception {
|
||||
};
|
||||
|
||||
// Exceptions that may be thrown from the library functions.
|
||||
class invalid_code_point : public exception {
|
||||
uint32_t cp;
|
||||
public:
|
||||
invalid_code_point(uint32_t cp) : cp(cp) {}
|
||||
virtual const char* what() const throw() { return "Invalid code point"; }
|
||||
uint32_t code_point() const {return cp;}
|
||||
};
|
||||
|
||||
class invalid_utf8 : public exception {
|
||||
uint8_t u8;
|
||||
public:
|
||||
invalid_utf8 (uint8_t u) : u8(u) {}
|
||||
virtual const char* what() const throw() { return "Invalid UTF-8"; }
|
||||
uint8_t utf8_octet() const {return u8;}
|
||||
};
|
||||
|
||||
class invalid_utf16 : public exception {
|
||||
uint16_t u16;
|
||||
public:
|
||||
invalid_utf16 (uint16_t u) : u16(u) {}
|
||||
virtual const char* what() const throw() { return "Invalid UTF-16"; }
|
||||
uint16_t utf16_word() const {return u16;}
|
||||
};
|
||||
|
||||
class not_enough_room : public exception {
|
||||
public:
|
||||
virtual const char* what() const throw() { return "Not enough space"; }
|
||||
};
|
||||
|
||||
/// The library API - functions intended to be called by the users
|
||||
|
||||
template <typename octet_iterator>
|
||||
octet_iterator append(uint32_t cp, octet_iterator result)
|
||||
{
|
||||
if (!utf8::internal::is_code_point_valid(cp))
|
||||
throw invalid_code_point(cp);
|
||||
|
||||
if (cp < 0x80) // one octet
|
||||
*(result++) = static_cast<uint8_t>(cp);
|
||||
else if (cp < 0x800) { // two octets
|
||||
*(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0);
|
||||
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
else if (cp < 0x10000) { // three octets
|
||||
*(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0);
|
||||
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
else { // four octets
|
||||
*(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0);
|
||||
*(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename output_iterator>
|
||||
output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement)
|
||||
{
|
||||
while (start != end) {
|
||||
octet_iterator sequence_start = start;
|
||||
internal::utf_error err_code = utf8::internal::validate_next(start, end);
|
||||
switch (err_code) {
|
||||
case internal::UTF8_OK :
|
||||
for (octet_iterator it = sequence_start; it != start; ++it)
|
||||
*out++ = *it;
|
||||
break;
|
||||
case internal::NOT_ENOUGH_ROOM:
|
||||
throw not_enough_room();
|
||||
case internal::INVALID_LEAD:
|
||||
out = utf8::append (replacement, out);
|
||||
++start;
|
||||
break;
|
||||
case internal::INCOMPLETE_SEQUENCE:
|
||||
case internal::OVERLONG_SEQUENCE:
|
||||
case internal::INVALID_CODE_POINT:
|
||||
out = utf8::append (replacement, out);
|
||||
++start;
|
||||
// just one replacement mark for the sequence
|
||||
while (start != end && utf8::internal::is_trail(*start))
|
||||
++start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename output_iterator>
|
||||
inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out)
|
||||
{
|
||||
static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd);
|
||||
return utf8::replace_invalid(start, end, out, replacement_marker);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
uint32_t next(octet_iterator& it, octet_iterator end)
|
||||
{
|
||||
uint32_t cp = 0;
|
||||
internal::utf_error err_code = utf8::internal::validate_next(it, end, cp);
|
||||
switch (err_code) {
|
||||
case internal::UTF8_OK :
|
||||
break;
|
||||
case internal::NOT_ENOUGH_ROOM :
|
||||
throw not_enough_room();
|
||||
case internal::INVALID_LEAD :
|
||||
case internal::INCOMPLETE_SEQUENCE :
|
||||
case internal::OVERLONG_SEQUENCE :
|
||||
throw invalid_utf8(*it);
|
||||
case internal::INVALID_CODE_POINT :
|
||||
throw invalid_code_point(cp);
|
||||
}
|
||||
return cp;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
uint32_t peek_next(octet_iterator it, octet_iterator end)
|
||||
{
|
||||
return utf8::next(it, end);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
uint32_t prior(octet_iterator& it, octet_iterator start)
|
||||
{
|
||||
// can't do much if it == start
|
||||
if (it == start)
|
||||
throw not_enough_room();
|
||||
|
||||
octet_iterator end = it;
|
||||
// Go back until we hit either a lead octet or start
|
||||
while (utf8::internal::is_trail(*(--it)))
|
||||
if (it == start)
|
||||
throw invalid_utf8(*it); // error - no lead byte in the sequence
|
||||
return utf8::peek_next(it, end);
|
||||
}
|
||||
|
||||
/// Deprecated in versions that include "prior"
|
||||
template <typename octet_iterator>
|
||||
uint32_t previous(octet_iterator& it, octet_iterator pass_start)
|
||||
{
|
||||
octet_iterator end = it;
|
||||
while (utf8::internal::is_trail(*(--it)))
|
||||
if (it == pass_start)
|
||||
throw invalid_utf8(*it); // error - no lead byte in the sequence
|
||||
octet_iterator temp = it;
|
||||
return utf8::next(temp, end);
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename distance_type>
|
||||
void advance (octet_iterator& it, distance_type n, octet_iterator end)
|
||||
{
|
||||
for (distance_type i = 0; i < n; ++i)
|
||||
utf8::next(it, end);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
typename std::iterator_traits<octet_iterator>::difference_type
|
||||
distance (octet_iterator first, octet_iterator last)
|
||||
{
|
||||
typename std::iterator_traits<octet_iterator>::difference_type dist;
|
||||
for (dist = 0; first < last; ++dist)
|
||||
utf8::next(first, last);
|
||||
return dist;
|
||||
}
|
||||
|
||||
template <typename u16bit_iterator, typename octet_iterator>
|
||||
octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)
|
||||
{
|
||||
while (start != end) {
|
||||
uint32_t cp = utf8::internal::mask16(*start++);
|
||||
// Take care of surrogate pairs first
|
||||
if (utf8::internal::is_lead_surrogate(cp)) {
|
||||
if (start != end) {
|
||||
uint32_t trail_surrogate = utf8::internal::mask16(*start++);
|
||||
if (utf8::internal::is_trail_surrogate(trail_surrogate))
|
||||
cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;
|
||||
else
|
||||
throw invalid_utf16(static_cast<uint16_t>(trail_surrogate));
|
||||
}
|
||||
else
|
||||
throw invalid_utf16(static_cast<uint16_t>(cp));
|
||||
|
||||
}
|
||||
// Lone trail surrogate
|
||||
else if (utf8::internal::is_trail_surrogate(cp))
|
||||
throw invalid_utf16(static_cast<uint16_t>(cp));
|
||||
|
||||
result = utf8::append(cp, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename u16bit_iterator, typename octet_iterator>
|
||||
u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)
|
||||
{
|
||||
while (start != end) {
|
||||
uint32_t cp = utf8::next(start, end);
|
||||
if (cp > 0xffff) { //make a surrogate pair
|
||||
*result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET);
|
||||
*result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);
|
||||
}
|
||||
else
|
||||
*result++ = static_cast<uint16_t>(cp);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename u32bit_iterator>
|
||||
octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result)
|
||||
{
|
||||
while (start != end)
|
||||
result = utf8::append(*(start++), result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename u32bit_iterator>
|
||||
u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)
|
||||
{
|
||||
while (start != end)
|
||||
(*result++) = utf8::next(start, end);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// The iterator class
|
||||
template <typename octet_iterator>
|
||||
class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> {
|
||||
octet_iterator it;
|
||||
octet_iterator range_start;
|
||||
octet_iterator range_end;
|
||||
public:
|
||||
iterator () {}
|
||||
explicit iterator (const octet_iterator& octet_it,
|
||||
const octet_iterator& range_start,
|
||||
const octet_iterator& range_end) :
|
||||
it(octet_it), range_start(range_start), range_end(range_end)
|
||||
{
|
||||
if (it < range_start || it > range_end)
|
||||
throw std::out_of_range("Invalid utf-8 iterator position");
|
||||
}
|
||||
// the default "big three" are OK
|
||||
octet_iterator base () const { return it; }
|
||||
uint32_t operator * () const
|
||||
{
|
||||
octet_iterator temp = it;
|
||||
return utf8::next(temp, range_end);
|
||||
}
|
||||
bool operator == (const iterator& rhs) const
|
||||
{
|
||||
if (range_start != rhs.range_start || range_end != rhs.range_end)
|
||||
throw std::logic_error("Comparing utf-8 iterators defined with different ranges");
|
||||
return (it == rhs.it);
|
||||
}
|
||||
bool operator != (const iterator& rhs) const
|
||||
{
|
||||
return !(operator == (rhs));
|
||||
}
|
||||
iterator& operator ++ ()
|
||||
{
|
||||
utf8::next(it, range_end);
|
||||
return *this;
|
||||
}
|
||||
iterator operator ++ (int)
|
||||
{
|
||||
iterator temp = *this;
|
||||
utf8::next(it, range_end);
|
||||
return temp;
|
||||
}
|
||||
iterator& operator -- ()
|
||||
{
|
||||
utf8::prior(it, range_start);
|
||||
return *this;
|
||||
}
|
||||
iterator operator -- (int)
|
||||
{
|
||||
iterator temp = *this;
|
||||
utf8::prior(it, range_start);
|
||||
return temp;
|
||||
}
|
||||
}; // class iterator
|
||||
|
||||
} // namespace utf8
|
||||
|
||||
#endif //header guard
|
||||
|
||||
|
||||
329
3rdparty/utf8/source/utf8/core.h
vendored
329
3rdparty/utf8/source/utf8/core.h
vendored
@@ -1,329 +0,0 @@
|
||||
// Copyright 2006 Nemanja Trifunovic
|
||||
|
||||
/*
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
#define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
|
||||
#include <iterator>
|
||||
|
||||
namespace utf8
|
||||
{
|
||||
// The typedefs for 8-bit, 16-bit and 32-bit unsigned integers
|
||||
// You may need to change them to match your system.
|
||||
// These typedefs have the same names as ones from cstdint, or boost/cstdint
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
|
||||
// Helper code - not intended to be directly called by the library users. May be changed at any time
|
||||
namespace internal
|
||||
{
|
||||
// Unicode constants
|
||||
// Leading (high) surrogates: 0xd800 - 0xdbff
|
||||
// Trailing (low) surrogates: 0xdc00 - 0xdfff
|
||||
const uint16_t LEAD_SURROGATE_MIN = 0xd800u;
|
||||
const uint16_t LEAD_SURROGATE_MAX = 0xdbffu;
|
||||
const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u;
|
||||
const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu;
|
||||
const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10);
|
||||
const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN;
|
||||
|
||||
// Maximum valid value for a Unicode code point
|
||||
const uint32_t CODE_POINT_MAX = 0x0010ffffu;
|
||||
|
||||
template<typename octet_type>
|
||||
inline uint8_t mask8(octet_type oc)
|
||||
{
|
||||
return static_cast<uint8_t>(0xff & oc);
|
||||
}
|
||||
template<typename u16_type>
|
||||
inline uint16_t mask16(u16_type oc)
|
||||
{
|
||||
return static_cast<uint16_t>(0xffff & oc);
|
||||
}
|
||||
template<typename octet_type>
|
||||
inline bool is_trail(octet_type oc)
|
||||
{
|
||||
return ((utf8::internal::mask8(oc) >> 6) == 0x2);
|
||||
}
|
||||
|
||||
template <typename u16>
|
||||
inline bool is_lead_surrogate(u16 cp)
|
||||
{
|
||||
return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX);
|
||||
}
|
||||
|
||||
template <typename u16>
|
||||
inline bool is_trail_surrogate(u16 cp)
|
||||
{
|
||||
return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);
|
||||
}
|
||||
|
||||
template <typename u16>
|
||||
inline bool is_surrogate(u16 cp)
|
||||
{
|
||||
return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);
|
||||
}
|
||||
|
||||
template <typename u32>
|
||||
inline bool is_code_point_valid(u32 cp)
|
||||
{
|
||||
return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp));
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
inline typename std::iterator_traits<octet_iterator>::difference_type
|
||||
sequence_length(octet_iterator lead_it)
|
||||
{
|
||||
uint8_t lead = utf8::internal::mask8(*lead_it);
|
||||
if (lead < 0x80)
|
||||
return 1;
|
||||
else if ((lead >> 5) == 0x6)
|
||||
return 2;
|
||||
else if ((lead >> 4) == 0xe)
|
||||
return 3;
|
||||
else if ((lead >> 3) == 0x1e)
|
||||
return 4;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename octet_difference_type>
|
||||
inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length)
|
||||
{
|
||||
if (cp < 0x80) {
|
||||
if (length != 1)
|
||||
return true;
|
||||
}
|
||||
else if (cp < 0x800) {
|
||||
if (length != 2)
|
||||
return true;
|
||||
}
|
||||
else if (cp < 0x10000) {
|
||||
if (length != 3)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT};
|
||||
|
||||
/// Helper for get_sequence_x
|
||||
template <typename octet_iterator>
|
||||
utf_error increase_safely(octet_iterator& it, octet_iterator end)
|
||||
{
|
||||
if (++it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
if (!utf8::internal::is_trail(*it))
|
||||
return INCOMPLETE_SEQUENCE;
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
#define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;}
|
||||
|
||||
/// get_sequence_x functions decode utf-8 sequences of the length x
|
||||
template <typename octet_iterator>
|
||||
utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
code_point = utf8::internal::mask8(*it);
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
code_point = utf8::internal::mask8(*it);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f);
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
code_point = utf8::internal::mask8(*it);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point += (*it) & 0x3f;
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
code_point = utf8::internal::mask8(*it);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point += (utf8::internal::mask8(*it) << 6) & 0xfff;
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point += (*it) & 0x3f;
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
#undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR
|
||||
|
||||
template <typename octet_iterator>
|
||||
utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
// Save the original value of it so we can go back in case of failure
|
||||
// Of course, it does not make much sense with i.e. stream iterators
|
||||
octet_iterator original_it = it;
|
||||
|
||||
uint32_t cp = 0;
|
||||
// Determine the sequence length based on the lead octet
|
||||
typedef typename std::iterator_traits<octet_iterator>::difference_type octet_difference_type;
|
||||
const octet_difference_type length = utf8::internal::sequence_length(it);
|
||||
|
||||
// Get trail octets and calculate the code point
|
||||
utf_error err = UTF8_OK;
|
||||
switch (length) {
|
||||
case 0:
|
||||
return INVALID_LEAD;
|
||||
case 1:
|
||||
err = utf8::internal::get_sequence_1(it, end, cp);
|
||||
break;
|
||||
case 2:
|
||||
err = utf8::internal::get_sequence_2(it, end, cp);
|
||||
break;
|
||||
case 3:
|
||||
err = utf8::internal::get_sequence_3(it, end, cp);
|
||||
break;
|
||||
case 4:
|
||||
err = utf8::internal::get_sequence_4(it, end, cp);
|
||||
break;
|
||||
}
|
||||
|
||||
if (err == UTF8_OK) {
|
||||
// Decoding succeeded. Now, security checks...
|
||||
if (utf8::internal::is_code_point_valid(cp)) {
|
||||
if (!utf8::internal::is_overlong_sequence(cp, length)){
|
||||
// Passed! Return here.
|
||||
code_point = cp;
|
||||
++it;
|
||||
return UTF8_OK;
|
||||
}
|
||||
else
|
||||
err = OVERLONG_SEQUENCE;
|
||||
}
|
||||
else
|
||||
err = INVALID_CODE_POINT;
|
||||
}
|
||||
|
||||
// Failure branch - restore the original value of the iterator
|
||||
it = original_it;
|
||||
return err;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
inline utf_error validate_next(octet_iterator& it, octet_iterator end) {
|
||||
uint32_t ignored;
|
||||
return utf8::internal::validate_next(it, end, ignored);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/// The library API - functions intended to be called by the users
|
||||
|
||||
// Byte order mark
|
||||
const uint8_t bom[] = {0xef, 0xbb, 0xbf};
|
||||
|
||||
template <typename octet_iterator>
|
||||
octet_iterator find_invalid(octet_iterator start, octet_iterator end)
|
||||
{
|
||||
octet_iterator result = start;
|
||||
while (result != end) {
|
||||
utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end);
|
||||
if (err_code != internal::UTF8_OK)
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
inline bool is_valid(octet_iterator start, octet_iterator end)
|
||||
{
|
||||
return (utf8::find_invalid(start, end) == end);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
inline bool starts_with_bom (octet_iterator it, octet_iterator end)
|
||||
{
|
||||
return (
|
||||
((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) &&
|
||||
((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) &&
|
||||
((it != end) && (utf8::internal::mask8(*it)) == bom[2])
|
||||
);
|
||||
}
|
||||
|
||||
//Deprecated in release 2.3
|
||||
template <typename octet_iterator>
|
||||
inline bool is_bom (octet_iterator it)
|
||||
{
|
||||
return (
|
||||
(utf8::internal::mask8(*it++)) == bom[0] &&
|
||||
(utf8::internal::mask8(*it++)) == bom[1] &&
|
||||
(utf8::internal::mask8(*it)) == bom[2]
|
||||
);
|
||||
}
|
||||
} // namespace utf8
|
||||
|
||||
#endif // header guard
|
||||
|
||||
|
||||
228
3rdparty/utf8/source/utf8/unchecked.h
vendored
228
3rdparty/utf8/source/utf8/unchecked.h
vendored
@@ -1,228 +0,0 @@
|
||||
// Copyright 2006 Nemanja Trifunovic
|
||||
|
||||
/*
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
#define UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
|
||||
#include "core.h"
|
||||
|
||||
namespace utf8
|
||||
{
|
||||
namespace unchecked
|
||||
{
|
||||
template <typename octet_iterator>
|
||||
octet_iterator append(uint32_t cp, octet_iterator result)
|
||||
{
|
||||
if (cp < 0x80) // one octet
|
||||
*(result++) = static_cast<uint8_t>(cp);
|
||||
else if (cp < 0x800) { // two octets
|
||||
*(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0);
|
||||
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
else if (cp < 0x10000) { // three octets
|
||||
*(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0);
|
||||
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
else { // four octets
|
||||
*(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0);
|
||||
*(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f)| 0x80);
|
||||
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
uint32_t next(octet_iterator& it)
|
||||
{
|
||||
uint32_t cp = utf8::internal::mask8(*it);
|
||||
typename std::iterator_traits<octet_iterator>::difference_type length = utf8::internal::sequence_length(it);
|
||||
switch (length) {
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
it++;
|
||||
cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f);
|
||||
break;
|
||||
case 3:
|
||||
++it;
|
||||
cp = ((cp << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff);
|
||||
++it;
|
||||
cp += (*it) & 0x3f;
|
||||
break;
|
||||
case 4:
|
||||
++it;
|
||||
cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff);
|
||||
++it;
|
||||
cp += (utf8::internal::mask8(*it) << 6) & 0xfff;
|
||||
++it;
|
||||
cp += (*it) & 0x3f;
|
||||
break;
|
||||
}
|
||||
++it;
|
||||
return cp;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
uint32_t peek_next(octet_iterator it)
|
||||
{
|
||||
return utf8::unchecked::next(it);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
uint32_t prior(octet_iterator& it)
|
||||
{
|
||||
while (utf8::internal::is_trail(*(--it))) ;
|
||||
octet_iterator temp = it;
|
||||
return utf8::unchecked::next(temp);
|
||||
}
|
||||
|
||||
// Deprecated in versions that include prior, but only for the sake of consistency (see utf8::previous)
|
||||
template <typename octet_iterator>
|
||||
inline uint32_t previous(octet_iterator& it)
|
||||
{
|
||||
return utf8::unchecked::prior(it);
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename distance_type>
|
||||
void advance (octet_iterator& it, distance_type n)
|
||||
{
|
||||
for (distance_type i = 0; i < n; ++i)
|
||||
utf8::unchecked::next(it);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
typename std::iterator_traits<octet_iterator>::difference_type
|
||||
distance (octet_iterator first, octet_iterator last)
|
||||
{
|
||||
typename std::iterator_traits<octet_iterator>::difference_type dist;
|
||||
for (dist = 0; first < last; ++dist)
|
||||
utf8::unchecked::next(first);
|
||||
return dist;
|
||||
}
|
||||
|
||||
template <typename u16bit_iterator, typename octet_iterator>
|
||||
octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)
|
||||
{
|
||||
while (start != end) {
|
||||
uint32_t cp = utf8::internal::mask16(*start++);
|
||||
// Take care of surrogate pairs first
|
||||
if (utf8::internal::is_lead_surrogate(cp)) {
|
||||
uint32_t trail_surrogate = utf8::internal::mask16(*start++);
|
||||
cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;
|
||||
}
|
||||
result = utf8::unchecked::append(cp, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename u16bit_iterator, typename octet_iterator>
|
||||
u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)
|
||||
{
|
||||
while (start < end) {
|
||||
uint32_t cp = utf8::unchecked::next(start);
|
||||
if (cp > 0xffff) { //make a surrogate pair
|
||||
*result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET);
|
||||
*result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);
|
||||
}
|
||||
else
|
||||
*result++ = static_cast<uint16_t>(cp);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename u32bit_iterator>
|
||||
octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result)
|
||||
{
|
||||
while (start != end)
|
||||
result = utf8::unchecked::append(*(start++), result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename u32bit_iterator>
|
||||
u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)
|
||||
{
|
||||
while (start < end)
|
||||
(*result++) = utf8::unchecked::next(start);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// The iterator class
|
||||
template <typename octet_iterator>
|
||||
class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> {
|
||||
octet_iterator it;
|
||||
public:
|
||||
iterator () {}
|
||||
explicit iterator (const octet_iterator& octet_it): it(octet_it) {}
|
||||
// the default "big three" are OK
|
||||
octet_iterator base () const { return it; }
|
||||
uint32_t operator * () const
|
||||
{
|
||||
octet_iterator temp = it;
|
||||
return utf8::unchecked::next(temp);
|
||||
}
|
||||
bool operator == (const iterator& rhs) const
|
||||
{
|
||||
return (it == rhs.it);
|
||||
}
|
||||
bool operator != (const iterator& rhs) const
|
||||
{
|
||||
return !(operator == (rhs));
|
||||
}
|
||||
iterator& operator ++ ()
|
||||
{
|
||||
::std::advance(it, utf8::internal::sequence_length(it));
|
||||
return *this;
|
||||
}
|
||||
iterator operator ++ (int)
|
||||
{
|
||||
iterator temp = *this;
|
||||
::std::advance(it, utf8::internal::sequence_length(it));
|
||||
return temp;
|
||||
}
|
||||
iterator& operator -- ()
|
||||
{
|
||||
utf8::unchecked::prior(it);
|
||||
return *this;
|
||||
}
|
||||
iterator operator -- (int)
|
||||
{
|
||||
iterator temp = *this;
|
||||
utf8::unchecked::prior(it);
|
||||
return temp;
|
||||
}
|
||||
}; // class iterator
|
||||
|
||||
} // namespace utf8::unchecked
|
||||
} // namespace utf8
|
||||
|
||||
|
||||
#endif // header guard
|
||||
|
||||
@@ -7,6 +7,9 @@ if(COMMAND cmake_policy)
|
||||
if(POLICY CMP0042)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
endif()
|
||||
if(POLICY CMP0067)
|
||||
cmake_policy(SET CMP0067 NEW)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -49,7 +52,7 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7")
|
||||
# add a dependency on the versino file
|
||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS version)
|
||||
|
||||
set(FIND_LIBRARY_USE_LIB64_PATHS ON)
|
||||
set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS TRUE)
|
||||
|
||||
# use simgear version also as the SO version (if building SOs)
|
||||
SET(SIMGEAR_SOVERSION ${SIMGEAR_VERSION})
|
||||
@@ -135,6 +138,16 @@ include (DetectArch)
|
||||
# keep the compatability link option as the default
|
||||
option(OSG_FSTREAM_EXPORT_FIXED "Set to ON if the osgDB fstream export patch is applied" OFF)
|
||||
|
||||
if (CMAKE_COMPILER_IS_GNUCXX OR CLANG)
|
||||
if (CMAKE_VERSION VERSION_LESS 3.1)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++${CMAKE_CXX_STANDARD}")
|
||||
elseif (CMAKE_VERSION VERSION_LESS 3.8)
|
||||
# policy CMP0067 (try_compile does not honor CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++${CMAKE_CXX_STANDARD}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
if (MSVC)
|
||||
GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} PATH)
|
||||
if (CMAKE_CL_64)
|
||||
@@ -343,7 +356,7 @@ 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
|
||||
check_cxx_source_compiles(
|
||||
"#include <cstdlib>
|
||||
"#include <cmath>
|
||||
int main() { return std::isnan(0.0);} "
|
||||
HAVE_STD_ISNAN)
|
||||
|
||||
@@ -351,14 +364,35 @@ if (NOT ${HAVE_STD_ISNAN})
|
||||
message(FATAL_ERROR "Your compiler lacks C++11 std::isnan, please update it")
|
||||
endif()
|
||||
|
||||
# Check if the <regex> implementation in the C++ standard library is usable.
|
||||
# This is necessary because g++ 4.8 lies about its C++11 compliance: its
|
||||
# <regex> is utterly unusable, cf. [1].
|
||||
# The big preprocessor test essentially comes from [2], and gcc upstream devs
|
||||
# appear to back it (see comments following [2], as well as [3]).
|
||||
#
|
||||
# [1] https://stackoverflow.com/a/12665408/4756009
|
||||
# [2] https://stackoverflow.com/a/41186162/4756009
|
||||
# [3] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78905
|
||||
check_cxx_source_compiles(
|
||||
"#include <regex>
|
||||
|
||||
int main() {
|
||||
#if __cplusplus >= 201103L && \
|
||||
(!defined(__GLIBCXX__) || \
|
||||
(__cplusplus >= 201402L) || \
|
||||
defined(_GLIBCXX_REGEX_DFS_QUANTIFIERS_LIMIT) || \
|
||||
defined(_GLIBCXX_REGEX_STATE_LIMIT) || \
|
||||
(defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE > 4))
|
||||
#else
|
||||
nullptr = void; // intentionally trigger a compilation error
|
||||
#endif
|
||||
}"
|
||||
HAVE_WORKING_STD_REGEX)
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(WARNING_FLAGS_CXX "-Wall -fPIC")
|
||||
set(WARNING_FLAGS_C "-Wall -fPIC")
|
||||
|
||||
if (CMAKE_VERSION VERSION_LESS 3.1)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.4)
|
||||
message(WARNING "GCC 4.4 will be required soon, please upgrade")
|
||||
endif()
|
||||
@@ -386,10 +420,6 @@ if (CLANG)
|
||||
# fix Boost compilation :(
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
|
||||
if (CMAKE_VERSION VERSION_LESS 3.1)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
endif()
|
||||
|
||||
if(ENABLE_SIMD)
|
||||
if (X86 OR X86_64)
|
||||
set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse")
|
||||
@@ -462,6 +492,8 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS_C} ${MSVC_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS_CXX} ${MSVC_FLAGS} ${BOOST_CXX_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MSVC_LD_FLAGS}")
|
||||
|
||||
include(CheckCXXFeatures)
|
||||
|
||||
# use BEFORE to ensure local directories are used first,
|
||||
# ahead of system-installed libs
|
||||
include_directories(BEFORE ${PROJECT_BINARY_DIR}/simgear)
|
||||
|
||||
30
CMakeModules/CheckCXXFeatures.cmake
Normal file
30
CMakeModules/CheckCXXFeatures.cmake
Normal file
@@ -0,0 +1,30 @@
|
||||
check_cxx_source_compiles("
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
std::make_index_sequence<0> t;
|
||||
int main() {}" HAVE_STD_INDEX_SEQUENCE
|
||||
)
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <type_traits>
|
||||
std::remove_cv_t<const int> t;
|
||||
int main() {}" HAVE_STD_REMOVE_CV_T
|
||||
)
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <type_traits>
|
||||
std::remove_cvref_t<const int&> t;
|
||||
int main() {}" HAVE_STD_REMOVE_CVREF_T
|
||||
)
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <type_traits>
|
||||
std::enable_if_t<true, int> t;
|
||||
int main() {}" HAVE_STD_ENABLE_IF_T
|
||||
)
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <type_traits>
|
||||
std::bool_constant<true> t;
|
||||
int main() {}" HAVE_STD_BOOL_CONSTANT
|
||||
)
|
||||
@@ -10,12 +10,13 @@
|
||||
#
|
||||
# Created by Erik Hofman.
|
||||
|
||||
FIND_PATH(AAX_INCLUDE_DIR aax/aeonwave.hpp
|
||||
FIND_PATH(AAX_INCLUDE_DIR aax/aax.h
|
||||
HINTS
|
||||
$ENV{AAXDIR}
|
||||
$ENV{ProgramFiles}/aax
|
||||
$ENV{ProgramFiles}/AeonWave
|
||||
$ENV{ProgramFiles}/Adalin/AeonWave
|
||||
${CMAKE_SOURCE_DIR}/aax
|
||||
PATH_SUFFIXES include
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
@@ -26,23 +27,35 @@ FIND_PATH(AAX_INCLUDE_DIR aax/aeonwave.hpp
|
||||
)
|
||||
|
||||
FIND_LIBRARY(AAX_LIBRARY
|
||||
NAMES AAX aax AAX32 libAAX32
|
||||
NAMES AAX aax AAX32
|
||||
HINTS
|
||||
$ENV{AAXDIR}
|
||||
$ENV{ProgramFiles}/AAX
|
||||
$ENV{ProgramFiles}/AeonWave
|
||||
$ENV{ProgramFiles}/Adalin/AeonWave
|
||||
${CMAKE_BUILD_DIR}/aax
|
||||
PATH_SUFFIXES bin lib lib/${CMAKE_LIBRARY_ARCHITECTURE} lib64 libs64 libs libs/Win32 libs/Win64
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local
|
||||
/usr
|
||||
/opt
|
||||
/usr/local
|
||||
)
|
||||
|
||||
SET(AAX_FOUND "NO")
|
||||
IF(AAX_LIBRARY AND AAX_INCLUDE_DIR)
|
||||
SET(AAX_FOUND "YES")
|
||||
ELSE(AAX_LIBRARY AND AAX_INCLUDE_DIR)
|
||||
IF(NOT AAX_INCLUDE_DIR)
|
||||
MESSAGE(FATAL_ERROR "Unable to find the AAX library development files.")
|
||||
SET(AAX_FOUND "NO")
|
||||
ENDIF(NOT AAX_INCLUDE_DIR)
|
||||
IF(NOT AAX_LIBRARY)
|
||||
IF(SINGLE_PACKAGE)
|
||||
SET(AAX_LIBRARY "${aax_BUILD_DIR}/aax/AAX32.dll")
|
||||
SET(AAX_FOUND "YES")
|
||||
ELSE(SINGLE_PACKAGE)
|
||||
ENDIF(SINGLE_PACKAGE)
|
||||
ENDIF(NOT AAX_LIBRARY)
|
||||
ENDIF(AAX_LIBRARY AND AAX_INCLUDE_DIR)
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
documentation. It has a .cxx extension so that emacs will happily
|
||||
autoindent correctly. */
|
||||
|
||||
/**
|
||||
* \namespace simgear
|
||||
* \brief \ref index "SimGear" main namespace.
|
||||
*/
|
||||
/** \mainpage SimGear
|
||||
* Simulation, Visualization, and Game development libraries.
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ find_dependency(Threads)
|
||||
|
||||
set(SIMGEAR_HEADLESS @SIMGEAR_HEADLESS@)
|
||||
set(SIMGEAR_SOUND @ENABLE_SOUND@)
|
||||
set(USE_AEONWAVE @USE_AEONWAVE@)
|
||||
|
||||
# OpenAL isn't a public dependency, so maybe not needed
|
||||
#if (SIMGEAR_SOUND)
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
Building a SimGear RPM package for Red Hat
|
||||
|
||||
Please see the "package/openSUSE" directory for an
|
||||
example how to build a SimGear RPM package with
|
||||
shared SimGear libraries.
|
||||
|
||||
You may need to adapt the names (exact spelling) of some
|
||||
of the package dependencies in the openSUSE RPM spec,
|
||||
since these may slightly differ for Red Hat.
|
||||
|
||||
(If you have a working and tested Red Hat RPM spec,
|
||||
you're welcome to contribute it to this project.)
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
Building a SimGear RPM package for openSUSE
|
||||
|
||||
(Last tested with openSUSE 11.4+12.1)
|
||||
|
||||
This directory contains the files which, along with
|
||||
the source code tar files, can be used to build
|
||||
an RPM package targeted at an openSUSE Linux system.
|
||||
|
||||
To build SimGear from source do the following:
|
||||
|
||||
1. obtain simgear-2.8.0.tar.bz2 (adapt version if
|
||||
necessary) and copy it into ~/rpmbuild/SOURCES
|
||||
|
||||
2. look in the BuildRequires section of SimGear.spec
|
||||
and check that all the packages referred to are
|
||||
installed (note, some of these packages may be part
|
||||
of openSUSE's "games" repository).
|
||||
|
||||
3. run 'rpmbuild -ba simgear.spec' and find the RPM
|
||||
build result in ~/rpmbuild/RPMS
|
||||
|
||||
That's all!
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
Summary: Simulator Construction Gear
|
||||
Name: SimGear
|
||||
Version: 2.8.0
|
||||
Release: 1
|
||||
License: LGPL
|
||||
URL: http://www.flightgear.org
|
||||
Group: Amusements/Games/3D/Simulation
|
||||
Source: http://mirrors.ibiblio.org/pub/mirrors/flightgear/ftp/Source/simgear-%{version}.tar.bz2
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-build
|
||||
|
||||
BuildRequires: gcc, gcc-c++, cmake
|
||||
BuildRequires: libopenal1-soft, openal-soft
|
||||
BuildRequires: libOpenSceneGraph-devel >= 3.0
|
||||
BuildRequires: zlib, zlib-devel
|
||||
BuildRequires: libjpeg62, libjpeg62-devel
|
||||
BuildRequires: boost-devel >= 1.37
|
||||
BuildRequires: subversion-devel, libapr1-devel
|
||||
Requires: OpenSceneGraph-plugins >= 3.0
|
||||
|
||||
%description
|
||||
This package contains a tools and libraries useful for constructing
|
||||
simulation and visualization applications such as FlightGear or TerraGear.
|
||||
|
||||
%package devel
|
||||
Group: Development/Libraries/Other
|
||||
Summary: Development header files for SimGear
|
||||
Requires: SimGear = %{version}
|
||||
|
||||
%description devel
|
||||
Development headers and libraries for building applications against SimGear.
|
||||
|
||||
%prep
|
||||
%setup -T -q -n simgear-%{version} -b 0
|
||||
|
||||
%build
|
||||
export CFLAGS="$RPM_OPT_FLAGS"
|
||||
export CXXFLAGS="$RPM_OPT_FLAGS"
|
||||
# build SHARED simgear libraries
|
||||
cmake -DCMAKE_INSTALL_PREFIX=%{_prefix} -DSIMGEAR_SHARED:BOOL=ON -DENABLE_TESTS:BOOL=OFF -DJPEG_FACTORY:BOOL=ON
|
||||
make %{?_smp_mflags}
|
||||
|
||||
%install
|
||||
make DESTDIR=%{buildroot} install
|
||||
|
||||
%post -p /sbin/ldconfig
|
||||
|
||||
%postun -p /sbin/ldconfig
|
||||
|
||||
%files
|
||||
%defattr (-, root, root, -)
|
||||
%doc AUTHORS COPYING ChangeLog NEWS README
|
||||
%{_libdir}/libSimGear*.so.*
|
||||
|
||||
%files devel
|
||||
%defattr(-,root,root,-)
|
||||
%dir %{_includedir}/simgear
|
||||
%{_includedir}/simgear/*
|
||||
%{_libdir}/libSimGear*.so
|
||||
|
||||
%changelog
|
||||
* Mon Jul 02 2012 thorstenb@flightgear.org
|
||||
- Initial version
|
||||
|
||||
@@ -15,6 +15,7 @@ foreach( mylibfolder
|
||||
nasal/cppbind
|
||||
props
|
||||
serial
|
||||
std
|
||||
structure
|
||||
threads
|
||||
timing
|
||||
|
||||
@@ -53,7 +53,7 @@ void testBasic()
|
||||
SG_CHECK_EQUAL(b1.gen_index(), 3040320);
|
||||
SG_CHECK_EQUAL(b1.gen_base_path(), "e000n50/e005n55");
|
||||
SG_VERIFY(b1.isValid());
|
||||
|
||||
|
||||
SGBucket b2(-10.1, -43.8);
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lon(), -11);
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lat(), -44);
|
||||
@@ -62,7 +62,7 @@ void testBasic()
|
||||
SG_CHECK_EQUAL(b2.get_y(), 1);
|
||||
SG_CHECK_EQUAL(b2.gen_base_path(), "w020s50/w011s44");
|
||||
SG_VERIFY(b2.isValid());
|
||||
|
||||
|
||||
SGBucket b3(123.48, 9.01);
|
||||
SG_CHECK_EQUAL(b3.get_chunk_lon(), 123);
|
||||
SG_CHECK_EQUAL(b3.get_chunk_lat(), 9);
|
||||
@@ -70,10 +70,10 @@ void testBasic()
|
||||
SG_CHECK_EQUAL(b3.get_y(), 0);
|
||||
SG_CHECK_EQUAL(b3.gen_base_path(), "e120n00/e123n09");
|
||||
SG_VERIFY(b3.isValid());
|
||||
|
||||
|
||||
SGBucket defBuck;
|
||||
SG_VERIFY(!defBuck.isValid());
|
||||
|
||||
|
||||
b3.make_bad();
|
||||
SG_VERIFY(!b3.isValid());
|
||||
|
||||
@@ -87,19 +87,47 @@ void testBasic()
|
||||
SG_CHECK_EQUAL(atAntiMeridian2.get_chunk_lon(), -180);
|
||||
SG_CHECK_EQUAL(atAntiMeridian2.get_x(), 0);
|
||||
|
||||
// check comparisom operator overload
|
||||
// check comparison operator overload
|
||||
SGBucket b4(5.11, 55.1);
|
||||
SG_VERIFY(b1 == b4); // should be equal
|
||||
SG_VERIFY(b1 == b1);
|
||||
SG_VERIFY(b1 != defBuck);
|
||||
SG_VERIFY(b1 != b2);
|
||||
|
||||
// check wrapping/clipping of inputs
|
||||
|
||||
// check wrapping/clipping of inputs
|
||||
SGBucket wrapMeridian(-200.0, 45.0);
|
||||
SG_CHECK_EQUAL(wrapMeridian.get_chunk_lon(), 160);
|
||||
|
||||
SGBucket clipPole(48.9, 91);
|
||||
SG_CHECK_EQUAL(clipPole.get_chunk_lat(), 89);
|
||||
|
||||
// test override of a bucket's geod
|
||||
auto geod = SGGeod::fromDegFt(-86.678, 36.1248, 599.0);
|
||||
#ifndef NO_DEPRECATED_API
|
||||
SGBucket bna_airport;
|
||||
bna_airport.set_bucket(geod);
|
||||
#else
|
||||
SGBucket bna_airport(geod);
|
||||
#endif
|
||||
SG_VERIFY(bna_airport.isValid());
|
||||
SG_CHECK_EQUAL(bna_airport.get_chunk_lon(), -87); // left origin of the 1-degree chunk
|
||||
SG_CHECK_EQUAL(bna_airport.get_chunk_lat(), 36); // bottom origin of the 1-degree chunk
|
||||
SG_CHECK_EQUAL(bna_airport.get_x(), 1); // buckets are 0.25 deg wide at the W87 parallel
|
||||
// we're 0.322 deg from the origin (second bucket)
|
||||
SG_CHECK_EQUAL(bna_airport.get_y(), 0); // buckets are always 0.125 deg tall
|
||||
// we're 0.1248 deg from the origin (first bucket)
|
||||
SG_CHECK_EQUAL(bna_airport.gen_base_path(), "w090n30/w087n36");
|
||||
SG_CHECK_EQUAL_EP2(bna_airport.get_width_m(), 22479.1, 0.1);
|
||||
SG_CHECK_EQUAL_EP2(bna_airport.get_height_m(), 13914.9, 0.1);
|
||||
SG_CHECK_EQUAL(bna_airport.gen_index_str(), "1531777"); // 0x175F81 = b01011101|01111110|000|001
|
||||
// = 93-180 | 126-90 | 0 | 1
|
||||
// = -87 | 36 | 0 | 1
|
||||
|
||||
// test stream output
|
||||
cout << "[TEST] BNA Airport: " << bna_airport << endl;
|
||||
auto center = bna_airport.get_center();
|
||||
cout << "[TEST] BNA lon: " << center.getLongitudeDeg() << endl;
|
||||
cout << "[TEST] BNA lat: " << center.getLatitudeDeg() << endl;
|
||||
}
|
||||
|
||||
void testPolar()
|
||||
@@ -110,17 +138,17 @@ void testPolar()
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lon(), 0);
|
||||
SG_CHECK_EQUAL(b1.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b1.get_y(), 7);
|
||||
|
||||
|
||||
SG_CHECK_EQUAL_EP(b1.get_highest_lat(), 90.0);
|
||||
SG_CHECK_EQUAL_EP(b1.get_width_m(), 10.0);
|
||||
|
||||
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lat(), 89);
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lon(), 0);
|
||||
SG_CHECK_EQUAL(b2.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b2.get_y(), 7);
|
||||
|
||||
|
||||
SG_CHECK_EQUAL(b1.gen_index(), b2.gen_index());
|
||||
|
||||
|
||||
SGGeod actualNorthPole1 = b1.get_corner(2);
|
||||
SGGeod actualNorthPole2 = b1.get_corner(3);
|
||||
SG_CHECK_EQUAL_EP(actualNorthPole1.getLatitudeDeg(), 90.0);
|
||||
@@ -131,11 +159,11 @@ void testPolar()
|
||||
SGBucket b3(-2, 89.88);
|
||||
SGBucket b4(-7, 89.88);
|
||||
SG_CHECK_EQUAL(b3.gen_index(), b4.gen_index());
|
||||
|
||||
|
||||
// south pole
|
||||
SGBucket b5(-170, -89.88);
|
||||
SGBucket b6(-179, -89.88);
|
||||
|
||||
|
||||
SG_CHECK_EQUAL(b5.get_chunk_lat(), -90);
|
||||
SG_CHECK_EQUAL(b5.get_chunk_lon(), -180);
|
||||
SG_CHECK_EQUAL(b5.get_x(), 0);
|
||||
@@ -143,17 +171,16 @@ void testPolar()
|
||||
SG_CHECK_EQUAL(b5.gen_index(), b6.gen_index());
|
||||
SG_CHECK_EQUAL_EP(b5.get_highest_lat(), -90.0);
|
||||
SG_CHECK_EQUAL_EP(b5.get_width_m(), 10.0);
|
||||
|
||||
|
||||
SGGeod actualSouthPole1 = b5.get_corner(0);
|
||||
SGGeod actualSouthPole2 = b5.get_corner(1);
|
||||
SG_CHECK_EQUAL_EP(actualSouthPole1.getLatitudeDeg(), -90.0);
|
||||
SG_CHECK_EQUAL_EP(actualSouthPole1.getLongitudeDeg(), -180);
|
||||
SG_CHECK_EQUAL_EP(actualSouthPole2.getLatitudeDeg(), -90.0);
|
||||
SG_CHECK_EQUAL_EP(actualSouthPole2.getLongitudeDeg(), -168);
|
||||
|
||||
|
||||
SGBucket b7(200, 89.88);
|
||||
SG_CHECK_EQUAL(b7.get_chunk_lon(), -168);
|
||||
|
||||
}
|
||||
|
||||
// test the tiles just below the pole (between 86 & 89 degrees N/S)
|
||||
@@ -167,7 +194,7 @@ void testNearPolar()
|
||||
|
||||
SGBucket b3(176.1, 88.5);
|
||||
SG_CHECK_EQUAL(b3.get_chunk_lon(), 176);
|
||||
|
||||
|
||||
SGBucket b4(-178, 88.5);
|
||||
SG_CHECK_EQUAL(b4.get_chunk_lon(), -180);
|
||||
}
|
||||
@@ -181,7 +208,7 @@ void testOffset()
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lon(), -60);
|
||||
SG_CHECK_EQUAL(b1.get_x(), 1);
|
||||
SG_CHECK_EQUAL(b1.get_y(), 7);
|
||||
|
||||
|
||||
// offset vertically
|
||||
SGBucket b2(b1.sibling(0, 1));
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lat(), 22);
|
||||
@@ -190,7 +217,7 @@ void testOffset()
|
||||
SG_CHECK_EQUAL(b2.get_y(), 0);
|
||||
|
||||
SG_CHECK_EQUAL(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.
|
||||
@@ -199,7 +226,7 @@ void testOffset()
|
||||
SG_CHECK_EQUAL(b3.get_chunk_lon(), -61);
|
||||
SG_CHECK_EQUAL(b3.get_x(), 1);
|
||||
SG_CHECK_EQUAL(b3.get_y(), 0);
|
||||
|
||||
|
||||
SG_CHECK_EQUAL(b3.gen_index(), sgBucketOffset(-59.8, 21.9, -3, 1));
|
||||
}
|
||||
|
||||
@@ -210,40 +237,40 @@ void testPolarOffset()
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lon(), -12);
|
||||
SG_CHECK_EQUAL(b1.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b1.get_y(), 3);
|
||||
|
||||
|
||||
// offset horizontally
|
||||
SGBucket b2(b1.sibling(-2, 0));
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lat(), -90);
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lon(), -36);
|
||||
SG_CHECK_EQUAL(b2.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b2.get_y(), 3);
|
||||
|
||||
|
||||
SG_CHECK_EQUAL(b2.gen_index(), sgBucketOffset(-11.7, -89.6, -2, 0));
|
||||
|
||||
// offset and wrap
|
||||
|
||||
// offset and wrap
|
||||
SGBucket b3(-170, 89.1);
|
||||
SGBucket b4(b3.sibling(-1, 0));
|
||||
SG_CHECK_EQUAL(b4.get_chunk_lat(), 89);
|
||||
SG_CHECK_EQUAL(b4.get_chunk_lon(), 168);
|
||||
SG_CHECK_EQUAL(b4.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b4.get_y(), 0);
|
||||
|
||||
|
||||
SG_CHECK_EQUAL(b4.gen_index(), sgBucketOffset(-170, 89.1, -1, 0));
|
||||
|
||||
|
||||
|
||||
SGBucket b5(177, 87.3);
|
||||
SGBucket b6(b5.sibling(1, 1));
|
||||
SG_CHECK_EQUAL(b6.get_chunk_lat(), 87);
|
||||
SG_CHECK_EQUAL(b6.get_chunk_lon(), -180);
|
||||
SG_CHECK_EQUAL(b6.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b6.get_y(), 3);
|
||||
|
||||
|
||||
SG_CHECK_EQUAL(b6.gen_index(), sgBucketOffset(177, 87.3, 1, 1));
|
||||
|
||||
// offset vertically towards the pole
|
||||
SGBucket b7(b1.sibling(0, -5));
|
||||
SG_VERIFY(!b7.isValid());
|
||||
|
||||
|
||||
SG_VERIFY(!SGBucket(0, 90).sibling(0, 1).isValid());
|
||||
}
|
||||
|
||||
@@ -256,29 +283,80 @@ void testOffsetWrap()
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lon(), -180);
|
||||
SG_CHECK_EQUAL(b1.get_x(), 1);
|
||||
SG_CHECK_EQUAL(b1.get_y(), 6);
|
||||
|
||||
|
||||
SGBucket b2(b1.sibling(-2, 0));
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lat(), 16);
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lon(), 179);
|
||||
SG_CHECK_EQUAL(b2.get_x(), 7);
|
||||
SG_CHECK_EQUAL(b2.get_y(), 6);
|
||||
SG_CHECK_EQUAL(b2.gen_index(), sgBucketOffset(-179.8, 16.8, -2, 0));
|
||||
|
||||
|
||||
}
|
||||
|
||||
void testSiblings()
|
||||
{
|
||||
SGBucket bna_airport(-86.678, 36.1248);
|
||||
SG_VERIFY(bna_airport.isValid());
|
||||
|
||||
// retrieve the sibling two positions north-east of my position
|
||||
auto sib1 = bna_airport.sibling(2, 2);
|
||||
SG_CHECK_EQUAL(sib1.get_chunk_lon(), bna_airport.get_chunk_lon());
|
||||
SG_CHECK_EQUAL(sib1.get_chunk_lat(), bna_airport.get_chunk_lat());
|
||||
SG_CHECK_EQUAL(sib1.get_x(), 3); // my x-pos (1) + 2 = 3
|
||||
SG_CHECK_EQUAL(sib1.get_y(), 2); // my y-pos (0) + 2 = 2
|
||||
SG_CHECK_EQUAL(sib1.gen_base_path(), bna_airport.gen_base_path());
|
||||
|
||||
// retrieve the one sibling two positions to the north-east
|
||||
std::vector<SGBucket> siblings;
|
||||
bna_airport.siblings(2, 2, siblings);
|
||||
SG_CHECK_EQUAL(siblings.size(), static_cast<std::vector<SGBucket>::size_type>(1));
|
||||
siblings.clear();
|
||||
|
||||
// retrieve the one sibling at the chunk origin of sib1
|
||||
sib1.siblings(-2, -2, siblings);
|
||||
SG_CHECK_EQUAL(siblings.size(), static_cast<std::vector<SGBucket>::size_type>(1));
|
||||
siblings.clear();
|
||||
|
||||
// calculate delta between two buckets
|
||||
int dx = 0;
|
||||
int dy = 0;
|
||||
sgBucketDiff(bna_airport, sib1, &dx, &dy);
|
||||
SG_CHECK_EQUAL(dx, 2);
|
||||
SG_CHECK_EQUAL(dy, 2);
|
||||
|
||||
// retrieve all siblings between two geodetic locations
|
||||
auto geod_bna = SGGeod::fromDegFt(-86.678, 36.1248, 599.0);
|
||||
auto geod_m54 = SGGeod::fromDegFt(-86.317, 36.1908, 122.0);
|
||||
sgGetBuckets(geod_bna, geod_m54, siblings);
|
||||
SG_CHECK_EQUAL(siblings.size(), static_cast<std::vector<SGBucket>::size_type>(4));
|
||||
siblings.clear();
|
||||
|
||||
// edge cases
|
||||
|
||||
// ensure you cannot retrieve the sibling of an invalid bucket
|
||||
SGBucket bad;
|
||||
auto bad_sib = bad.sibling(1, 1);
|
||||
SG_CHECK_EQUAL(bad_sib.get_chunk_lon(), -1000);
|
||||
SG_CHECK_EQUAL(bad.siblings(2, 2, siblings), 0);
|
||||
|
||||
// if we drop below the 22nd parallel, the bucket widths are half the size
|
||||
// expect this to retrieve two buckets
|
||||
bna_airport.siblings(0, -160, siblings);
|
||||
SG_CHECK_EQUAL(siblings.size(), static_cast<std::vector<SGBucket>::size_type>(2));
|
||||
siblings.clear();
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
testBucketSpans();
|
||||
|
||||
|
||||
testBasic();
|
||||
testPolar();
|
||||
testNearPolar();
|
||||
testOffset();
|
||||
testOffsetWrap();
|
||||
testPolarOffset();
|
||||
|
||||
testSiblings();
|
||||
|
||||
cout << "all tests passed OK" << endl;
|
||||
return 0; // passed
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,10 @@
|
||||
#include "CanvasEventManager.hxx"
|
||||
#include "CanvasEventVisitor.hxx"
|
||||
#include "CanvasPlacement.hxx"
|
||||
|
||||
#include <simgear/canvas/events/KeyboardEvent.hxx>
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/scene/util/parse_color.hxx>
|
||||
#include <simgear/scene/util/RenderConstants.hxx>
|
||||
|
||||
@@ -32,9 +34,6 @@
|
||||
#include <osgText/Text>
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
@@ -64,18 +63,9 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
Canvas::Canvas(SGPropertyNode* node):
|
||||
PropertyBasedElement(node),
|
||||
_canvas_mgr(0),
|
||||
_event_manager(new EventManager),
|
||||
_size_x(-1),
|
||||
_size_y(-1),
|
||||
_view_width(-1),
|
||||
_view_height(-1),
|
||||
_status(node, "status"),
|
||||
_status_msg(node, "status-msg"),
|
||||
_sampling_dirty(false),
|
||||
_render_dirty(true),
|
||||
_visible(true),
|
||||
_render_always(false)
|
||||
_status_msg(node, "status-msg")
|
||||
{
|
||||
_status = 0;
|
||||
setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
|
||||
@@ -236,6 +226,10 @@ namespace canvas
|
||||
|
||||
if( _status & STATUS_DIRTY )
|
||||
{
|
||||
// Retrieve reference here, to ensure the scene group is not deleted while
|
||||
// creating the new texture and camera
|
||||
osg::ref_ptr<osg::Group> root_scene_group = _root_group->getSceneGroup();
|
||||
|
||||
_texture.setSize(_size_x, _size_y);
|
||||
|
||||
if( !_texture.serviceable() )
|
||||
@@ -261,7 +255,7 @@ namespace canvas
|
||||
parseColor(_node->getStringValue("background"), clear_color);
|
||||
camera->setClearColor(clear_color);
|
||||
|
||||
camera->addChild(_root_group->getMatrixTransform());
|
||||
camera->addChild(root_scene_group);
|
||||
|
||||
if( _texture.serviceable() )
|
||||
{
|
||||
@@ -281,7 +275,7 @@ namespace canvas
|
||||
|
||||
if( _visible || _render_always )
|
||||
{
|
||||
BOOST_FOREACH(CanvasWeakPtr canvas_weak, _child_canvases)
|
||||
for(auto& canvas_weak: _child_canvases)
|
||||
{
|
||||
// TODO should we check if the image the child canvas is displayed
|
||||
// within is really visible?
|
||||
@@ -293,7 +287,7 @@ namespace canvas
|
||||
if( _render_dirty )
|
||||
{
|
||||
// Also mark all canvases this canvas is displayed within as dirty
|
||||
BOOST_FOREACH(CanvasWeakPtr canvas_weak, _parent_canvases)
|
||||
for(auto& canvas_weak: _parent_canvases)
|
||||
{
|
||||
CanvasPtr canvas = canvas_weak.lock();
|
||||
if( canvas )
|
||||
@@ -522,8 +516,8 @@ namespace canvas
|
||||
{
|
||||
const std::string& name = node->getNameString();
|
||||
|
||||
if( boost::starts_with(name, "status")
|
||||
|| boost::starts_with(name, "data-") )
|
||||
if( strutils::starts_with(name, "status")
|
||||
|| strutils::starts_with(name, "data-") )
|
||||
return;
|
||||
_render_dirty = true;
|
||||
|
||||
@@ -538,7 +532,7 @@ namespace canvas
|
||||
if( !placements.empty() )
|
||||
{
|
||||
bool placement_dirty = false;
|
||||
BOOST_FOREACH(PlacementPtr& placement, placements)
|
||||
for(auto& placement: placements)
|
||||
{
|
||||
// check if change can be directly handled by placement
|
||||
if( placement->getProps() == node->getParent()
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
#include <osg/NodeCallback>
|
||||
#include <osg/observer_ptr>
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace simgear
|
||||
@@ -211,21 +211,21 @@ namespace canvas
|
||||
|
||||
protected:
|
||||
|
||||
CanvasMgr *_canvas_mgr;
|
||||
CanvasMgr *_canvas_mgr {nullptr};
|
||||
|
||||
boost::scoped_ptr<EventManager> _event_manager;
|
||||
std::unique_ptr<EventManager> _event_manager;
|
||||
|
||||
int _size_x,
|
||||
_size_y,
|
||||
_view_width,
|
||||
_view_height;
|
||||
int _size_x {-1},
|
||||
_size_y {-1},
|
||||
_view_width {-1},
|
||||
_view_height {-1};
|
||||
|
||||
PropertyObject<int> _status;
|
||||
PropertyObject<std::string> _status_msg;
|
||||
|
||||
bool _sampling_dirty,
|
||||
_render_dirty,
|
||||
_visible;
|
||||
bool _sampling_dirty {false},
|
||||
_render_dirty {true},
|
||||
_visible {true};
|
||||
|
||||
ODGauge _texture;
|
||||
|
||||
@@ -235,7 +235,9 @@ namespace canvas
|
||||
ElementWeakPtr _focus_element;
|
||||
|
||||
CullCallbackPtr _cull_callback;
|
||||
bool _render_always; //!< Used to disable automatic lazy rendering (culling)
|
||||
|
||||
/** Used to disable automatic lazy rendering (culling) */
|
||||
bool _render_always {false};
|
||||
|
||||
std::vector<SGPropertyNode*> _dirty_placements;
|
||||
std::vector<Placements> _placements;
|
||||
|
||||
@@ -22,13 +22,11 @@
|
||||
#include "CanvasWindow.hxx"
|
||||
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/scene/util/OsgMath.hxx>
|
||||
|
||||
#include <osgGA/GUIEventHandler>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
@@ -43,9 +41,6 @@ namespace canvas
|
||||
const Style& parent_style,
|
||||
Element* parent ):
|
||||
Image(canvas, node, parent_style, parent),
|
||||
_attributes_dirty(0),
|
||||
_resizable(false),
|
||||
_capture_events(true),
|
||||
_resize_top(node, "resize-top"),
|
||||
_resize_right(node, "resize-right"),
|
||||
_resize_bottom(node, "resize-bottom"),
|
||||
@@ -92,7 +87,7 @@ namespace canvas
|
||||
_capture_events = node->getBoolValue();
|
||||
else if( name == "decoration-border" )
|
||||
parseDecorationBorder(node->getStringValue());
|
||||
else if( boost::starts_with(name, "shadow-")
|
||||
else if( strutils::starts_with(name, "shadow-")
|
||||
|| name == "content-size" )
|
||||
_attributes_dirty |= DECORATION;
|
||||
else
|
||||
@@ -103,16 +98,10 @@ namespace canvas
|
||||
Image::valueChanged(node);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::Group* Window::getGroup()
|
||||
{
|
||||
return getMatrixTransform();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const SGVec2<float> Window::getPosition() const
|
||||
{
|
||||
const osg::Matrix& m = getMatrixTransform()->getMatrix();
|
||||
auto const& m = getMatrix();
|
||||
return SGVec2<float>( m(3, 0), m(3, 1) );
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,6 @@ namespace canvas
|
||||
virtual void update(double delta_time_sec);
|
||||
virtual void valueChanged(SGPropertyNode* node);
|
||||
|
||||
osg::Group* getGroup();
|
||||
const SGVec2<float> getPosition() const;
|
||||
const SGRect<float> getScreenRegion() const;
|
||||
|
||||
@@ -104,7 +103,7 @@ namespace canvas
|
||||
DECORATION = 1
|
||||
};
|
||||
|
||||
uint32_t _attributes_dirty;
|
||||
uint32_t _attributes_dirty {0};
|
||||
|
||||
CanvasPtr _canvas_decoration;
|
||||
CanvasWeakPtr _canvas_content;
|
||||
@@ -113,8 +112,8 @@ namespace canvas
|
||||
ImagePtr _image_content,
|
||||
_image_shadow;
|
||||
|
||||
bool _resizable,
|
||||
_capture_events;
|
||||
bool _resizable {false},
|
||||
_capture_events {true};
|
||||
|
||||
PropertyObject<int> _resize_top,
|
||||
_resize_right,
|
||||
|
||||
@@ -9,10 +9,6 @@ set(HEADERS
|
||||
CanvasText.hxx
|
||||
)
|
||||
|
||||
set(DETAIL_HEADERS
|
||||
detail/add_segment_variadic.hxx
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
CanvasElement.cxx
|
||||
CanvasGroup.cxx
|
||||
@@ -23,7 +19,6 @@ set(SOURCES
|
||||
)
|
||||
|
||||
simgear_scene_component(canvas-elements canvas/elements "${SOURCES}" "${HEADERS}")
|
||||
simgear_component(canvas-elements/detail canvas/elements/detail "" "${DETAIL_HEADERS}")
|
||||
|
||||
add_boost_test(canvas_element
|
||||
SOURCES canvas_element_test.cpp
|
||||
|
||||
@@ -31,10 +31,6 @@
|
||||
#include <osg/StateAttribute>
|
||||
#include <osg/Version>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
@@ -214,22 +210,22 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::onDestroy()
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
if( !_scene_group.valid() )
|
||||
return;
|
||||
|
||||
// The transform node keeps a reference on this element, so ensure it is
|
||||
// deleted.
|
||||
BOOST_FOREACH(osg::Group* parent, _transform->getParents())
|
||||
for(osg::Group* parent: _scene_group->getParents())
|
||||
{
|
||||
parent->removeChild(_transform.get());
|
||||
parent->removeChild(_scene_group.get());
|
||||
}
|
||||
|
||||
// Hide in case someone still holds a reference
|
||||
setVisible(false);
|
||||
removeListener();
|
||||
|
||||
_parent = 0;
|
||||
_transform = 0;
|
||||
_parent = nullptr;
|
||||
_scene_group = nullptr;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -247,29 +243,8 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::update(double dt)
|
||||
{
|
||||
if( !isVisible() )
|
||||
return;
|
||||
|
||||
// Trigger matrix update
|
||||
getMatrix();
|
||||
|
||||
// Update bounding box on manual update (manual updates pass zero dt)
|
||||
if( dt == 0 && _drawable )
|
||||
_drawable->getBound();
|
||||
|
||||
if( (_attributes_dirty & BLEND_FUNC) && _transform.valid() )
|
||||
{
|
||||
parseBlendFunc(
|
||||
_transform->getOrCreateStateSet(),
|
||||
_node->getChild("blend-source"),
|
||||
_node->getChild("blend-destination"),
|
||||
_node->getChild("blend-source-rgb"),
|
||||
_node->getChild("blend-destination-rgb"),
|
||||
_node->getChild("blend-source-alpha"),
|
||||
_node->getChild("blend-destination-alpha")
|
||||
);
|
||||
_attributes_dirty &= ~BLEND_FUNC;
|
||||
}
|
||||
if( isVisible() )
|
||||
updateImpl(dt);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -341,7 +316,8 @@ namespace canvas
|
||||
if( listeners == _listener.end() )
|
||||
return false;
|
||||
|
||||
BOOST_FOREACH(EventListener const& listener, listeners->second)
|
||||
for(auto const& listener: listeners->second)
|
||||
{
|
||||
try
|
||||
{
|
||||
listener(event);
|
||||
@@ -354,6 +330,7 @@ namespace canvas
|
||||
"canvas::Element: event handler error: '" << ex.what() << "'"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -395,9 +372,9 @@ namespace canvas
|
||||
getBoundingBox()
|
||||
#endif
|
||||
.contains(osg::Vec3f(local_pos, 0));
|
||||
else if( _transform.valid() )
|
||||
else if( _scene_group.valid() )
|
||||
// ... for other elements, i.e. groups only a bounding sphere is available
|
||||
return _transform->getBound().contains(osg::Vec3f(parent_pos, 0));
|
||||
return _scene_group->getBound().contains(osg::Vec3f(parent_pos, 0));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
@@ -406,34 +383,32 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setVisible(bool visible)
|
||||
{
|
||||
if( _transform.valid() )
|
||||
if( _scene_group.valid() )
|
||||
// TODO check if we need another nodemask
|
||||
_transform->setNodeMask(visible ? 0xffffffff : 0);
|
||||
_scene_group->setNodeMask(visible ? 0xffffffff : 0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Element::isVisible() const
|
||||
{
|
||||
return _transform.valid() && _transform->getNodeMask() != 0;
|
||||
return _scene_group.valid() && _scene_group->getNodeMask() != 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::MatrixTransform* Element::getMatrixTransform()
|
||||
osg::MatrixTransform* Element::getSceneGroup() const
|
||||
{
|
||||
return _transform.get();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::MatrixTransform const* Element::getMatrixTransform() const
|
||||
{
|
||||
return _transform.get();
|
||||
return _scene_group.get();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::Vec2f Element::posToLocal(const osg::Vec2f& pos) const
|
||||
{
|
||||
getMatrix();
|
||||
const osg::Matrix& m = _transform->getInverseMatrix();
|
||||
if( !_scene_group )
|
||||
// TODO log warning?
|
||||
return pos;
|
||||
|
||||
updateMatrix();
|
||||
const osg::Matrix& m = _scene_group->getInverseMatrix();
|
||||
return osg::Vec2f
|
||||
(
|
||||
m(0, 0) * pos[0] + m(1, 0) * pos[1] + m(3, 0),
|
||||
@@ -486,9 +461,6 @@ namespace canvas
|
||||
{
|
||||
if( child->getNameString() == NAME_TRANSFORM )
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
return;
|
||||
|
||||
if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
|
||||
{
|
||||
SG_LOG
|
||||
@@ -525,7 +497,7 @@ namespace canvas
|
||||
if( parent == _node )
|
||||
{
|
||||
const std::string& name = child->getNameString();
|
||||
if( boost::starts_with(name, "data-") )
|
||||
if( strutils::starts_with(name, "data-") )
|
||||
return;
|
||||
else if( StyleInfo const* style_info = getStyleInfo(name) )
|
||||
{
|
||||
@@ -540,7 +512,7 @@ namespace canvas
|
||||
}
|
||||
else if( name == "update" )
|
||||
return update(0);
|
||||
else if( boost::starts_with(name, "blend-") )
|
||||
else if( strutils::starts_with(name, "blend-") )
|
||||
return (void)(_attributes_dirty |= BLEND_FUNC);
|
||||
}
|
||||
else if( parent
|
||||
@@ -564,6 +536,10 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setClip(const std::string& clip)
|
||||
{
|
||||
if( !_scene_group )
|
||||
// TODO warn?
|
||||
return;
|
||||
|
||||
osg::StateSet* ss = getOrCreateStateSet();
|
||||
if( !ss )
|
||||
return;
|
||||
@@ -577,8 +553,8 @@ namespace canvas
|
||||
|
||||
// TODO generalize CSS property parsing
|
||||
const std::string RECT("rect(");
|
||||
if( !boost::ends_with(clip, ")")
|
||||
|| !boost::starts_with(clip, RECT) )
|
||||
if( !strutils::ends_with(clip, ")")
|
||||
|| !strutils::starts_with(clip, RECT) )
|
||||
{
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "Canvas: invalid clip: " << clip);
|
||||
return;
|
||||
@@ -618,7 +594,7 @@ namespace canvas
|
||||
}
|
||||
|
||||
if( !_scissor )
|
||||
_scissor = new RelativeScissor(_transform.get());
|
||||
_scissor = new RelativeScissor(_scene_group.get());
|
||||
|
||||
// <top>, <right>, <bottom>, <left>
|
||||
_scissor->x() = values[3];
|
||||
@@ -642,26 +618,26 @@ namespace canvas
|
||||
_scissor->_coord_reference = rf;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setRotation(unsigned int index, double r)
|
||||
{
|
||||
_node->getChild(NAME_TRANSFORM, index, true)->setDoubleValue("rot", r);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setRotation(unsigned int index, double r)
|
||||
{
|
||||
_node->getChild(NAME_TRANSFORM, index, true)->setDoubleValue("rot", r);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setTranslation(unsigned int index, double x, double y)
|
||||
{
|
||||
SGPropertyNode* tf = _node->getChild(NAME_TRANSFORM, index, true);
|
||||
tf->getChild("t", 0, true)->setDoubleValue(x);
|
||||
tf->getChild("t", 1, true)->setDoubleValue(y);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setTranslation(unsigned int index, double x, double y)
|
||||
{
|
||||
SGPropertyNode* tf = _node->getChild(NAME_TRANSFORM, index, true);
|
||||
tf->getChild("t", 0, true)->setDoubleValue(x);
|
||||
tf->getChild("t", 1, true)->setDoubleValue(y);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setTransformEnabled(unsigned int index, bool enabled)
|
||||
{
|
||||
SGPropertyNode* tf = _node->getChild(NAME_TRANSFORM, index, true);
|
||||
tf->setBoolValue("enabled", enabled);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setTransformEnabled(unsigned int index, bool enabled)
|
||||
{
|
||||
SGPropertyNode* tf = _node->getChild(NAME_TRANSFORM, index, true);
|
||||
tf->setBoolValue("enabled", enabled);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::BoundingBox Element::getBoundingBox() const
|
||||
@@ -675,8 +651,8 @@ namespace canvas
|
||||
|
||||
osg::BoundingBox bb;
|
||||
|
||||
if( _transform.valid() )
|
||||
bb.expandBy(_transform->getBound());
|
||||
if( _scene_group.valid() )
|
||||
bb.expandBy( _scene_group->getBound() );
|
||||
|
||||
return bb;
|
||||
}
|
||||
@@ -710,73 +686,11 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
osg::Matrix Element::getMatrix() const
|
||||
{
|
||||
if( !_transform )
|
||||
if( !_scene_group )
|
||||
return osg::Matrix::identity();
|
||||
|
||||
if( !(_attributes_dirty & TRANSFORM) )
|
||||
return _transform->getMatrix();
|
||||
|
||||
osg::Matrix m;
|
||||
for( size_t i = 0; i < _transform_types.size(); ++i )
|
||||
{
|
||||
// Skip unused indizes...
|
||||
if( _transform_types[i] == TT_NONE )
|
||||
continue;
|
||||
|
||||
SGPropertyNode* tf_node = _node->getChild("tf", i, true);
|
||||
if (!tf_node->getBoolValue("enabled", true)) {
|
||||
continue; // skip disabled transforms
|
||||
}
|
||||
|
||||
// Build up the matrix representation of the current transform node
|
||||
osg::Matrix tf;
|
||||
switch( _transform_types[i] )
|
||||
{
|
||||
case TT_MATRIX:
|
||||
tf = osg::Matrix( tf_node->getDoubleValue("m[0]", 1),
|
||||
tf_node->getDoubleValue("m[1]", 0),
|
||||
0,
|
||||
tf_node->getDoubleValue("m[6]", 0),
|
||||
|
||||
tf_node->getDoubleValue("m[2]", 0),
|
||||
tf_node->getDoubleValue("m[3]", 1),
|
||||
0,
|
||||
tf_node->getDoubleValue("m[7]", 0),
|
||||
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
|
||||
tf_node->getDoubleValue("m[4]", 0),
|
||||
tf_node->getDoubleValue("m[5]", 0),
|
||||
0,
|
||||
tf_node->getDoubleValue("m[8]", 1) );
|
||||
break;
|
||||
case TT_TRANSLATE:
|
||||
tf.makeTranslate( osg::Vec3f( tf_node->getDoubleValue("t[0]", 0),
|
||||
tf_node->getDoubleValue("t[1]", 0),
|
||||
0 ) );
|
||||
break;
|
||||
case TT_ROTATE:
|
||||
tf.makeRotate( tf_node->getDoubleValue("rot", 0), 0, 0, 1 );
|
||||
break;
|
||||
case TT_SCALE:
|
||||
{
|
||||
float sx = tf_node->getDoubleValue("s[0]", 1);
|
||||
// sy defaults to sx...
|
||||
tf.makeScale( sx, tf_node->getDoubleValue("s[1]", sx), 1 );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
m.postMult( tf );
|
||||
}
|
||||
_transform->setMatrix(m);
|
||||
_attributes_dirty &= ~TRANSFORM;
|
||||
|
||||
return m;
|
||||
updateMatrix();
|
||||
return _scene_group->getMatrix();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -790,11 +704,8 @@ namespace canvas
|
||||
PropertyBasedElement(node),
|
||||
_canvas( canvas ),
|
||||
_parent( parent ),
|
||||
_attributes_dirty( 0 ),
|
||||
_transform( new osg::MatrixTransform ),
|
||||
_style( parent_style ),
|
||||
_scissor( 0 ),
|
||||
_drawable( 0 )
|
||||
_scene_group( new osg::MatrixTransform ),
|
||||
_style( parent_style )
|
||||
{
|
||||
staticInit();
|
||||
|
||||
@@ -806,15 +717,15 @@ namespace canvas
|
||||
);
|
||||
|
||||
// Ensure elements are drawn in order they appear in the element tree
|
||||
_transform->getOrCreateStateSet()
|
||||
->setRenderBinDetails
|
||||
(
|
||||
0,
|
||||
"PreOrderBin",
|
||||
osg::StateSet::OVERRIDE_RENDERBIN_DETAILS
|
||||
);
|
||||
_scene_group
|
||||
->getOrCreateStateSet()
|
||||
->setRenderBinDetails(
|
||||
0,
|
||||
"PreOrderBin",
|
||||
osg::StateSet::OVERRIDE_RENDERBIN_DETAILS
|
||||
);
|
||||
|
||||
_transform->setUserData( new OSGUserData(this) );
|
||||
_scene_group->setUserData( new OSGUserData(this) );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -902,11 +813,21 @@ namespace canvas
|
||||
void Element::setDrawable( osg::Drawable* drawable )
|
||||
{
|
||||
_drawable = drawable;
|
||||
assert( _drawable );
|
||||
|
||||
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
|
||||
if( !_drawable )
|
||||
{
|
||||
SG_LOG(SG_GL, SG_WARN, "canvas::Element::setDrawable: NULL drawable");
|
||||
return;
|
||||
}
|
||||
if( !_scene_group )
|
||||
{
|
||||
SG_LOG(SG_GL, SG_WARN, "canvas::Element::setDrawable: NULL scenegroup");
|
||||
return;
|
||||
}
|
||||
|
||||
auto geode = new osg::Geode;
|
||||
geode->addDrawable(_drawable);
|
||||
_transform->addChild(geode);
|
||||
_scene_group->addChild(geode);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -914,18 +835,109 @@ namespace canvas
|
||||
{
|
||||
if( _drawable.valid() )
|
||||
return _drawable->getOrCreateStateSet();
|
||||
if( _transform.valid() )
|
||||
return _transform->getOrCreateStateSet();
|
||||
|
||||
return 0;
|
||||
else if( _scene_group.valid() )
|
||||
return _scene_group->getOrCreateStateSet();
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setupStyle()
|
||||
{
|
||||
BOOST_FOREACH( Style::value_type style, _style )
|
||||
for(auto const& style: _style)
|
||||
setStyle(style.second);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::updateMatrix() const
|
||||
{
|
||||
if( !(_attributes_dirty & TRANSFORM) || !_scene_group )
|
||||
return;
|
||||
|
||||
osg::Matrix m;
|
||||
for( size_t i = 0; i < _transform_types.size(); ++i )
|
||||
{
|
||||
// Skip unused indizes...
|
||||
if( _transform_types[i] == TT_NONE )
|
||||
continue;
|
||||
|
||||
SGPropertyNode* tf_node = _node->getChild("tf", i, true);
|
||||
if (!tf_node->getBoolValue("enabled", true)) {
|
||||
continue; // skip disabled transforms
|
||||
}
|
||||
|
||||
// Build up the matrix representation of the current transform node
|
||||
osg::Matrix tf;
|
||||
switch( _transform_types[i] )
|
||||
{
|
||||
case TT_MATRIX:
|
||||
tf = osg::Matrix( tf_node->getDoubleValue("m[0]", 1),
|
||||
tf_node->getDoubleValue("m[1]", 0),
|
||||
0,
|
||||
tf_node->getDoubleValue("m[6]", 0),
|
||||
|
||||
tf_node->getDoubleValue("m[2]", 0),
|
||||
tf_node->getDoubleValue("m[3]", 1),
|
||||
0,
|
||||
tf_node->getDoubleValue("m[7]", 0),
|
||||
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
|
||||
tf_node->getDoubleValue("m[4]", 0),
|
||||
tf_node->getDoubleValue("m[5]", 0),
|
||||
0,
|
||||
tf_node->getDoubleValue("m[8]", 1) );
|
||||
break;
|
||||
case TT_TRANSLATE:
|
||||
tf.makeTranslate( osg::Vec3f( tf_node->getDoubleValue("t[0]", 0),
|
||||
tf_node->getDoubleValue("t[1]", 0),
|
||||
0 ) );
|
||||
break;
|
||||
case TT_ROTATE:
|
||||
tf.makeRotate( tf_node->getDoubleValue("rot", 0), 0, 0, 1 );
|
||||
break;
|
||||
case TT_SCALE:
|
||||
{
|
||||
float sx = tf_node->getDoubleValue("s[0]", 1);
|
||||
// sy defaults to sx...
|
||||
tf.makeScale( sx, tf_node->getDoubleValue("s[1]", sx), 1 );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
m.postMult( tf );
|
||||
}
|
||||
_scene_group->setMatrix(m);
|
||||
_attributes_dirty &= ~TRANSFORM;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::updateImpl(double dt)
|
||||
{
|
||||
updateMatrix();
|
||||
|
||||
// Update bounding box on manual update (manual updates pass zero dt)
|
||||
if( dt == 0 && _drawable )
|
||||
_drawable->getBound();
|
||||
|
||||
if( (_attributes_dirty & BLEND_FUNC) )
|
||||
{
|
||||
parseBlendFunc(
|
||||
_scene_group->getOrCreateStateSet(),
|
||||
_node->getChild("blend-source"),
|
||||
_node->getChild("blend-destination"),
|
||||
_node->getChild("blend-source-rgb"),
|
||||
_node->getChild("blend-destination-rgb"),
|
||||
_node->getChild("blend-source-alpha"),
|
||||
_node->getChild("blend-destination-alpha")
|
||||
);
|
||||
_attributes_dirty &= ~BLEND_FUNC;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
#include <simgear/canvas/CanvasEvent.hxx>
|
||||
#include <simgear/props/PropertyBasedElement.hxx>
|
||||
#include <simgear/misc/stdint.hxx> // for uint32_t
|
||||
#include <simgear/std/type_traits.hxx>
|
||||
|
||||
#include <osg/BoundingBox>
|
||||
#include <osg/MatrixTransform>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/type_traits/is_base_of.hpp>
|
||||
|
||||
namespace osg
|
||||
{
|
||||
@@ -49,6 +49,7 @@ namespace canvas
|
||||
public PropertyBasedElement
|
||||
{
|
||||
public:
|
||||
using SceneGroupWeakPtr = osg::observer_ptr<osg::MatrixTransform>;
|
||||
|
||||
/**
|
||||
* Store pointer to window as user data
|
||||
@@ -142,8 +143,11 @@ namespace canvas
|
||||
*/
|
||||
virtual bool isVisible() const;
|
||||
|
||||
osg::MatrixTransform* getMatrixTransform();
|
||||
osg::MatrixTransform const* getMatrixTransform() const;
|
||||
/**
|
||||
* Get the according group in the OSG scene graph
|
||||
*/
|
||||
// TODO ref_ptr
|
||||
osg::MatrixTransform* getSceneGroup() const;
|
||||
|
||||
/**
|
||||
* Transform position to local coordinages.
|
||||
@@ -217,13 +221,14 @@ namespace canvas
|
||||
*/
|
||||
template<typename Derived>
|
||||
static
|
||||
typename boost::enable_if<
|
||||
boost::is_base_of<Element, Derived>,
|
||||
std::enable_if_t<
|
||||
std::is_base_of<Element, Derived>::value,
|
||||
ElementPtr
|
||||
>::type create( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& style = Style(),
|
||||
Element* parent = NULL )
|
||||
>
|
||||
create( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& style = Style(),
|
||||
Element* parent = NULL )
|
||||
{
|
||||
return ElementPtr( new Derived(canvas, node, style, parent) );
|
||||
}
|
||||
@@ -251,13 +256,13 @@ namespace canvas
|
||||
CanvasWeakPtr _canvas;
|
||||
ElementWeakPtr _parent;
|
||||
|
||||
mutable uint32_t _attributes_dirty;
|
||||
mutable uint32_t _attributes_dirty = 0;
|
||||
|
||||
osg::observer_ptr<osg::MatrixTransform> _transform;
|
||||
std::vector<TransformType> _transform_types;
|
||||
SceneGroupWeakPtr _scene_group;
|
||||
std::vector<TransformType> _transform_types;
|
||||
|
||||
Style _style;
|
||||
RelativeScissor *_scissor;
|
||||
RelativeScissor *_scissor = nullptr;
|
||||
|
||||
typedef std::vector<EventListener> Listener;
|
||||
typedef std::map<int, Listener> ListenerMap;
|
||||
@@ -584,6 +589,10 @@ namespace canvas
|
||||
|
||||
void setupStyle();
|
||||
|
||||
void updateMatrix() const;
|
||||
|
||||
virtual void updateImpl(double dt);
|
||||
|
||||
private:
|
||||
|
||||
osg::ref_ptr<osg::Drawable> _drawable;
|
||||
|
||||
@@ -23,13 +23,10 @@
|
||||
#include "CanvasMap.hxx"
|
||||
#include "CanvasPath.hxx"
|
||||
#include "CanvasText.hxx"
|
||||
|
||||
#include <simgear/canvas/CanvasEventVisitor.hxx>
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/lambda/core.hpp>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
@@ -48,7 +45,7 @@ namespace canvas
|
||||
ElementFactories Group::_child_factories;
|
||||
const std::string Group::TYPE_NAME = "group";
|
||||
|
||||
void warnTransformExpired(const char* member_name)
|
||||
void warnSceneGroupExpired(const char* member_name)
|
||||
{
|
||||
SG_LOG( SG_GENERAL,
|
||||
SG_WARN,
|
||||
@@ -135,63 +132,57 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
ElementPtr Group::getElementById(const std::string& id)
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
if( !_scene_group.valid() )
|
||||
{
|
||||
warnTransformExpired("getElementById");
|
||||
return ElementPtr();
|
||||
warnSceneGroupExpired("getElementById");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<GroupPtr> groups;
|
||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||
// TODO check search algorithm. Not completely breadth-first and might be
|
||||
// possible with using less dynamic memory
|
||||
std::vector<GroupPtr> child_groups;
|
||||
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
|
||||
{
|
||||
const ElementPtr& el = getChildByIndex(i);
|
||||
if( el->get<std::string>("id") == id )
|
||||
return el;
|
||||
|
||||
Group* group = dynamic_cast<Group*>(el.get());
|
||||
if( group )
|
||||
groups.push_back(group);
|
||||
if( Group* child_group = dynamic_cast<Group*>(el.get()) )
|
||||
child_groups.push_back(child_group);
|
||||
}
|
||||
|
||||
BOOST_FOREACH( GroupPtr group, groups )
|
||||
for(auto group: child_groups)
|
||||
{
|
||||
ElementPtr el = group->getElementById(id);
|
||||
if( el )
|
||||
if( ElementPtr el = group->getElementById(id) )
|
||||
return el;
|
||||
}
|
||||
|
||||
return ElementPtr();
|
||||
return {};
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Group::clearEventListener()
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
return warnTransformExpired("clearEventListener");
|
||||
|
||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||
getChildByIndex(i)->clearEventListener();
|
||||
|
||||
Element::clearEventListener();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Group::update(double dt)
|
||||
{
|
||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||
getChildByIndex(i)->update(dt);
|
||||
if( !_scene_group.valid() )
|
||||
return warnSceneGroupExpired("clearEventListener");
|
||||
|
||||
Element::update(dt);
|
||||
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
|
||||
getChildByIndex(i)->clearEventListener();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Group::traverse(EventVisitor& visitor)
|
||||
{
|
||||
// Iterate in reverse order as last child is displayed on top
|
||||
for(size_t i = _transform->getNumChildren(); i --> 0;)
|
||||
if( _scene_group.valid() )
|
||||
{
|
||||
if( getChildByIndex(i)->accept(visitor) )
|
||||
return true;
|
||||
// Iterate in reverse order as last child is displayed on top
|
||||
for(size_t i = _scene_group->getNumChildren(); i --> 0;)
|
||||
{
|
||||
if( getChildByIndex(i)->accept(visitor) )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -206,13 +197,13 @@ namespace canvas
|
||||
bool handled = setStyleImpl(style, style_info);
|
||||
if( style_info->inheritable )
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
if( !_scene_group.valid() )
|
||||
{
|
||||
warnTransformExpired("setStyle");
|
||||
warnSceneGroupExpired("setStyle");
|
||||
return false;
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
|
||||
handled |= getChildByIndex(i)->setStyle(style, style_info);
|
||||
}
|
||||
|
||||
@@ -222,26 +213,20 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
osg::BoundingBox Group::getTransformedBounds(const osg::Matrix& m) const
|
||||
{
|
||||
osg::BoundingBox bb;
|
||||
if( !_transform.valid() )
|
||||
if( !_scene_group.valid() )
|
||||
{
|
||||
warnTransformExpired("getTransformedBounds");
|
||||
return bb;
|
||||
warnSceneGroupExpired("getTransformedBounds");
|
||||
return {};
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||
osg::BoundingBox bb;
|
||||
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
|
||||
{
|
||||
const ElementPtr& child = getChildByIndex(i);
|
||||
if( !child->getMatrixTransform()->getNodeMask() )
|
||||
auto child = getChildByIndex(i);
|
||||
if( !child || !child->isVisible() )
|
||||
continue;
|
||||
|
||||
bb.expandBy
|
||||
(
|
||||
child->getTransformedBounds
|
||||
(
|
||||
child->getMatrixTransform()->getMatrix() * m
|
||||
)
|
||||
);
|
||||
bb.expandBy( child->getTransformedBounds(child->getMatrix() * m) );
|
||||
}
|
||||
|
||||
return bb;
|
||||
@@ -257,6 +242,15 @@ namespace canvas
|
||||
return ElementFactory();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Group::updateImpl(double dt)
|
||||
{
|
||||
Element::updateImpl(dt);
|
||||
|
||||
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
|
||||
getChildByIndex(i)->update(dt);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Group::childAdded(SGPropertyNode* child)
|
||||
{
|
||||
@@ -266,13 +260,13 @@ namespace canvas
|
||||
ElementFactory child_factory = getChildFactory( child->getNameString() );
|
||||
if( child_factory )
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
return warnTransformExpired("childAdded");
|
||||
if( !_scene_group.valid() )
|
||||
return warnSceneGroupExpired("childAdded");
|
||||
|
||||
ElementPtr element = child_factory(_canvas, child, _style, this);
|
||||
|
||||
// Add to osg scene graph...
|
||||
_transform->addChild( element->getMatrixTransform() );
|
||||
_scene_group->addChild(element->getSceneGroup());
|
||||
|
||||
// ...and ensure correct ordering
|
||||
handleZIndexChanged(element);
|
||||
@@ -293,7 +287,7 @@ namespace canvas
|
||||
|
||||
if( getChildFactory(node->getNameString()) )
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
if( !_scene_group.valid() )
|
||||
// If transform is destroyed also all children are destroyed, so we can
|
||||
// not do anything here.
|
||||
return;
|
||||
@@ -323,7 +317,7 @@ namespace canvas
|
||||
void Group::childChanged(SGPropertyNode* node)
|
||||
{
|
||||
SGPropertyNode* parent = node->getParent();
|
||||
SGPropertyNode* grand_parent = parent ? parent->getParent() : NULL;
|
||||
SGPropertyNode* grand_parent = parent ? parent->getParent() : nullptr;
|
||||
|
||||
if( grand_parent == _node
|
||||
&& node->getNameString() == "z-index" )
|
||||
@@ -333,16 +327,18 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
void Group::handleZIndexChanged(ElementPtr child, int z_index)
|
||||
{
|
||||
if( !child || !_transform.valid() )
|
||||
if( !child || !_scene_group.valid() )
|
||||
return;
|
||||
|
||||
osg::ref_ptr<osg::MatrixTransform> tf = child->getMatrixTransform();
|
||||
size_t index = _transform->getChildIndex(tf),
|
||||
// Keep reference to prevent deleting while removing and re-inserting later
|
||||
osg::ref_ptr<osg::MatrixTransform> tf = child->getSceneGroup();
|
||||
|
||||
size_t index = _scene_group->getChildIndex(tf),
|
||||
index_new = index;
|
||||
|
||||
for(;; ++index_new)
|
||||
{
|
||||
if( index_new + 1 == _transform->getNumChildren() )
|
||||
if( index_new + 1 == _scene_group->getNumChildren() )
|
||||
break;
|
||||
|
||||
// Move to end of block with same index (= move upwards until the next
|
||||
@@ -369,8 +365,8 @@ namespace canvas
|
||||
return;
|
||||
}
|
||||
|
||||
_transform->removeChild(index);
|
||||
_transform->insertChild(index_new, tf);
|
||||
_scene_group->removeChild(index);
|
||||
_scene_group->insertChild(index_new, tf);
|
||||
|
||||
SG_LOG
|
||||
(
|
||||
@@ -383,24 +379,27 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
ElementPtr Group::getChildByIndex(size_t index) const
|
||||
{
|
||||
assert(_transform.valid());
|
||||
OSGUserData* ud =
|
||||
static_cast<OSGUserData*>(_transform->getChild(index)->getUserData());
|
||||
assert(ud);
|
||||
return ud->element;
|
||||
assert( _scene_group.valid() );
|
||||
|
||||
auto child = _scene_group->getChild(index);
|
||||
if( !child )
|
||||
return {};
|
||||
|
||||
auto ud = static_cast<OSGUserData*>(child->getUserData());
|
||||
return ud ? ud->element : ElementPtr();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ElementPtr Group::findChild( const SGPropertyNode* node,
|
||||
const std::string& id ) const
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
if( !_scene_group.valid() )
|
||||
{
|
||||
warnTransformExpired("findChild");
|
||||
return ElementPtr();
|
||||
warnSceneGroupExpired("findChild");
|
||||
return {};
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
|
||||
{
|
||||
ElementPtr el = getChildByIndex(i);
|
||||
|
||||
@@ -416,7 +415,7 @@ namespace canvas
|
||||
}
|
||||
}
|
||||
|
||||
return ElementPtr();
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
|
||||
@@ -88,8 +88,6 @@ namespace canvas
|
||||
|
||||
virtual void clearEventListener();
|
||||
|
||||
virtual void update(double dt);
|
||||
|
||||
virtual bool traverse(EventVisitor& visitor);
|
||||
|
||||
virtual bool setStyle( const SGPropertyNode* child,
|
||||
@@ -107,6 +105,8 @@ namespace canvas
|
||||
*/
|
||||
virtual ElementFactory getChildFactory(const std::string& type) const;
|
||||
|
||||
virtual void updateImpl(double dt);
|
||||
|
||||
virtual void childAdded(SGPropertyNode * child);
|
||||
virtual void childRemoved(SGPropertyNode * child);
|
||||
virtual void childChanged(SGPropertyNode * child);
|
||||
|
||||
@@ -117,9 +117,7 @@ namespace canvas
|
||||
ElementWeakPtr parent ):
|
||||
Element(canvas, node, parent_style, parent),
|
||||
_texture(new osg::Texture2D),
|
||||
_node_src_rect( node->getNode("source", 0, true) ),
|
||||
_src_rect(0,0),
|
||||
_region(0,0)
|
||||
_node_src_rect( node->getNode("source", 0, true) )
|
||||
{
|
||||
staticInit();
|
||||
|
||||
@@ -157,22 +155,207 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
Image::~Image()
|
||||
{
|
||||
if( _http_request ) {
|
||||
Canvas::getSystemAdapter()->getHTTPClient()->cancelRequest(_http_request, "image destroyed");
|
||||
}
|
||||
if( _http_request )
|
||||
{
|
||||
Canvas::getSystemAdapter()
|
||||
->getHTTPClient()
|
||||
->cancelRequest(_http_request, "image destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::update(double dt)
|
||||
void Image::valueChanged(SGPropertyNode* child)
|
||||
{
|
||||
Element::update(dt);
|
||||
// If the image is switched from invisible to visible, and it shows a
|
||||
// canvas, we need to delay showing it by one frame to ensure the canvas is
|
||||
// updated before the image is displayed.
|
||||
//
|
||||
// As canvas::Element handles and filters changes to the "visible" property
|
||||
// we can not check this in Image::childChanged but instead have to override
|
||||
// Element::valueChanged.
|
||||
if( !isVisible()
|
||||
&& child->getParent() == _node
|
||||
&& child->getNameString() == "visible"
|
||||
&& child->getBoolValue() )
|
||||
{
|
||||
CullCallback* cb =
|
||||
#if OSG_VERSION_LESS_THAN(3,3,2)
|
||||
static_cast<CullCallback*>
|
||||
#else
|
||||
dynamic_cast<CullCallback*>
|
||||
#endif
|
||||
( _geom->getCullCallback() );
|
||||
|
||||
if( cb )
|
||||
cb->cullNextFrame();
|
||||
}
|
||||
|
||||
Element::valueChanged(child);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setSrcCanvas(CanvasPtr canvas)
|
||||
{
|
||||
CanvasPtr src_canvas = _src_canvas.lock(),
|
||||
self_canvas = _canvas.lock();
|
||||
|
||||
if( src_canvas )
|
||||
src_canvas->removeParentCanvas(self_canvas);
|
||||
if( self_canvas )
|
||||
self_canvas->removeChildCanvas(src_canvas);
|
||||
|
||||
_src_canvas = src_canvas = canvas;
|
||||
_attributes_dirty |= SRC_CANVAS;
|
||||
_geom->setCullCallback(canvas ? new CullCallback(canvas) : 0);
|
||||
|
||||
if( src_canvas )
|
||||
{
|
||||
setupDefaultDimensions();
|
||||
|
||||
if( self_canvas )
|
||||
{
|
||||
self_canvas->addChildCanvas(src_canvas);
|
||||
src_canvas->addParentCanvas(self_canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
CanvasWeakPtr Image::getSrcCanvas() const
|
||||
{
|
||||
return _src_canvas;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setImage(osg::ref_ptr<osg::Image> img)
|
||||
{
|
||||
// remove canvas...
|
||||
setSrcCanvas( CanvasPtr() );
|
||||
|
||||
_texture->setResizeNonPowerOfTwoHint(false);
|
||||
_texture->setImage(img);
|
||||
_texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
|
||||
_texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
|
||||
_geom->getOrCreateStateSet()
|
||||
->setTextureAttributeAndModes(0, _texture);
|
||||
|
||||
if( img )
|
||||
setupDefaultDimensions();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setFill(const std::string& fill)
|
||||
{
|
||||
osg::Vec4 color(1,1,1,1);
|
||||
if( !fill.empty() // If no color is given default to white
|
||||
&& !parseColor(fill, color) )
|
||||
return;
|
||||
setFill(color);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setFill(const osg::Vec4& color)
|
||||
{
|
||||
_colors->front() = color;
|
||||
_colors->dirty();
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setOutset(const std::string& outset)
|
||||
{
|
||||
_outset = CSSBorder::parse(outset);
|
||||
_attributes_dirty |= DEST_SIZE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setPreserveAspectRatio(const std::string& scale)
|
||||
{
|
||||
_preserve_aspect_ratio = SVGpreserveAspectRatio::parse(scale);
|
||||
_attributes_dirty |= SRC_RECT;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setSourceRect(const SGRect<float>& sourceRect)
|
||||
{
|
||||
_attributes_dirty |= SRC_RECT;
|
||||
_src_rect = sourceRect;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setSlice(const std::string& slice)
|
||||
{
|
||||
_slice = CSSBorder::parse(slice);
|
||||
_attributes_dirty |= SRC_RECT | DEST_SIZE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setSliceWidth(const std::string& width)
|
||||
{
|
||||
_slice_width = CSSBorder::parse(width);
|
||||
_attributes_dirty |= DEST_SIZE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const SGRect<float>& Image::getRegion() const
|
||||
{
|
||||
return _region;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Image::handleEvent(const EventPtr& event)
|
||||
{
|
||||
bool handled = Element::handleEvent(event);
|
||||
|
||||
CanvasPtr src_canvas = _src_canvas.lock();
|
||||
if( !src_canvas )
|
||||
return handled;
|
||||
|
||||
if( MouseEventPtr mouse_event = dynamic_cast<MouseEvent*>(event.get()) )
|
||||
{
|
||||
mouse_event.reset( new MouseEvent(*mouse_event) );
|
||||
|
||||
mouse_event->client_pos = mouse_event->local_pos
|
||||
- toOsg(_region.getMin());
|
||||
|
||||
osg::Vec2f size(_region.width(), _region.height());
|
||||
if( _outset.isValid() )
|
||||
{
|
||||
CSSBorder::Offsets outset =
|
||||
_outset.getAbsOffsets(getTextureDimensions());
|
||||
|
||||
mouse_event->client_pos += osg::Vec2f(outset.l, outset.t);
|
||||
size.x() += outset.l + outset.r;
|
||||
size.y() += outset.t + outset.b;
|
||||
}
|
||||
|
||||
// Scale event pos according to canvas view size vs. displayed/screen size
|
||||
mouse_event->client_pos.x() *= src_canvas->getViewWidth() / size.x();
|
||||
mouse_event->client_pos.y() *= src_canvas->getViewHeight()/ size.y();
|
||||
mouse_event->local_pos = mouse_event->client_pos;
|
||||
|
||||
handled |= src_canvas->handleMouseEvent(mouse_event);
|
||||
}
|
||||
else if( KeyboardEventPtr keyboard_event =
|
||||
dynamic_cast<KeyboardEvent*>(event.get()) )
|
||||
{
|
||||
handled |= src_canvas->handleKeyboardEvent(keyboard_event);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::updateImpl(double dt)
|
||||
{
|
||||
Element::updateImpl(dt);
|
||||
|
||||
osg::Texture2D* texture = dynamic_cast<osg::Texture2D*>
|
||||
(
|
||||
_geom->getOrCreateStateSet()
|
||||
->getTextureAttribute(0, osg::StateAttribute::TEXTURE)
|
||||
);
|
||||
simgear::canvas::CanvasPtr canvas = _src_canvas.lock();
|
||||
auto canvas = _src_canvas.lock();
|
||||
|
||||
if( (_attributes_dirty & SRC_CANVAS)
|
||||
// check if texture has changed (eg. due to resizing)
|
||||
@@ -402,188 +585,6 @@ namespace canvas
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::valueChanged(SGPropertyNode* child)
|
||||
{
|
||||
// If the image is switched from invisible to visible, and it shows a
|
||||
// canvas, we need to delay showing it by one frame to ensure the canvas is
|
||||
// updated before the image is displayed.
|
||||
//
|
||||
// As canvas::Element handles and filters changes to the "visible" property
|
||||
// we can not check this in Image::childChanged but instead have to override
|
||||
// Element::valueChanged.
|
||||
if( !isVisible()
|
||||
&& child->getParent() == _node
|
||||
&& child->getNameString() == "visible"
|
||||
&& child->getBoolValue() )
|
||||
{
|
||||
CullCallback* cb =
|
||||
#if OSG_VERSION_LESS_THAN(3,3,2)
|
||||
static_cast<CullCallback*>
|
||||
#else
|
||||
dynamic_cast<CullCallback*>
|
||||
#endif
|
||||
( _geom->getCullCallback() );
|
||||
|
||||
if( cb )
|
||||
cb->cullNextFrame();
|
||||
}
|
||||
|
||||
Element::valueChanged(child);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setSrcCanvas(CanvasPtr canvas)
|
||||
{
|
||||
CanvasPtr src_canvas = _src_canvas.lock(),
|
||||
self_canvas = _canvas.lock();
|
||||
|
||||
if( src_canvas )
|
||||
src_canvas->removeParentCanvas(self_canvas);
|
||||
if( self_canvas )
|
||||
self_canvas->removeChildCanvas(src_canvas);
|
||||
|
||||
_src_canvas = src_canvas = canvas;
|
||||
_attributes_dirty |= SRC_CANVAS;
|
||||
_geom->setCullCallback(canvas ? new CullCallback(canvas) : 0);
|
||||
|
||||
if( src_canvas )
|
||||
{
|
||||
setupDefaultDimensions();
|
||||
|
||||
if( self_canvas )
|
||||
{
|
||||
self_canvas->addChildCanvas(src_canvas);
|
||||
src_canvas->addParentCanvas(self_canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
CanvasWeakPtr Image::getSrcCanvas() const
|
||||
{
|
||||
return _src_canvas;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setImage(osg::ref_ptr<osg::Image> img)
|
||||
{
|
||||
// remove canvas...
|
||||
setSrcCanvas( CanvasPtr() );
|
||||
|
||||
_texture->setResizeNonPowerOfTwoHint(false);
|
||||
_texture->setImage(img);
|
||||
_texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
|
||||
_texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
|
||||
_geom->getOrCreateStateSet()
|
||||
->setTextureAttributeAndModes(0, _texture);
|
||||
|
||||
if( img )
|
||||
setupDefaultDimensions();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setFill(const std::string& fill)
|
||||
{
|
||||
osg::Vec4 color(1,1,1,1);
|
||||
if( !fill.empty() // If no color is given default to white
|
||||
&& !parseColor(fill, color) )
|
||||
return;
|
||||
setFill(color);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setFill(const osg::Vec4& color)
|
||||
{
|
||||
_colors->front() = color;
|
||||
_colors->dirty();
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setOutset(const std::string& outset)
|
||||
{
|
||||
_outset = CSSBorder::parse(outset);
|
||||
_attributes_dirty |= DEST_SIZE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setPreserveAspectRatio(const std::string& scale)
|
||||
{
|
||||
_preserve_aspect_ratio = SVGpreserveAspectRatio::parse(scale);
|
||||
_attributes_dirty |= SRC_RECT;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setSourceRect(const SGRect<float>& sourceRect)
|
||||
{
|
||||
_attributes_dirty |= SRC_RECT;
|
||||
_src_rect = sourceRect;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setSlice(const std::string& slice)
|
||||
{
|
||||
_slice = CSSBorder::parse(slice);
|
||||
_attributes_dirty |= SRC_RECT | DEST_SIZE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setSliceWidth(const std::string& width)
|
||||
{
|
||||
_slice_width = CSSBorder::parse(width);
|
||||
_attributes_dirty |= DEST_SIZE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const SGRect<float>& Image::getRegion() const
|
||||
{
|
||||
return _region;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Image::handleEvent(const EventPtr& event)
|
||||
{
|
||||
bool handled = Element::handleEvent(event);
|
||||
|
||||
CanvasPtr src_canvas = _src_canvas.lock();
|
||||
if( !src_canvas )
|
||||
return handled;
|
||||
|
||||
if( MouseEventPtr mouse_event = dynamic_cast<MouseEvent*>(event.get()) )
|
||||
{
|
||||
mouse_event.reset( new MouseEvent(*mouse_event) );
|
||||
|
||||
mouse_event->client_pos = mouse_event->local_pos
|
||||
- toOsg(_region.getMin());
|
||||
|
||||
osg::Vec2f size(_region.width(), _region.height());
|
||||
if( _outset.isValid() )
|
||||
{
|
||||
CSSBorder::Offsets outset =
|
||||
_outset.getAbsOffsets(getTextureDimensions());
|
||||
|
||||
mouse_event->client_pos += osg::Vec2f(outset.l, outset.t);
|
||||
size.x() += outset.l + outset.r;
|
||||
size.y() += outset.t + outset.b;
|
||||
}
|
||||
|
||||
// Scale event pos according to canvas view size vs. displayed/screen size
|
||||
mouse_event->client_pos.x() *= src_canvas->getViewWidth() / size.x();
|
||||
mouse_event->client_pos.y() *= src_canvas->getViewHeight()/ size.y();
|
||||
mouse_event->local_pos = mouse_event->client_pos;
|
||||
|
||||
handled |= src_canvas->handleMouseEvent(mouse_event);
|
||||
}
|
||||
else if( KeyboardEventPtr keyboard_event =
|
||||
dynamic_cast<KeyboardEvent*>(event.get()) )
|
||||
{
|
||||
handled |= src_canvas->handleKeyboardEvent(keyboard_event);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::childChanged(SGPropertyNode* child)
|
||||
{
|
||||
@@ -634,7 +635,9 @@ namespace canvas
|
||||
// Abort pending request
|
||||
if( _http_request )
|
||||
{
|
||||
Canvas::getSystemAdapter()->getHTTPClient()->cancelRequest(_http_request, "setting new image");
|
||||
Canvas::getSystemAdapter()
|
||||
->getHTTPClient()
|
||||
->cancelRequest(_http_request, "setting new image");
|
||||
_http_request.reset();
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@ namespace canvas
|
||||
ElementWeakPtr parent = 0 );
|
||||
virtual ~Image();
|
||||
|
||||
virtual void update(double dt);
|
||||
virtual void valueChanged(SGPropertyNode* child);
|
||||
|
||||
void setSrcCanvas(CanvasPtr canvas);
|
||||
@@ -100,8 +99,8 @@ namespace canvas
|
||||
*
|
||||
*/
|
||||
void setSourceRect(const SGRect<float>& sourceRect);
|
||||
protected:
|
||||
|
||||
protected:
|
||||
enum ImageAttributes
|
||||
{
|
||||
SRC_RECT = LAST_ATTRIBUTE << 1, // Source image rectangle
|
||||
@@ -109,6 +108,8 @@ namespace canvas
|
||||
SRC_CANVAS = DEST_SIZE << 1
|
||||
};
|
||||
|
||||
virtual void updateImpl(double dt);
|
||||
|
||||
virtual void childChanged(SGPropertyNode * child);
|
||||
|
||||
void setupDefaultDimensions();
|
||||
@@ -134,9 +135,9 @@ namespace canvas
|
||||
osg::ref_ptr<osg::Vec2Array> _texCoords;
|
||||
osg::ref_ptr<osg::Vec4Array> _colors;
|
||||
|
||||
SGPropertyNode *_node_src_rect;
|
||||
SGRect<float> _src_rect,
|
||||
_region;
|
||||
SGPropertyNode *_node_src_rect = nullptr;
|
||||
SGRect<float> _src_rect {0, 0},
|
||||
_region {0, 0};
|
||||
|
||||
SVGpreserveAspectRatio _preserve_aspect_ratio;
|
||||
|
||||
|
||||
@@ -18,13 +18,14 @@
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "CanvasMap.hxx"
|
||||
#include "map/geo_node_pair.hxx"
|
||||
#include "map/projection.hxx"
|
||||
|
||||
#include <cmath>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <cmath>
|
||||
|
||||
#define LOG_GEO_RET(msg) \
|
||||
{\
|
||||
@@ -47,6 +48,12 @@ namespace canvas
|
||||
const std::string GEO = "-geo";
|
||||
const std::string HDG = "hdg";
|
||||
const std::string Map::TYPE_NAME = "map";
|
||||
const std::string WEB_MERCATOR = "webmercator";
|
||||
const std::string REF_LAT = "ref-lat";
|
||||
const std::string REF_LON = "ref-lon";
|
||||
const std::string SCREEN_RANGE = "screen-range";
|
||||
const std::string RANGE = "range";
|
||||
const std::string PROJECTION = "projection";
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::staticInit()
|
||||
@@ -64,12 +71,11 @@ namespace canvas
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
ElementWeakPtr parent ):
|
||||
Group(canvas, node, parent_style, parent),
|
||||
// TODO make projection configurable
|
||||
_projection(new SansonFlamsteedProjection),
|
||||
_projection_dirty(true)
|
||||
Group(canvas, node, parent_style, parent)
|
||||
{
|
||||
staticInit();
|
||||
|
||||
projectionNodeChanged(node->getChild(PROJECTION));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -79,13 +85,13 @@ namespace canvas
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::update(double dt)
|
||||
void Map::updateImpl(double dt)
|
||||
{
|
||||
for( GeoNodes::iterator it = _geo_nodes.begin();
|
||||
it != _geo_nodes.end();
|
||||
++it )
|
||||
Group::updateImpl(dt);
|
||||
|
||||
for(auto& it: _geo_nodes)
|
||||
{
|
||||
GeoNodePair* geo_node = it->second.get();
|
||||
GeoNodePair* geo_node = it.second.get();
|
||||
if( !geo_node->isComplete()
|
||||
|| (!geo_node->isDirty() && !_projection_dirty) )
|
||||
continue;
|
||||
@@ -107,14 +113,12 @@ namespace canvas
|
||||
geo_node->setDirty(false);
|
||||
}
|
||||
_projection_dirty = false;
|
||||
|
||||
Group::update(dt);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
|
||||
{
|
||||
if( boost::ends_with(child->getNameString(), GEO) )
|
||||
if( strutils::ends_with(child->getNameString(), GEO) )
|
||||
_geo_nodes[child].reset(new GeoNodePair());
|
||||
else if( parent != _node && child->getNameString() == HDG )
|
||||
_hdg_nodes.insert(child);
|
||||
@@ -125,7 +129,7 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
|
||||
{
|
||||
if( boost::ends_with(child->getNameString(), GEO) )
|
||||
if( strutils::ends_with(child->getNameString(), GEO) )
|
||||
// TODO remove from other node
|
||||
_geo_nodes.erase(child);
|
||||
else if( parent != _node && child->getName() == HDG )
|
||||
@@ -147,7 +151,7 @@ namespace canvas
|
||||
{
|
||||
const std::string& name = child->getNameString();
|
||||
|
||||
if( boost::ends_with(name, GEO) )
|
||||
if( strutils::ends_with(name, GEO) )
|
||||
return geoNodeChanged(child);
|
||||
else if( name == HDG )
|
||||
return hdgNodeChanged(child);
|
||||
@@ -162,10 +166,10 @@ namespace canvas
|
||||
if( child->getParent() != _node )
|
||||
return Group::childChanged(child);
|
||||
|
||||
if( child->getNameString() == "ref-lat"
|
||||
|| child->getNameString() == "ref-lon" )
|
||||
_projection->setWorldPosition( _node->getDoubleValue("ref-lat"),
|
||||
_node->getDoubleValue("ref-lon") );
|
||||
if( child->getNameString() == REF_LAT
|
||||
|| child->getNameString() == REF_LON )
|
||||
_projection->setWorldPosition( _node->getDoubleValue(REF_LAT),
|
||||
_node->getDoubleValue(REF_LON) );
|
||||
else if( child->getNameString() == HDG )
|
||||
{
|
||||
_projection->setOrientation(child->getFloatValue());
|
||||
@@ -174,16 +178,35 @@ namespace canvas
|
||||
++it )
|
||||
hdgNodeChanged(*it);
|
||||
}
|
||||
else if( child->getNameString() == "range" )
|
||||
else if( child->getNameString() == RANGE )
|
||||
_projection->setRange(child->getDoubleValue());
|
||||
else if( child->getNameString() == "screen-range" )
|
||||
else if( child->getNameString() == SCREEN_RANGE )
|
||||
_projection->setScreenRange(child->getDoubleValue());
|
||||
else if( child->getNameString() == PROJECTION )
|
||||
projectionNodeChanged(child);
|
||||
else
|
||||
return Group::childChanged(child);
|
||||
|
||||
_projection_dirty = true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::projectionNodeChanged(SGPropertyNode* child)
|
||||
{
|
||||
if(child && child->getStringValue() == WEB_MERCATOR)
|
||||
_projection = std::make_shared<WebMercatorProjection>();
|
||||
else
|
||||
_projection = std::make_shared<SansonFlamsteedProjection>();
|
||||
|
||||
_projection->setWorldPosition(_node->getDoubleValue(REF_LAT),
|
||||
_node->getDoubleValue(REF_LON) );
|
||||
_projection->setOrientation(_node->getFloatValue(HDG));
|
||||
_projection->setScreenRange(_node->getDoubleValue(SCREEN_RANGE));
|
||||
_projection->setRange(_node->getDoubleValue(RANGE));
|
||||
|
||||
_projection_dirty = true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::geoNodeChanged(SGPropertyNode* child)
|
||||
{
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
|
||||
#include "CanvasGroup.hxx"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/unordered_set.hpp>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
@@ -45,45 +45,41 @@ namespace canvas
|
||||
ElementWeakPtr parent = 0 );
|
||||
virtual ~Map();
|
||||
|
||||
virtual void update(double dt);
|
||||
|
||||
virtual void childAdded( SGPropertyNode * parent,
|
||||
SGPropertyNode * child );
|
||||
virtual void childRemoved( SGPropertyNode * parent,
|
||||
SGPropertyNode * child );
|
||||
virtual void valueChanged(SGPropertyNode * child);
|
||||
|
||||
protected:
|
||||
virtual void updateImpl(double dt);
|
||||
|
||||
virtual void childChanged(SGPropertyNode * child);
|
||||
void updateProjection(SGPropertyNode* type_node);
|
||||
|
||||
typedef boost::unordered_map< SGPropertyNode*,
|
||||
boost::shared_ptr<GeoNodePair>
|
||||
> GeoNodes;
|
||||
typedef boost::unordered_set<SGPropertyNode*> NodeSet;
|
||||
virtual void childAdded( SGPropertyNode* parent,
|
||||
SGPropertyNode* child );
|
||||
virtual void childRemoved( SGPropertyNode* parent,
|
||||
SGPropertyNode* child );
|
||||
virtual void valueChanged(SGPropertyNode* child);
|
||||
virtual void childChanged(SGPropertyNode* child);
|
||||
|
||||
using GeoNodes =
|
||||
std::unordered_map<SGPropertyNode*, std::shared_ptr<GeoNodePair>>;
|
||||
using NodeSet = std::unordered_set<SGPropertyNode*>;
|
||||
|
||||
GeoNodes _geo_nodes;
|
||||
NodeSet _hdg_nodes;
|
||||
boost::shared_ptr<HorizontalProjection> _projection;
|
||||
bool _projection_dirty;
|
||||
std::shared_ptr<HorizontalProjection> _projection;
|
||||
bool _projection_dirty = false;
|
||||
|
||||
struct GeoCoord
|
||||
{
|
||||
GeoCoord():
|
||||
type(INVALID),
|
||||
value(0)
|
||||
{}
|
||||
enum
|
||||
{
|
||||
INVALID,
|
||||
LATITUDE,
|
||||
LONGITUDE
|
||||
} type;
|
||||
double value;
|
||||
} type = INVALID;
|
||||
double value = 0;
|
||||
};
|
||||
|
||||
void geoNodeChanged(SGPropertyNode * child);
|
||||
void hdgNodeChanged(SGPropertyNode * child);
|
||||
void projectionNodeChanged(SGPropertyNode* child);
|
||||
void geoNodeChanged(SGPropertyNode* child);
|
||||
void hdgNodeChanged(SGPropertyNode* child);
|
||||
|
||||
GeoCoord parseGeoCoord(const std::string& val) const;
|
||||
};
|
||||
|
||||
@@ -206,23 +206,13 @@ namespace canvas
|
||||
return SGVec2f(-1.0f, -1.0f);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
class Path::PathDrawable:
|
||||
public osg::Drawable
|
||||
{
|
||||
public:
|
||||
PathDrawable(Path* path):
|
||||
_path_element(path),
|
||||
_path(VG_INVALID_HANDLE),
|
||||
_paint(VG_INVALID_HANDLE),
|
||||
_paint_fill(VG_INVALID_HANDLE),
|
||||
_attributes_dirty(~0),
|
||||
_mode(0),
|
||||
_fill_rule(VG_EVEN_ODD),
|
||||
_stroke_width(1),
|
||||
_stroke_linecap(VG_CAP_BUTT),
|
||||
_stroke_linejoin(VG_JOIN_MITER)
|
||||
_path_element(path)
|
||||
{
|
||||
setSupportsDisplayList(false);
|
||||
setDataVariance(Object::DYNAMIC);
|
||||
@@ -283,6 +273,16 @@ namespace canvas
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set path fill opacity (Only used if fill is not "none")
|
||||
*/
|
||||
void setFillOpacity(float opacity)
|
||||
{
|
||||
_fill_opacity =
|
||||
static_cast<uint8_t>(SGMiscf::clip(opacity, 0.f, 1.f) * 255);
|
||||
_attributes_dirty |= FILL_COLOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set path fill rule ("pseudo-nonzero" or "evenodd")
|
||||
*
|
||||
@@ -310,7 +310,7 @@ namespace canvas
|
||||
else if( parseColor(stroke, _stroke_color) )
|
||||
{
|
||||
_mode |= VG_STROKE_PATH;
|
||||
_attributes_dirty |= STROKE_COLOR;
|
||||
_attributes_dirty |= STROKE_COLOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -323,6 +323,16 @@ namespace canvas
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set path stroke opacity (only used if stroke is not "none")
|
||||
*/
|
||||
void setStrokeOpacity(float opacity)
|
||||
{
|
||||
_stroke_opacity =
|
||||
static_cast<uint8_t>(SGMiscf::clip(opacity, 0.f, 1.f) * 255);
|
||||
_attributes_dirty |= STROKE_COLOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set stroke width
|
||||
*/
|
||||
@@ -390,31 +400,22 @@ namespace canvas
|
||||
osg::StateAttribute const* blend_func =
|
||||
state->getLastAppliedAttribute(osg::StateAttribute::BLENDFUNC);
|
||||
|
||||
// Initialize/Update the paint
|
||||
if( _attributes_dirty & STROKE_COLOR )
|
||||
{
|
||||
if( _paint == VG_INVALID_HANDLE )
|
||||
_paint = vgCreatePaint();
|
||||
|
||||
vgSetParameterfv(_paint, VG_PAINT_COLOR, 4, _stroke_color._v);
|
||||
|
||||
_attributes_dirty &= ~STROKE_COLOR;
|
||||
}
|
||||
|
||||
// Initialize/update fill paint
|
||||
if( _attributes_dirty & FILL_COLOR )
|
||||
{
|
||||
if( _paint_fill == VG_INVALID_HANDLE )
|
||||
_paint_fill = vgCreatePaint();
|
||||
|
||||
vgSetParameterfv(_paint_fill, VG_PAINT_COLOR, 4, _fill_color._v);
|
||||
|
||||
_attributes_dirty &= ~FILL_COLOR;
|
||||
}
|
||||
|
||||
// Setup paint
|
||||
if( _mode & VG_STROKE_PATH )
|
||||
{
|
||||
// Initialize/Update the paint
|
||||
if( _attributes_dirty & STROKE_COLOR )
|
||||
{
|
||||
if( _paint == VG_INVALID_HANDLE )
|
||||
_paint = vgCreatePaint();
|
||||
|
||||
auto color = _stroke_color;
|
||||
color.a() *= _stroke_opacity / 255.f;
|
||||
vgSetParameterfv(_paint, VG_PAINT_COLOR, 4, color._v);
|
||||
|
||||
_attributes_dirty &= ~STROKE_COLOR;
|
||||
}
|
||||
|
||||
vgSetPaint(_paint, VG_STROKE_PATH);
|
||||
|
||||
vgSetf(VG_STROKE_LINE_WIDTH, _stroke_width);
|
||||
@@ -426,6 +427,19 @@ namespace canvas
|
||||
}
|
||||
if( _mode & VG_FILL_PATH )
|
||||
{
|
||||
// Initialize/update fill paint
|
||||
if( _attributes_dirty & FILL_COLOR )
|
||||
{
|
||||
if( _paint_fill == VG_INVALID_HANDLE )
|
||||
_paint_fill = vgCreatePaint();
|
||||
|
||||
auto color = _fill_color;
|
||||
color.a() *= _fill_opacity / 255.f;
|
||||
vgSetParameterfv(_paint_fill, VG_PAINT_COLOR, 4, color._v);
|
||||
|
||||
_attributes_dirty &= ~FILL_COLOR;
|
||||
}
|
||||
|
||||
vgSetPaint(_paint_fill, VG_FILL_PATH);
|
||||
|
||||
vgSeti(VG_FILL_RULE, _fill_rule);
|
||||
@@ -579,22 +593,24 @@ namespace canvas
|
||||
|
||||
Path *_path_element;
|
||||
|
||||
mutable VGPath _path;
|
||||
mutable VGPaint _paint;
|
||||
mutable VGPaint _paint_fill;
|
||||
mutable uint32_t _attributes_dirty;
|
||||
mutable VGPath _path {VG_INVALID_HANDLE};
|
||||
mutable VGPaint _paint {VG_INVALID_HANDLE};
|
||||
mutable VGPaint _paint_fill {VG_INVALID_HANDLE};
|
||||
mutable uint32_t _attributes_dirty {~0u};
|
||||
|
||||
CmdList _cmds;
|
||||
CoordList _coords;
|
||||
|
||||
VGbitfield _mode;
|
||||
VGbitfield _mode {0};
|
||||
osg::Vec4f _fill_color;
|
||||
VGFillRule _fill_rule;
|
||||
uint8_t _fill_opacity {255};
|
||||
VGFillRule _fill_rule {VG_EVEN_ODD};
|
||||
osg::Vec4f _stroke_color;
|
||||
VGfloat _stroke_width;
|
||||
uint8_t _stroke_opacity {255};
|
||||
VGfloat _stroke_width {1};
|
||||
std::vector<VGfloat> _stroke_dash;
|
||||
VGCapStyle _stroke_linecap;
|
||||
VGJoinStyle _stroke_linejoin;
|
||||
VGCapStyle _stroke_linecap {VG_CAP_BUTT};
|
||||
VGJoinStyle _stroke_linejoin {VG_JOIN_MITER};
|
||||
|
||||
osg::Vec3f transformPoint( const osg::Matrix& m,
|
||||
osg::Vec2f pos ) const
|
||||
@@ -670,8 +686,10 @@ namespace canvas
|
||||
PathDrawableRef Path::*path = &Path::_path;
|
||||
|
||||
addStyle("fill", "color", &PathDrawable::setFill, path);
|
||||
addStyle("fill-opacity", "numeric", &PathDrawable::setFillOpacity, path);
|
||||
addStyle("fill-rule", "", &PathDrawable::setFillRule, path);
|
||||
addStyle("stroke", "color", &PathDrawable::setStroke, path);
|
||||
addStyle("stroke-opacity", "numeric", &PathDrawable::setStrokeOpacity, path);
|
||||
addStyle("stroke-width", "numeric", &PathDrawable::setStrokeWidth, path);
|
||||
addStyle("stroke-dasharray", "", &PathDrawable::setStrokeDashArray, path);
|
||||
addStyle("stroke-linecap", "", &PathDrawable::setStrokeLinecap, path);
|
||||
@@ -700,92 +718,69 @@ namespace canvas
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::update(double dt)
|
||||
{
|
||||
if( _attributes_dirty & (CMDS | COORDS) )
|
||||
{
|
||||
_path->setSegments
|
||||
(
|
||||
_node->getChildValues<VGubyte, int>("cmd"),
|
||||
_node->getChildValues<VGfloat, float>("coord")
|
||||
);
|
||||
|
||||
_attributes_dirty &= ~(CMDS | COORDS);
|
||||
}
|
||||
|
||||
// SVG path overrides manual cmd/coord specification
|
||||
if ( _hasSVG && (_attributes_dirty & SVG))
|
||||
{
|
||||
CmdList cmds;
|
||||
CoordList coords;
|
||||
parseSVGPathToVGPath(_node->getStringValue("svg"), cmds, coords);
|
||||
_path->setSegments(cmds, coords);
|
||||
_attributes_dirty &= ~SVG;
|
||||
}
|
||||
|
||||
if ( _hasRect &&(_attributes_dirty & RECT))
|
||||
{
|
||||
parseRectToVGPath();
|
||||
_attributes_dirty &= ~RECT;
|
||||
|
||||
}
|
||||
|
||||
Element::update(dt);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::BoundingBox Path::getTransformedBounds(const osg::Matrix& m) const
|
||||
{
|
||||
return _path->getTransformedBounds(m);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::addSegment(uint8_t cmd, std::initializer_list<float> coords)
|
||||
{
|
||||
_node->addChild("cmd")->setIntValue(cmd);
|
||||
|
||||
for(float coord: coords)
|
||||
_node->addChild("coord")->setFloatValue(coord);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::moveTo(float x_abs, float y_abs)
|
||||
{
|
||||
return addSegment(VG_MOVE_TO_ABS, x_abs, y_abs);
|
||||
return addSegment(VG_MOVE_TO_ABS, {x_abs, y_abs});
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::move(float x_rel, float y_rel)
|
||||
{
|
||||
return addSegment(VG_MOVE_TO_REL, x_rel, y_rel);
|
||||
return addSegment(VG_MOVE_TO_REL, {x_rel, y_rel});
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::lineTo(float x_abs, float y_abs)
|
||||
{
|
||||
return addSegment(VG_LINE_TO_ABS, x_abs, y_abs);
|
||||
return addSegment(VG_LINE_TO_ABS, {x_abs, y_abs});
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::line(float x_rel, float y_rel)
|
||||
{
|
||||
return addSegment(VG_LINE_TO_REL, x_rel, y_rel);
|
||||
return addSegment(VG_LINE_TO_REL, {x_rel, y_rel});
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::horizTo(float x_abs)
|
||||
{
|
||||
return addSegment(VG_HLINE_TO_ABS, x_abs);
|
||||
return addSegment(VG_HLINE_TO_ABS, {x_abs});
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::horiz(float x_rel)
|
||||
{
|
||||
return addSegment(VG_HLINE_TO_REL, x_rel);
|
||||
return addSegment(VG_HLINE_TO_REL, {x_rel});
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::vertTo(float y_abs)
|
||||
{
|
||||
return addSegment(VG_VLINE_TO_ABS, y_abs);
|
||||
return addSegment(VG_VLINE_TO_ABS, {y_abs});
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::vert(float y_rel)
|
||||
{
|
||||
return addSegment(VG_VLINE_TO_REL, y_rel);
|
||||
return addSegment(VG_VLINE_TO_REL, {y_rel});
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -828,36 +823,70 @@ namespace canvas
|
||||
_node->getChild("border-radius", 1, true)->setDoubleValue(radiusY);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::updateImpl(double dt)
|
||||
{
|
||||
Element::updateImpl(dt);
|
||||
|
||||
if( _attributes_dirty & (CMDS | COORDS) )
|
||||
{
|
||||
_path->setSegments
|
||||
(
|
||||
_node->getChildValues<VGubyte, int>("cmd"),
|
||||
_node->getChildValues<VGfloat, float>("coord")
|
||||
);
|
||||
|
||||
_attributes_dirty &= ~(CMDS | COORDS);
|
||||
}
|
||||
|
||||
// SVG path overrides manual cmd/coord specification
|
||||
if( _hasSVG && (_attributes_dirty & SVG) )
|
||||
{
|
||||
CmdList cmds;
|
||||
CoordList coords;
|
||||
parseSVGPathToVGPath(_node->getStringValue("svg"), cmds, coords);
|
||||
_path->setSegments(cmds, coords);
|
||||
_attributes_dirty &= ~SVG;
|
||||
}
|
||||
|
||||
if( _hasRect &&(_attributes_dirty & RECT) )
|
||||
{
|
||||
parseRectToVGPath();
|
||||
_attributes_dirty &= ~RECT;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::childChanged(SGPropertyNode* child)
|
||||
{
|
||||
const std::string& name = child->getNameString();
|
||||
const std::string &prName = child->getParent()->getNameString();
|
||||
const std::string& name = child->getNameString();
|
||||
const std::string &prName = child->getParent()->getNameString();
|
||||
|
||||
if (simgear::strutils::starts_with(name, "border-"))
|
||||
{
|
||||
_attributes_dirty |= RECT;
|
||||
return;
|
||||
}
|
||||
if( strutils::starts_with(name, "border-") )
|
||||
{
|
||||
_attributes_dirty |= RECT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (prName == "rect") {
|
||||
_hasRect = true;
|
||||
if (name == "left") {
|
||||
_rect.setLeft(child->getDoubleValue());
|
||||
} else if (name == "top") {
|
||||
_rect.setTop(child->getDoubleValue());
|
||||
} else if (name == "right") {
|
||||
_rect.setRight(child->getDoubleValue());
|
||||
} else if (name == "bottom") {
|
||||
_rect.setBottom(child->getDoubleValue());
|
||||
} else if (name == "width") {
|
||||
_rect.setWidth(child->getDoubleValue());
|
||||
} else if (name == "height") {
|
||||
_rect.setHeight(child->getDoubleValue());
|
||||
}
|
||||
_attributes_dirty |= RECT;
|
||||
return;
|
||||
if (prName == "rect")
|
||||
{
|
||||
_hasRect = true;
|
||||
if (name == "left") {
|
||||
_rect.setLeft(child->getDoubleValue());
|
||||
} else if (name == "top") {
|
||||
_rect.setTop(child->getDoubleValue());
|
||||
} else if (name == "right") {
|
||||
_rect.setRight(child->getDoubleValue());
|
||||
} else if (name == "bottom") {
|
||||
_rect.setBottom(child->getDoubleValue());
|
||||
} else if (name == "width") {
|
||||
_rect.setWidth(child->getDoubleValue());
|
||||
} else if (name == "height") {
|
||||
_rect.setHeight(child->getDoubleValue());
|
||||
}
|
||||
_attributes_dirty |= RECT;
|
||||
return;
|
||||
}
|
||||
|
||||
if( child->getParent() != _node )
|
||||
return;
|
||||
@@ -893,67 +922,68 @@ namespace canvas
|
||||
return values;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------
|
||||
void operator+=(CoordList& base, const std::initializer_list<VGfloat>& other)
|
||||
{
|
||||
base.insert(base.end(), other.begin(), other.end());
|
||||
}
|
||||
|
||||
void operator+=(CoordList& base, const std::initializer_list<VGfloat>& other)
|
||||
{
|
||||
base.insert(base.end(), other.begin(), other.end());
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::parseRectToVGPath()
|
||||
{
|
||||
CmdList commands;
|
||||
CoordList coords;
|
||||
commands.reserve(4);
|
||||
coords.reserve(8);
|
||||
|
||||
bool haveCorner = false;
|
||||
SGVec2f topLeft = parseRectCornerRadius(_node, "left", "top", haveCorner);
|
||||
if (haveCorner) {
|
||||
commands.push_back(VG_MOVE_TO_ABS);
|
||||
coords += {_rect.l(), _rect.t() + topLeft.y()};
|
||||
commands.push_back(VG_SCCWARC_TO_REL);
|
||||
coords += {topLeft.x(), topLeft.y(), 0.0, topLeft.x(), -topLeft.y()};
|
||||
} else {
|
||||
commands.push_back(VG_MOVE_TO_ABS);
|
||||
coords += {_rect.l(), _rect.t()};
|
||||
}
|
||||
|
||||
void Path::parseRectToVGPath()
|
||||
{
|
||||
CmdList commands;
|
||||
CoordList coords;
|
||||
commands.reserve(4);
|
||||
coords.reserve(8);
|
||||
|
||||
bool haveCorner = false;
|
||||
SGVec2f topLeft = parseRectCornerRadius(_node, "left", "top", haveCorner);
|
||||
if (haveCorner) {
|
||||
commands.push_back(VG_MOVE_TO_ABS);
|
||||
coords += {_rect.l(), _rect.t() + topLeft.y()};
|
||||
commands.push_back(VG_SCCWARC_TO_REL);
|
||||
coords += {topLeft.x(), topLeft.y(), 0.0, topLeft.x(), -topLeft.y()};
|
||||
} else {
|
||||
commands.push_back(VG_MOVE_TO_ABS);
|
||||
coords += {_rect.l(), _rect.t()};
|
||||
}
|
||||
|
||||
SGVec2f topRight = parseRectCornerRadius(_node, "right", "top", haveCorner);
|
||||
if (haveCorner) {
|
||||
commands.push_back(VG_HLINE_TO_ABS);
|
||||
coords += {_rect.r() - topRight.x()};
|
||||
commands.push_back(VG_SCCWARC_TO_REL);
|
||||
coords += {topRight.x(), topRight.y(), 0.0, topRight.x(), topRight.y()};
|
||||
} else {
|
||||
commands.push_back(VG_HLINE_TO_ABS);
|
||||
coords += {_rect.r()};
|
||||
}
|
||||
|
||||
SGVec2f bottomRight = parseRectCornerRadius(_node, "right", "bottom", haveCorner);
|
||||
if (haveCorner) {
|
||||
commands.push_back(VG_VLINE_TO_ABS);
|
||||
coords += {_rect.b() - bottomRight.y()};
|
||||
commands.push_back(VG_SCCWARC_TO_REL);
|
||||
coords += {bottomRight.x(), bottomRight.y(), 0.0, -bottomRight.x(), bottomRight.y()};
|
||||
} else {
|
||||
commands.push_back(VG_VLINE_TO_ABS);
|
||||
coords += {_rect.b()};
|
||||
}
|
||||
|
||||
SGVec2f bottomLeft = parseRectCornerRadius(_node, "left", "bottom", haveCorner);
|
||||
if (haveCorner) {
|
||||
commands.push_back(VG_HLINE_TO_ABS);
|
||||
coords += {_rect.l() + bottomLeft.x()};
|
||||
commands.push_back(VG_SCCWARC_TO_REL);
|
||||
coords += {bottomLeft.x(), bottomLeft.y(), 0.0, -bottomLeft.x(), -bottomLeft.y()};
|
||||
} else {
|
||||
commands.push_back(VG_HLINE_TO_ABS);
|
||||
coords += {_rect.l()};
|
||||
}
|
||||
|
||||
commands.push_back(VG_CLOSE_PATH);
|
||||
_path->setSegments(commands, coords);
|
||||
SGVec2f topRight = parseRectCornerRadius(_node, "right", "top", haveCorner);
|
||||
if (haveCorner) {
|
||||
commands.push_back(VG_HLINE_TO_ABS);
|
||||
coords += {_rect.r() - topRight.x()};
|
||||
commands.push_back(VG_SCCWARC_TO_REL);
|
||||
coords += {topRight.x(), topRight.y(), 0.0, topRight.x(), topRight.y()};
|
||||
} else {
|
||||
commands.push_back(VG_HLINE_TO_ABS);
|
||||
coords += {_rect.r()};
|
||||
}
|
||||
|
||||
SGVec2f bottomRight = parseRectCornerRadius(_node, "right", "bottom", haveCorner);
|
||||
if (haveCorner) {
|
||||
commands.push_back(VG_VLINE_TO_ABS);
|
||||
coords += {_rect.b() - bottomRight.y()};
|
||||
commands.push_back(VG_SCCWARC_TO_REL);
|
||||
coords += {bottomRight.x(), bottomRight.y(), 0.0, -bottomRight.x(), bottomRight.y()};
|
||||
} else {
|
||||
commands.push_back(VG_VLINE_TO_ABS);
|
||||
coords += {_rect.b()};
|
||||
}
|
||||
|
||||
SGVec2f bottomLeft = parseRectCornerRadius(_node, "left", "bottom", haveCorner);
|
||||
if (haveCorner) {
|
||||
commands.push_back(VG_HLINE_TO_ABS);
|
||||
coords += {_rect.l() + bottomLeft.x()};
|
||||
commands.push_back(VG_SCCWARC_TO_REL);
|
||||
coords += {bottomLeft.x(), bottomLeft.y(), 0.0, -bottomLeft.x(), -bottomLeft.y()};
|
||||
} else {
|
||||
commands.push_back(VG_HLINE_TO_ABS);
|
||||
coords += {_rect.l()};
|
||||
}
|
||||
|
||||
commands.push_back(VG_CLOSE_PATH);
|
||||
_path->setSegments(commands, coords);
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
#define CANVAS_PATH_HXX_
|
||||
|
||||
#include "CanvasElement.hxx"
|
||||
#include <boost/preprocessor/iteration/iterate.hpp>
|
||||
#include <simgear/math/SGRect.hxx>
|
||||
#include <initializer_list>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
@@ -40,14 +40,10 @@ namespace canvas
|
||||
ElementWeakPtr parent = 0 );
|
||||
virtual ~Path();
|
||||
|
||||
virtual void update(double dt);
|
||||
|
||||
virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
|
||||
|
||||
#define BOOST_PP_ITERATION_LIMITS (0, 6)
|
||||
#define BOOST_PP_FILENAME_1 \
|
||||
<simgear/canvas/elements/detail/add_segment_variadic.hxx>
|
||||
#include BOOST_PP_ITERATE()
|
||||
/** Add a segment with the given command and coordinates */
|
||||
Path& addSegment(uint8_t cmd, std::initializer_list<float> coords = {});
|
||||
|
||||
/** Move path cursor */
|
||||
Path& moveTo(float x_abs, float y_abs);
|
||||
@@ -70,10 +66,10 @@ namespace canvas
|
||||
|
||||
void setSVGPath(const std::string& svgPath);
|
||||
|
||||
void setRect(const SGRect<float>& r);
|
||||
void setRoundRect(const SGRect<float>& r, float radiusX, float radiusY = -1.0);
|
||||
protected:
|
||||
void setRect(const SGRectf& r);
|
||||
void setRoundRect(const SGRectf& r, float radiusX, float radiusY = -1.0);
|
||||
|
||||
protected:
|
||||
enum PathAttributes
|
||||
{
|
||||
CMDS = LAST_ATTRIBUTE << 1,
|
||||
@@ -88,12 +84,14 @@ namespace canvas
|
||||
|
||||
bool _hasSVG : 1;
|
||||
bool _hasRect : 1;
|
||||
SGRect<float> _rect;
|
||||
SGRectf _rect;
|
||||
|
||||
virtual void updateImpl(double dt);
|
||||
|
||||
void parseRectToVGPath();
|
||||
|
||||
virtual void childRemoved(SGPropertyNode * child);
|
||||
virtual void childChanged(SGPropertyNode * child);
|
||||
|
||||
void parseRectToVGPath();
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
|
||||
@@ -865,12 +865,12 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
osg::StateSet* Text::getOrCreateStateSet()
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
return 0;
|
||||
if( !_scene_group.valid() )
|
||||
return nullptr;
|
||||
|
||||
// Only check for StateSet on Transform, as the text stateset is shared
|
||||
// between all text instances using the same font (texture).
|
||||
return _transform->getOrCreateStateSet();
|
||||
return _scene_group->getOrCreateStateSet();
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#ifndef CANVAS_PATH_HXX_
|
||||
# error Canvas - do not include this file!
|
||||
#endif
|
||||
|
||||
#define n BOOST_PP_ITERATION()
|
||||
|
||||
Path& addSegment( uint8_t cmd
|
||||
BOOST_PP_COMMA_IF(n)
|
||||
BOOST_PP_ENUM_PARAMS(n, float coord) )
|
||||
{
|
||||
_node->addChild("cmd")->setIntValue(cmd);
|
||||
|
||||
#define SG_CANVAS_PATH_SET_COORD(z, n, dummy)\
|
||||
_node->addChild("coord")->setFloatValue(coord##n);
|
||||
|
||||
BOOST_PP_REPEAT(n, SG_CANVAS_PATH_SET_COORD, 0)
|
||||
#undef SG_CANVAS_PATH_SET_COORD
|
||||
return *this;
|
||||
}
|
||||
|
||||
#undef n
|
||||
@@ -190,6 +190,32 @@ namespace canvas
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* WebMercator projection, relative to the projection center.
|
||||
* Required for Slippy Maps - i.e. openstreetmap
|
||||
*/
|
||||
class WebMercatorProjection:
|
||||
public HorizontalProjection
|
||||
{
|
||||
protected:
|
||||
|
||||
virtual ScreenPosition project(double lat, double lon) const
|
||||
{
|
||||
double d_lat = lat - _ref_lat,
|
||||
d_lon = lon - _ref_lon;
|
||||
double r = 6378137.f / 1852; // Equatorial radius divided by ?
|
||||
|
||||
ScreenPosition pos;
|
||||
|
||||
pos.x = r * d_lon;
|
||||
pos.y = r * (log(tan(d_lat) + 1.0 / cos(d_lat)));
|
||||
//pos.x = lon;
|
||||
//pos.y = log(tan(lat) + 1.0 / cos(lat));
|
||||
return pos;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "KeyboardEvent.hxx"
|
||||
#include "utf8.h"
|
||||
|
||||
#include <osgGA/GUIEventAdapter>
|
||||
|
||||
@@ -42,6 +41,7 @@ namespace canvas
|
||||
|
||||
// TODO check Win/Mac keycode for altgr/ISO Level3 Shift
|
||||
const uint32_t KEY_AltGraph = 0xfe03;
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
KeyboardEvent::KeyboardEvent():
|
||||
@@ -269,10 +269,24 @@ namespace canvas
|
||||
// Empty or no mapping -> convert UTF-32 key value to UTF-8
|
||||
if( _name.empty() )
|
||||
{
|
||||
if( !utf8::internal::is_code_point_valid(_key) )
|
||||
if (( _key >= 0xd800u && _key <= 0xdfffu ) || _key > 0x10ffffu )
|
||||
_name = "Unidentified";
|
||||
else
|
||||
utf8::unchecked::append(_key, std::back_inserter(_name));
|
||||
if ( _key <= 0x7f ) {
|
||||
_name.push_back(static_cast<uint8_t>(_key));
|
||||
} else if ( _key <= 0x7ff ) {
|
||||
_name.push_back(static_cast<uint8_t>((_key >> 6) | 0xc0));
|
||||
_name.push_back(static_cast<uint8_t>((_key & 0x3f) | 0x80));
|
||||
} else if ( _key <= 0xffff ) {
|
||||
_name.push_back(static_cast<uint8_t>((_key >> 12) | 0xe0));
|
||||
_name.push_back(static_cast<uint8_t>(((_key >> 6) & 0x3f) | 0x80));
|
||||
_name.push_back(static_cast<uint8_t>((_key & 0x3f) | 0x80));
|
||||
} else {
|
||||
_name.push_back(static_cast<uint8_t>((_key >> 18) | 0xf0));
|
||||
_name.push_back(static_cast<uint8_t>(((_key >> 12) & 0x3f) | 0x80));
|
||||
_name.push_back(static_cast<uint8_t>(((_key >> 6) & 0x3f) | 0x80));
|
||||
_name.push_back(static_cast<uint8_t>((_key & 0x3f) | 0x80));
|
||||
}
|
||||
}
|
||||
|
||||
// Keys on the numpad with NumLock enabled are reported just like their
|
||||
@@ -307,11 +321,30 @@ namespace canvas
|
||||
if( key_name.empty() )
|
||||
return false;
|
||||
|
||||
std::string::const_iterator it = key_name.begin();
|
||||
uint32_t cp = utf8::next(it, key_name.end());
|
||||
// Convert the key name to the corresponding code point by checking the
|
||||
// sequence length (the first bits of the first byte) and performing the
|
||||
// conversion accordingly.
|
||||
uint32_t cp = key_name[0] & 0xff;
|
||||
size_t len;
|
||||
if (cp < 0x80) {
|
||||
len = 1;
|
||||
} else if ((cp >> 5) == 0x6) {
|
||||
cp = ((cp << 6) & 0x7ff) + (key_name[1] & 0x3f);
|
||||
len = 2;
|
||||
} else if ((cp >> 4) == 0xe) {
|
||||
cp = ((cp << 12) & 0xffff) + (((key_name[1] & 0xff) << 6) & 0xfff)
|
||||
+ (key_name[2] & 0x3f);
|
||||
len = 3;
|
||||
} else if ((cp >> 3) == 0x1e) {
|
||||
cp = ((cp << 18) & 0x1fffff) + (((key_name[1] & 0xff) << 12) & 0x3ffff)
|
||||
+ (((key_name[2] & 0xff) << 6) & 0xfff) + (key_name[3] & 0x3f);
|
||||
len = 4;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if _name contains exactly one (UTF-8 encoded) character.
|
||||
if( it != key_name.end() )
|
||||
if (key_name.length() > len)
|
||||
return false;
|
||||
|
||||
// C0 and C1 control characters are not printable.
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
NasalWidget::~NasalWidget()
|
||||
{
|
||||
onRemove();
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -189,10 +189,10 @@ namespace canvas
|
||||
if( hfw.empty() )
|
||||
return -1;
|
||||
|
||||
naContext c = naNewContext();
|
||||
try
|
||||
{
|
||||
return hfw(nasal::to_nasal(c, const_cast<NasalWidget*>(this)), w);
|
||||
nasal::Context ctx;
|
||||
return hfw(ctx.to_me(const_cast<NasalWidget*>(this)), w);
|
||||
}
|
||||
catch( std::exception const& ex )
|
||||
{
|
||||
@@ -202,7 +202,6 @@ namespace canvas
|
||||
"NasalWidget.heightForWidth: callback error: '" << ex.what() << "'"
|
||||
);
|
||||
}
|
||||
naFreeContext(c);
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -262,8 +261,8 @@ namespace canvas
|
||||
|
||||
try
|
||||
{
|
||||
nasal::Context c;
|
||||
_set_geometry(nasal::to_nasal(c, this), rect);
|
||||
nasal::Context ctx;
|
||||
_set_geometry(ctx.to_me(this), rect);
|
||||
_flags &= ~LAYOUT_DIRTY;
|
||||
}
|
||||
catch( std::exception const& ex )
|
||||
|
||||
@@ -95,7 +95,7 @@ const float SG_RADIANS_TO_DEGREES = 180.0f / SG_PI;
|
||||
/** Value of earth radius from LaRCsim (ft) */
|
||||
#define SG_EQUATORIAL_RADIUS_FT 20925650.
|
||||
|
||||
/** Value of earth radius from LaRCsim (meter) */
|
||||
/** Value of equatorial earth radius from LaRCsim (meter) */
|
||||
#define SG_EQUATORIAL_RADIUS_M 6378138.12
|
||||
|
||||
/** Radius squared (ft) */
|
||||
@@ -104,6 +104,8 @@ const float SG_RADIANS_TO_DEGREES = 180.0f / SG_PI;
|
||||
/** Radius squared (meter) */
|
||||
#define SG_EQ_RAD_SQUARE_M 40680645877797.1344
|
||||
|
||||
/** Value of WGS84 polar earth radius (meter) */
|
||||
#define SG_POLAR_RADIUS_M 6356752.3142451794975639668
|
||||
|
||||
// Physical Constants, SI
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
include (SimGearComponent)
|
||||
|
||||
set(HEADERS EmbeddedResource.hxx EmbeddedResourceManager.hxx)
|
||||
set(SOURCES EmbeddedResource.cxx EmbeddedResourceManager.cxx)
|
||||
set(HEADERS EmbeddedResource.hxx
|
||||
EmbeddedResourceManager.hxx
|
||||
EmbeddedResourceProxy.hxx)
|
||||
set(SOURCES EmbeddedResource.cxx
|
||||
EmbeddedResourceManager.cxx
|
||||
EmbeddedResourceProxy.cxx)
|
||||
|
||||
simgear_component(embedded_resources embedded_resources
|
||||
"${SOURCES}" "${HEADERS}")
|
||||
|
||||
257
simgear/embedded_resources/EmbeddedResourceProxy.cxx
Normal file
257
simgear/embedded_resources/EmbeddedResourceProxy.cxx
Normal file
@@ -0,0 +1,257 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// EmbeddedResourceProxy.cxx --- Unified access to real files or embedded
|
||||
// resources
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <algorithm> // std::find()
|
||||
#include <ios> // std::streamsize
|
||||
#include <istream>
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstdlib> // std::size_t
|
||||
#include <cassert>
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include "EmbeddedResourceManager.hxx"
|
||||
#include "EmbeddedResourceProxy.hxx"
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::shared_ptr;
|
||||
using std::unique_ptr;
|
||||
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
EmbeddedResourceProxy::EmbeddedResourceProxy(const SGPath& realRoot,
|
||||
const string& virtualRoot,
|
||||
bool useEmbeddedResourcesByDefault)
|
||||
: _realRoot(realRoot),
|
||||
_virtualRoot(normalizeVirtualRoot(virtualRoot)),
|
||||
_useEmbeddedResourcesByDefault(useEmbeddedResourcesByDefault)
|
||||
{ }
|
||||
|
||||
SGPath
|
||||
EmbeddedResourceProxy::getRealRoot() const
|
||||
{ return _realRoot; }
|
||||
|
||||
void
|
||||
EmbeddedResourceProxy::setRealRoot(const SGPath& realRoot)
|
||||
{ _realRoot = realRoot; }
|
||||
|
||||
string
|
||||
EmbeddedResourceProxy::getVirtualRoot() const
|
||||
{ return _virtualRoot; }
|
||||
|
||||
void
|
||||
EmbeddedResourceProxy::setVirtualRoot(const string& virtualRoot)
|
||||
{ _virtualRoot = normalizeVirtualRoot(virtualRoot); }
|
||||
|
||||
bool
|
||||
EmbeddedResourceProxy::getUseEmbeddedResources() const
|
||||
{ return _useEmbeddedResourcesByDefault; }
|
||||
|
||||
void
|
||||
EmbeddedResourceProxy::setUseEmbeddedResources(bool useEmbeddedResources)
|
||||
{ _useEmbeddedResourcesByDefault = useEmbeddedResources; }
|
||||
|
||||
// Static method: normalize the 'virtualRoot' argument of the constructor
|
||||
//
|
||||
// The argument must start with a slash and mustn't contain any '.' or '..'
|
||||
// component. The return value never ends with a slash.
|
||||
string
|
||||
EmbeddedResourceProxy::normalizeVirtualRoot(const string& path)
|
||||
{
|
||||
EmbeddedResourceProxy::checkPath(__func__, path,
|
||||
false /* allowStartWithColon */);
|
||||
string res = path;
|
||||
|
||||
// Make sure 'res' doesn't end with a '/'.
|
||||
while (!res.empty() && res.back() == '/') {
|
||||
res.pop_back(); // This will ease path concatenation
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Static method
|
||||
void
|
||||
EmbeddedResourceProxy::checkPath(const string& callerMethod, const string& path,
|
||||
bool allowStartWithColon)
|
||||
{
|
||||
if (path.empty()) {
|
||||
throw sg_format_exception(
|
||||
"Invalid empty path for EmbeddedResourceProxy::" +
|
||||
callerMethod + "(): '" + path + "'", path);
|
||||
} else if (allowStartWithColon &&
|
||||
!simgear::strutils::starts_with(path, ":/") && path[0] != '/') {
|
||||
throw sg_format_exception(
|
||||
"Invalid path for EmbeddedResourceProxy::" + callerMethod + "(): "
|
||||
"it should start with either ':/' or '/'", path);
|
||||
} else if (!allowStartWithColon && path[0] != '/') {
|
||||
throw sg_format_exception(
|
||||
"Invalid path for EmbeddedResourceProxy::" + callerMethod + "(): "
|
||||
"it should start with a slash ('/')", path);
|
||||
} else {
|
||||
const vector<string> components = simgear::strutils::split(path, "/");
|
||||
auto find = [&components](const string& s) -> bool {
|
||||
return (std::find(components.begin(), components.end(), s) !=
|
||||
components.end());
|
||||
};
|
||||
|
||||
if (find(".") || find("..")) {
|
||||
throw sg_format_exception(
|
||||
"Invalid path for EmbeddedResourceProxy::" + callerMethod + "(): "
|
||||
"'.' and '..' components are not allowed", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unique_ptr<std::istream>
|
||||
EmbeddedResourceProxy::getIStream(const string& path, bool fromEmbeddedResource)
|
||||
const
|
||||
{
|
||||
EmbeddedResourceProxy::checkPath(__func__, path,
|
||||
false /* allowStartWithColon */);
|
||||
assert(!path.empty() && path.front() == '/');
|
||||
|
||||
if (fromEmbeddedResource) {
|
||||
const auto& embeddedResMgr = simgear::EmbeddedResourceManager::instance();
|
||||
return embeddedResMgr->getIStream(
|
||||
_virtualRoot + path,
|
||||
""); // fetch the default-locale version of the resource
|
||||
} else {
|
||||
const SGPath sgPath = _realRoot / path.substr(std::size_t(1));
|
||||
return unique_ptr<std::istream>(new sg_ifstream(sgPath));
|
||||
}
|
||||
}
|
||||
|
||||
unique_ptr<std::istream>
|
||||
EmbeddedResourceProxy::getIStream(const string& path) const
|
||||
{
|
||||
return getIStream(path, _useEmbeddedResourcesByDefault);
|
||||
}
|
||||
|
||||
unique_ptr<std::istream>
|
||||
EmbeddedResourceProxy::getIStreamDecideOnPrefix(const string& path) const
|
||||
{
|
||||
EmbeddedResourceProxy::checkPath(__func__, path,
|
||||
true /* allowStartWithColon */);
|
||||
|
||||
// 'path' is non-empty
|
||||
if (path.front() == '/') {
|
||||
return getIStream(path, false /* fromEmbeddedResource */);
|
||||
} else if (path.front() == ':') {
|
||||
assert(path.size() >= 2 && path[1] == '/');
|
||||
// Skip the leading ':'
|
||||
return getIStream(path.substr(std::size_t(1)),
|
||||
true /* fromEmbeddedResource */);
|
||||
} else {
|
||||
// The checkPath() call should make it impossible to reach this point.
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
string
|
||||
EmbeddedResourceProxy::getString(const string& path, bool fromEmbeddedResource)
|
||||
const
|
||||
{
|
||||
string result;
|
||||
|
||||
EmbeddedResourceProxy::checkPath(__func__, path,
|
||||
false /* allowStartWithColon */);
|
||||
assert(!path.empty() && path.front() == '/');
|
||||
|
||||
if (fromEmbeddedResource) {
|
||||
const auto& embeddedResMgr = simgear::EmbeddedResourceManager::instance();
|
||||
// Fetch the default-locale version of the resource
|
||||
result = embeddedResMgr->getString(_virtualRoot + path, "");
|
||||
} else {
|
||||
const SGPath sgPath = _realRoot / path.substr(std::size_t(1));
|
||||
result.reserve(sgPath.sizeInBytes());
|
||||
const unique_ptr<std::istream> streamp = getIStream(path,
|
||||
fromEmbeddedResource);
|
||||
std::streamsize nbCharsRead;
|
||||
|
||||
// Allocate a buffer
|
||||
static constexpr std::size_t bufSize = 65536;
|
||||
static_assert(bufSize <= std::numeric_limits<std::streamsize>::max(),
|
||||
"Type std::streamsize is unexpectedly small");
|
||||
static_assert(bufSize <= std::numeric_limits<string::size_type>::max(),
|
||||
"Type std::string::size_type is unexpectedly small");
|
||||
unique_ptr<char[]> buf(new char[bufSize]);
|
||||
|
||||
do {
|
||||
streamp->read(buf.get(), bufSize);
|
||||
nbCharsRead = streamp->gcount();
|
||||
|
||||
if (nbCharsRead > 0) {
|
||||
result.append(buf.get(), nbCharsRead);
|
||||
}
|
||||
} while (*streamp);
|
||||
|
||||
// streamp->fail() would *not* indicate an error, due to the semantics
|
||||
// of std::istream::read().
|
||||
if (streamp->bad()) {
|
||||
throw sg_io_exception("Error reading from file", sg_location(path));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
string
|
||||
EmbeddedResourceProxy::getString(const string& path) const
|
||||
{
|
||||
return getString(path, _useEmbeddedResourcesByDefault);
|
||||
}
|
||||
|
||||
string
|
||||
EmbeddedResourceProxy::getStringDecideOnPrefix(const string& path) const
|
||||
{
|
||||
string result;
|
||||
|
||||
EmbeddedResourceProxy::checkPath(__func__, path,
|
||||
true /* allowStartWithColon */);
|
||||
|
||||
// 'path' is non-empty
|
||||
if (path.front() == '/') {
|
||||
result = getString(path, false /* fromEmbeddedResource */);
|
||||
} else if (path.front() == ':') {
|
||||
assert(path.size() >= 2 && path[1] == '/');
|
||||
// Skip the leading ':'
|
||||
result = getString(path.substr(std::size_t(1)),
|
||||
true /* fromEmbeddedResource */);
|
||||
} else {
|
||||
// The checkPath() call should make it impossible to reach this point.
|
||||
std::abort();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // of namespace simgear
|
||||
155
simgear/embedded_resources/EmbeddedResourceProxy.hxx
Normal file
155
simgear/embedded_resources/EmbeddedResourceProxy.hxx
Normal file
@@ -0,0 +1,155 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// EmbeddedResourceProxy.hxx --- Unified access to real files or embedded
|
||||
// resources
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#ifndef FG_EMBEDDEDRESOURCEPROXY_HXX
|
||||
#define FG_EMBEDDEDRESOURCEPROXY_HXX
|
||||
|
||||
#include <istream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
// The EmbeddedResourceProxy class allows one to access real files or embedded
|
||||
// resources in a unified way. When using it, one can switch from one data
|
||||
// source to the other with minimal code changes, possibly even at runtime (in
|
||||
// which case there is obviously no code change at all).
|
||||
//
|
||||
// Sample usage of the EmbeddedResourceProxy class (from FlightGear):
|
||||
//
|
||||
// simgear::EmbeddedResourceProxy proxy(globals->get_fg_root(), "/FGData");
|
||||
// std::string s = proxy.getString("/some/path");
|
||||
// std::unique_ptr<std::istream> streamp = proxy.getIStream("/some/path");
|
||||
//
|
||||
// The methods getString(const std::string& path) and
|
||||
// getIStream(const std::string& path) of EmbeddedResourceProxy decide whether
|
||||
// to use embedded resources or real files depending on the boolean value
|
||||
// passed to EmbeddedResourceProxy::setUseEmbeddedResources() (also available
|
||||
// as an optional parameter to the EmbeddedResourceProxy constructor,
|
||||
// defaulting to true). It is often most convenient to set this boolean once
|
||||
// and then don't worry about it anymore (it is stored as a data member of
|
||||
// EmbeddedResourceProxy). Otherwise, if you want to fetch resources some
|
||||
// times from real files, other times from embedded resources, you may use the
|
||||
// following methods:
|
||||
//
|
||||
// // Retrieve contents using embedded resources
|
||||
// std:string s = proxy.getString("/some/path", true);
|
||||
// std:string s = proxy.getStringDecideOnPrefix(":/some/path");
|
||||
//
|
||||
// // Retrieve contents using real files
|
||||
// std:string s = proxy.getString("/some/path", false);
|
||||
// std:string s = proxy.getStringDecideOnPrefix("/some/path");
|
||||
//
|
||||
// You can do exactly the same with EmbeddedResourceProxy::getIStream() and
|
||||
// EmbeddedResourceProxy::getIStreamDecideOnPrefix(), except they return an
|
||||
// std::unique_ptr<std::istream> instead of an std::string.
|
||||
//
|
||||
// Given how the 'proxy' object was constructed above, each of these calls
|
||||
// will fetch data from either the real file $FG_ROOT/some/path or the
|
||||
// embedded resource whose virtual path is '/FGData/some/path' (more
|
||||
// precisely: the default-locale version of this resource).
|
||||
//
|
||||
// The 'path' argument of EmbeddedResourceProxy's methods getString(),
|
||||
// getIStream(), getStringDecideOnPrefix() and getIStreamDecideOnPrefix()
|
||||
// must:
|
||||
//
|
||||
// - use UTF-8 encoding;
|
||||
//
|
||||
// - start with:
|
||||
// * either '/' or ':/' for the 'DecideOnPrefix' variants;
|
||||
// * only '/' for the other methods.
|
||||
//
|
||||
// - have its components separated by slashes;
|
||||
//
|
||||
// - not contain any '.' or '..' component.
|
||||
//
|
||||
// For the 'DecideOnPrefix' variants:
|
||||
//
|
||||
// - if the path starts with a slash ('/'), a real file access is done;
|
||||
//
|
||||
// - if, on the other hand, it starts with ':/', EmbeddedResourceProxy uses
|
||||
// the embedded resource whose virtual path is the specified path without
|
||||
// its leading ':' (more precisely: the default-locale version of this
|
||||
// resource).
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
class EmbeddedResourceProxy
|
||||
{
|
||||
public:
|
||||
// 'virtualRoot' must start with a '/', e.g: '/FGData'. Whether it ends
|
||||
// with a '/' doesn't make a difference.
|
||||
explicit EmbeddedResourceProxy(const SGPath& realRoot,
|
||||
const std::string& virtualRoot,
|
||||
bool useEmbeddedResourcesByDefault = true);
|
||||
|
||||
// Getters and setters for the corresponding data members
|
||||
SGPath getRealRoot() const;
|
||||
void setRealRoot(const SGPath& realRoot);
|
||||
|
||||
std::string getVirtualRoot() const;
|
||||
void setVirtualRoot(const std::string& virtualRoot);
|
||||
|
||||
bool getUseEmbeddedResources() const;
|
||||
void setUseEmbeddedResources(bool useEmbeddedResources);
|
||||
|
||||
// Get an std::istream to read from a file or from an embedded resource.
|
||||
std::unique_ptr<std::istream>
|
||||
getIStream(const std::string& path, bool fromEmbeddedResource) const;
|
||||
|
||||
std::unique_ptr<std::istream>
|
||||
getIStream(const std::string& path) const;
|
||||
|
||||
std::unique_ptr<std::istream>
|
||||
getIStreamDecideOnPrefix(const std::string& path) const;
|
||||
|
||||
// Get a file or embedded resource contents as a string.
|
||||
std::string
|
||||
getString(const std::string& path, bool fromEmbeddedResource) const;
|
||||
|
||||
std::string
|
||||
getString(const std::string& path) const;
|
||||
|
||||
std::string
|
||||
getStringDecideOnPrefix(const std::string& path) const;
|
||||
|
||||
private:
|
||||
// Check that 'path' starts with either ':/' or '/', and doesn't contain any
|
||||
// '..' component ('path' may only start with ':/' if 'allowStartWithColon'
|
||||
// is true).
|
||||
static void
|
||||
checkPath(const std::string& callerMethod, const std::string& path,
|
||||
bool allowStartWithColon);
|
||||
|
||||
// Normalize the 'virtualRoot' argument of the constructor. The argument
|
||||
// must start with a '/' and mustn't contain any '.' or '..' component. The
|
||||
// return value never ends with a '/'.
|
||||
static std::string
|
||||
normalizeVirtualRoot(const std::string& path);
|
||||
|
||||
SGPath _realRoot;
|
||||
std::string _virtualRoot;
|
||||
bool _useEmbeddedResourcesByDefault;
|
||||
};
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
#endif // of FG_EMBEDDEDRESOURCEPROXY_HXX
|
||||
@@ -33,11 +33,14 @@
|
||||
#include <cstddef> // std::size_t
|
||||
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/io/iostreams/CharArrayStream.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/io/iostreams/zlibstream.hxx>
|
||||
#include "EmbeddedResource.hxx"
|
||||
#include "EmbeddedResourceManager.hxx"
|
||||
#include "EmbeddedResourceProxy.hxx"
|
||||
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
@@ -395,6 +398,106 @@ void test_getLocaleAndSelectLocale()
|
||||
}
|
||||
}
|
||||
|
||||
// Auxiliary function for test_EmbeddedResourceProxy()
|
||||
void auxTest_EmbeddedResourceProxy_getIStream(unique_ptr<std::istream> iStream,
|
||||
const string& contents)
|
||||
{
|
||||
cout << "Testing EmbeddedResourceProxy::getIStream()" << endl;
|
||||
|
||||
iStream->exceptions(std::ios_base::badbit);
|
||||
static constexpr std::size_t bufSize = 65536;
|
||||
unique_ptr<char[]> buf(new char[bufSize]); // intermediate buffer
|
||||
string result;
|
||||
|
||||
do {
|
||||
iStream->read(buf.get(), bufSize);
|
||||
result.append(buf.get(), iStream->gcount());
|
||||
} while (*iStream); // iStream *points* to an std::istream
|
||||
|
||||
// 1) If set, badbit would have caused an exception to be raised (see above).
|
||||
// 2) failbit doesn't necessarily indicate an error here: it is set as soon
|
||||
// as the read() call can't provide the requested number of characters.
|
||||
SG_VERIFY(iStream->eof() && !iStream->bad());
|
||||
SG_CHECK_EQUAL(result, contents);
|
||||
}
|
||||
|
||||
void test_EmbeddedResourceProxy()
|
||||
{
|
||||
cout << "Testing the EmbeddedResourceProxy class" << endl;
|
||||
|
||||
// Initialize stuff we need and create two files containing the contents of
|
||||
// the default-locale version of two embedded resources: those with virtual
|
||||
// paths '/path/to/resource1' and '/path/to/resource2'.
|
||||
const auto& resMgr = EmbeddedResourceManager::instance();
|
||||
simgear::Dir tmpDir = simgear::Dir::tempDir("FlightGear");
|
||||
tmpDir.setRemoveOnDestroy();
|
||||
|
||||
const SGPath path1 = tmpDir.path() / "resource1";
|
||||
const SGPath path2 = tmpDir.path() / "resource2";
|
||||
|
||||
sg_ofstream out1(path1);
|
||||
sg_ofstream out2(path2);
|
||||
const string s1 = resMgr->getString("/path/to/resource1", "");
|
||||
// To make sure in these tests that we can tell whether something came from
|
||||
// a real file or from an embedded resource.
|
||||
const string rs1 = s1 + " from real file";
|
||||
const string rlipsum = lipsum + " from real file";
|
||||
|
||||
out1 << rs1;
|
||||
out1.close();
|
||||
if (!out1) {
|
||||
throw sg_io_exception("Error writing to file", sg_location(path1));
|
||||
}
|
||||
|
||||
out2 << rlipsum;
|
||||
out2.close();
|
||||
if (!out2) {
|
||||
throw sg_io_exception("Error writing to file", sg_location(path2));
|
||||
}
|
||||
|
||||
// 'proxy' defaults to using embedded resources
|
||||
const simgear::EmbeddedResourceProxy proxy(tmpDir.path(), "/path/to",
|
||||
/* useEmbeddedResourcesByDefault */
|
||||
true);
|
||||
simgear::EmbeddedResourceProxy rproxy(tmpDir.path(), "/path/to");
|
||||
// 'rproxy' defaults to using real files
|
||||
rproxy.setUseEmbeddedResources(false); // could be done from the ctor too
|
||||
|
||||
// Test EmbeddedResourceProxy::getString()
|
||||
SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix("/resource1"), rs1);
|
||||
SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix(":/resource1"), s1);
|
||||
SG_CHECK_EQUAL(proxy.getString("/resource1", false), rs1);
|
||||
SG_CHECK_EQUAL(proxy.getString("/resource1", true), s1);
|
||||
SG_CHECK_EQUAL(proxy.getString("/resource1"), s1);
|
||||
SG_CHECK_EQUAL(rproxy.getString("/resource1"), rs1);
|
||||
|
||||
SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix("/resource2"), rlipsum);
|
||||
SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix(":/resource2"), lipsum);
|
||||
SG_CHECK_EQUAL(proxy.getString("/resource2", false), rlipsum);
|
||||
SG_CHECK_EQUAL(proxy.getString("/resource2", true), lipsum);
|
||||
SG_CHECK_EQUAL(proxy.getString("/resource2"), lipsum);
|
||||
SG_CHECK_EQUAL(rproxy.getString("/resource2"), rlipsum);
|
||||
|
||||
// Test EmbeddedResourceProxy::getIStream()
|
||||
auxTest_EmbeddedResourceProxy_getIStream(
|
||||
proxy.getIStreamDecideOnPrefix("/resource1"),
|
||||
rs1);
|
||||
auxTest_EmbeddedResourceProxy_getIStream(
|
||||
proxy.getIStreamDecideOnPrefix(":/resource1"),
|
||||
s1);
|
||||
auxTest_EmbeddedResourceProxy_getIStream(proxy.getIStream("/resource1"), s1);
|
||||
auxTest_EmbeddedResourceProxy_getIStream(rproxy.getIStream("/resource1"), rs1);
|
||||
|
||||
auxTest_EmbeddedResourceProxy_getIStream(proxy.getIStream("/resource2", false),
|
||||
rlipsum);
|
||||
auxTest_EmbeddedResourceProxy_getIStream(proxy.getIStream("/resource2", true),
|
||||
lipsum);
|
||||
auxTest_EmbeddedResourceProxy_getIStream(proxy.getIStream("/resource2"),
|
||||
lipsum);
|
||||
auxTest_EmbeddedResourceProxy_getIStream(rproxy.getIStream("/resource2"),
|
||||
rlipsum);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// Initialize the EmbeddedResourceManager instance, add a few resources
|
||||
@@ -407,6 +510,7 @@ int main(int argc, char **argv)
|
||||
test_addAlreadyExistingResource();
|
||||
test_localeDependencyOfResourceFetching();
|
||||
test_getLocaleAndSelectLocale();
|
||||
test_EmbeddedResourceProxy();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -21,4 +21,9 @@ else()
|
||||
endif()
|
||||
|
||||
add_test(metar ${EXECUTABLE_OUTPUT_PATH}/test_metar)
|
||||
|
||||
add_executable(test_precipitation test_precipitation.cxx)
|
||||
target_link_libraries(test_precipitation ${TEST_LIBS})
|
||||
add_test(precipitation ${EXECUTABLE_OUTPUT_PATH}/test_precipitation)
|
||||
|
||||
endif(ENABLE_TESTS)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
* @brief Precipitation effects to draw rain and snow.
|
||||
*
|
||||
* @par Licences
|
||||
* @par License
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
@@ -25,7 +25,6 @@
|
||||
*/
|
||||
|
||||
#include "precipitation.hxx"
|
||||
//#include "visual_enviro.hxx"
|
||||
|
||||
#include <simgear/constants.h>
|
||||
#include <osg/ClipNode>
|
||||
@@ -65,7 +64,7 @@ bool SGPrecipitation::getEnabled() const
|
||||
*/
|
||||
osg::Group* SGPrecipitation::build(void)
|
||||
{
|
||||
osg::Group* group = new osg::Group;
|
||||
osg::ref_ptr<osg::Group> group = new osg::Group;
|
||||
|
||||
_precipitationEffect->snow(0);
|
||||
_precipitationEffect->rain(0);
|
||||
@@ -73,8 +72,9 @@ osg::Group* SGPrecipitation::build(void)
|
||||
if (_clip_distance!=0.0)
|
||||
{
|
||||
osg::ref_ptr<osg::ClipNode> clipNode = new osg::ClipNode;
|
||||
clipNode->addClipPlane( new osg::ClipPlane( 0 ) );
|
||||
clipNode->getClipPlane(0)->setClipPlane( 0.0, 0.0, -1.0, -_clip_distance );
|
||||
osg::ref_ptr<osg::ClipPlane> clipPlane = new osg::ClipPlane(0);
|
||||
clipNode->addClipPlane(clipPlane.get());
|
||||
clipNode->getClipPlane(0)->setClipPlane(0.0, 0.0, -1.0, -_clip_distance);
|
||||
clipNode->setReferenceFrame(osg::ClipNode::ABSOLUTE_RF);
|
||||
clipNode->addChild(_precipitationEffect.get());
|
||||
|
||||
@@ -87,7 +87,7 @@ osg::Group* SGPrecipitation::build(void)
|
||||
|
||||
group->setNodeMask( ~(simgear::CASTSHADOW_BIT | simgear::MODELLIGHT_BIT) );
|
||||
|
||||
return group;
|
||||
return group.release();
|
||||
}
|
||||
|
||||
|
||||
@@ -140,8 +140,8 @@ void SGPrecipitation::setRainDropletSize(float size)
|
||||
/**
|
||||
* @brief Define the illumination multiplier
|
||||
*
|
||||
* This function permits you to define and change the rain droplet size
|
||||
* which is used if external droplet size control is enabled
|
||||
* This function permits you to define and change the brightness
|
||||
* of the precipitation.
|
||||
*/
|
||||
|
||||
void SGPrecipitation::setIllumination(float illumination)
|
||||
@@ -163,10 +163,9 @@ void SGPrecipitation::setSnowFlakeSize(float size)
|
||||
|
||||
|
||||
/**
|
||||
* @brief Define the rain droplet size
|
||||
* @brief Define the clip plane distance
|
||||
*
|
||||
* This function permits you to define and change the rain droplet size
|
||||
* which is used if external droplet size control is enabled
|
||||
* This function permits you to define and change the clip plane distance.
|
||||
*/
|
||||
|
||||
void SGPrecipitation::setClipDistance(float distance)
|
||||
@@ -224,7 +223,7 @@ void SGPrecipitation::setWindProperty(double heading, double speed)
|
||||
* Be careful, if snow and rain intensity are greater than '0', snow effect
|
||||
* will be first.
|
||||
*
|
||||
* The settings come from the osgParticule/PrecipitationEffect.cpp exemple.
|
||||
* The settings come from the osgParticle/PrecipitationEffect.cpp example.
|
||||
*/
|
||||
bool SGPrecipitation::update(void)
|
||||
{
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
class SGPrecipitation : public osg::Referenced
|
||||
{
|
||||
private:
|
||||
protected:
|
||||
bool _freeze;
|
||||
bool _enabled;
|
||||
bool _droplet_external;
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
void setIllumination(float);
|
||||
void setClipDistance(float);
|
||||
|
||||
void setEnabled( bool );
|
||||
void setEnabled(bool);
|
||||
bool getEnabled() const;
|
||||
};
|
||||
|
||||
|
||||
239
simgear/environment/test_precipitation.cxx
Normal file
239
simgear/environment/test_precipitation.cxx
Normal file
@@ -0,0 +1,239 @@
|
||||
/**************************************************************************
|
||||
* test_precipitation.cxx -- unit-tests for SGPrecipitation class
|
||||
*
|
||||
* Copyright (C) 2017 Scott Giese (xDraconian) - <scttgs0@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Id$
|
||||
**************************************************************************/
|
||||
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
|
||||
#include <osg/Group>
|
||||
|
||||
#include <simgear/constants.h>
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
|
||||
#include "precipitation.hxx"
|
||||
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
|
||||
|
||||
class SGPrecipitationTestFixture : public SGPrecipitation
|
||||
{
|
||||
public:
|
||||
osg::ref_ptr<osg::Group> _group;
|
||||
|
||||
void test_configuration()
|
||||
{
|
||||
_group = build();
|
||||
|
||||
SG_VERIFY(getEnabled());
|
||||
SG_VERIFY(_precipitationEffect);
|
||||
SG_CHECK_EQUAL_EP(_rain_intensity, 0.0f);
|
||||
SG_CHECK_EQUAL_EP(_snow_intensity, 0.0f);
|
||||
SG_VERIFY(!_freeze);
|
||||
}
|
||||
|
||||
void test_rain()
|
||||
{
|
||||
int count = 500;
|
||||
|
||||
do {
|
||||
if (--count == 0) {
|
||||
cerr << "error: method setRainIntensity took too long.";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
float saveRainIntensity = _rain_intensity;
|
||||
|
||||
setRainIntensity(0.4f);
|
||||
update();
|
||||
|
||||
SG_CHECK_GE(_rain_intensity, saveRainIntensity);
|
||||
} while (std::fabs(_rain_intensity - 0.4f) > 0.00001f);
|
||||
|
||||
SG_CHECK_EQUAL_EP2(_rain_intensity, 0.4f, 0.00001f);
|
||||
}
|
||||
|
||||
void test_rain_external()
|
||||
{
|
||||
int count = 500;
|
||||
|
||||
do {
|
||||
if (--count == 0) {
|
||||
cerr << "error: method setRainIntensity-external took too long.";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
float saveRainIntensity = _rain_intensity;
|
||||
|
||||
setRainIntensity(0.25f);
|
||||
update();
|
||||
|
||||
SG_CHECK_LE(_rain_intensity, saveRainIntensity);
|
||||
} while (std::fabs(_rain_intensity - 0.25f) > 0.00001f);
|
||||
|
||||
// call once more to ensure we have intensity precisely set
|
||||
setRainIntensity(0.25f);
|
||||
update();
|
||||
|
||||
SG_CHECK_EQUAL_EP2(_rain_intensity, 0.25f, 0.00001f);
|
||||
}
|
||||
|
||||
void test_freeze()
|
||||
{
|
||||
float saveParticleSize = _precipitationEffect->getParticleSize();
|
||||
float saveParticleSpeed = std::fabs(_precipitationEffect->getParticleSpeed());
|
||||
|
||||
// change rain to snow
|
||||
// expect particle size to increase
|
||||
// expect particle speed to decrease
|
||||
setFreezing(true);
|
||||
update();
|
||||
|
||||
SG_CHECK_GT(_precipitationEffect->getParticleSize(), saveParticleSize);
|
||||
SG_CHECK_LT(std::fabs(_precipitationEffect->getParticleSpeed()), saveParticleSpeed);
|
||||
}
|
||||
|
||||
void test_snow()
|
||||
{
|
||||
int count = 500;
|
||||
|
||||
do {
|
||||
if (--count == 0) {
|
||||
cerr << "error: method setSnowIntensity took too long.";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
float saveSnowIntensity = _snow_intensity;
|
||||
|
||||
// not a typo - when freezing is enabled, snow intensity is keep in sync with rain intensity
|
||||
setRainIntensity(0.3f);
|
||||
update();
|
||||
|
||||
SG_CHECK_LE(_snow_intensity, saveSnowIntensity);
|
||||
} while (std::fabs(_snow_intensity - 0.3f) > 0.00001f);
|
||||
|
||||
SG_CHECK_EQUAL_EP2(_snow_intensity, 0.3f, 0.00001f);
|
||||
}
|
||||
|
||||
void test_snow_external()
|
||||
{
|
||||
setRainDropletSize(0.025f);
|
||||
setSnowFlakeSize(0.04f);
|
||||
setDropletExternal(true);
|
||||
|
||||
int count = 600;
|
||||
|
||||
do {
|
||||
if (--count == 0) {
|
||||
cerr << "error: method setSnowIntensity-external took too long.";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
float saveSnowIntensity = _snow_intensity;
|
||||
|
||||
setSnowIntensity(0.55f);
|
||||
update();
|
||||
|
||||
SG_CHECK_GE(_snow_intensity, saveSnowIntensity);
|
||||
} while (std::fabs(_snow_intensity - 0.55f) > 0.00001f);
|
||||
|
||||
// call once more to ensure we have intensity precisely set
|
||||
setSnowIntensity(0.55f);
|
||||
update();
|
||||
|
||||
SG_CHECK_EQUAL_EP2(_snow_intensity, 0.55f, 0.00001f);
|
||||
}
|
||||
|
||||
void test_unfreeze()
|
||||
{
|
||||
float saveParticleSize = _precipitationEffect->getParticleSize();
|
||||
float saveParticleSpeed = std::fabs(_precipitationEffect->getParticleSpeed());
|
||||
|
||||
// change snow to rain
|
||||
// expect particle size to decrease
|
||||
// expect particle speed to increase
|
||||
setFreezing(false);
|
||||
update();
|
||||
|
||||
SG_CHECK_LT(_precipitationEffect->getParticleSize(), saveParticleSize);
|
||||
SG_CHECK_GT(std::fabs(_precipitationEffect->getParticleSpeed()), saveParticleSpeed);
|
||||
}
|
||||
|
||||
void test_no_precipitation()
|
||||
{
|
||||
setEnabled(false);
|
||||
SG_VERIFY(!getEnabled());
|
||||
|
||||
update();
|
||||
|
||||
// intensity drops to zero, so we should see this reflected in the particles
|
||||
SG_CHECK_EQUAL_EP(_precipitationEffect->getParticleSize(), 0.01f);
|
||||
SG_CHECK_EQUAL_EP(_precipitationEffect->getParticleSpeed(), -2.0f);
|
||||
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
void test_illumination()
|
||||
{
|
||||
setIllumination(0.87f);
|
||||
SG_CHECK_EQUAL_EP(_illumination, 0.87f);
|
||||
}
|
||||
|
||||
void test_clipping()
|
||||
{
|
||||
setClipDistance(6.5);
|
||||
SG_CHECK_EQUAL_EP(_clip_distance, 6.5f);
|
||||
}
|
||||
|
||||
void test_wind()
|
||||
{
|
||||
setWindProperty(87.0f, 0.7f);
|
||||
auto vec = _wind_vec;
|
||||
SG_CHECK_EQUAL_EP2(vec[0], 0.011166f, 0.00001f);
|
||||
SG_CHECK_EQUAL_EP2(vec[1], -0.213068f, 0.00001f);
|
||||
SG_CHECK_EQUAL_EP2(vec[2], 0.0f, 0.00001f);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
auto fixture = std::unique_ptr<SGPrecipitationTestFixture>(new SGPrecipitationTestFixture);
|
||||
|
||||
fixture->test_configuration();
|
||||
fixture->test_rain();
|
||||
fixture->test_freeze();
|
||||
fixture->test_snow();
|
||||
fixture->test_unfreeze();
|
||||
fixture->test_no_precipitation();
|
||||
|
||||
fixture->test_snow_external();
|
||||
fixture->test_rain_external();
|
||||
fixture->test_illumination();
|
||||
fixture->test_clipping();
|
||||
fixture->test_wind();
|
||||
fixture->test_no_precipitation();
|
||||
|
||||
cout << "all tests passed OK" << endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -19,6 +19,8 @@
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include "HLAArrayDataType.hxx"
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include "HLADataType.hxx"
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include "HLAEnumeratedDataType.hxx"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "HLAEnumeratedDataType.hxx"
|
||||
#include "HLADataTypeVisitor.hxx"
|
||||
|
||||
|
||||
@@ -19,10 +19,11 @@
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include "HLAFixedRecordDataType.hxx"
|
||||
|
||||
#include "HLADataTypeVisitor.hxx"
|
||||
#include "HLAFixedRecordDataElement.hxx"
|
||||
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include "HLAInteractionClass.hxx"
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
#include "HLAInteractionClass.hxx"
|
||||
#include "HLADataElement.hxx"
|
||||
#include "HLAFederate.hxx"
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include "HLAObjectClass.hxx"
|
||||
|
||||
@@ -19,10 +19,11 @@
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include "HLAVariantRecordDataType.hxx"
|
||||
|
||||
#include "HLADataTypeVisitor.hxx"
|
||||
#include "HLAVariantRecordDataElement.hxx"
|
||||
|
||||
|
||||
@@ -22,9 +22,13 @@
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "DNSClient.hxx"
|
||||
#include <udns.h>
|
||||
#include <time.h>
|
||||
#include <ctime>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
namespace simgear {
|
||||
|
||||
@@ -142,7 +142,7 @@ public:
|
||||
void finishedRequest(const RepoRequestPtr& req);
|
||||
|
||||
HTTPDirectory* getOrCreateDirectory(const std::string& path);
|
||||
bool deleteDirectory(const std::string& path);
|
||||
bool deleteDirectory(const std::string& relPath, const SGPath& absPath);
|
||||
|
||||
typedef std::vector<HTTPDirectory*> DirectoryVector;
|
||||
DirectoryVector directories;
|
||||
@@ -317,7 +317,8 @@ public:
|
||||
ChildInfoList::iterator c = findIndexChild(it->file());
|
||||
if (c == children.end()) {
|
||||
SG_LOG(SG_TERRASYNC, SG_DEBUG, "is orphan '" << it->file() << "'" );
|
||||
orphans.push_back(it->file());
|
||||
|
||||
orphans.push_back(it->file());
|
||||
} else if (c->hash != hash) {
|
||||
SG_LOG(SG_TERRASYNC, SG_DEBUG, "hash mismatch'" << it->file() );
|
||||
// file exists, but hash mismatch, schedule update
|
||||
@@ -534,7 +535,7 @@ private:
|
||||
|
||||
std::string fpath = _relativePath + "/" + name;
|
||||
if (p.isDir()) {
|
||||
ok = _repository->deleteDirectory(fpath);
|
||||
ok = _repository->deleteDirectory(fpath, p);
|
||||
} else {
|
||||
// remove the hash cache entry
|
||||
_repository->updatedFileContents(p, std::string());
|
||||
@@ -1044,25 +1045,26 @@ HTTPRepository::failure() const
|
||||
return d;
|
||||
}
|
||||
|
||||
bool HTTPRepoPrivate::deleteDirectory(const std::string& path)
|
||||
bool HTTPRepoPrivate::deleteDirectory(const std::string& relPath, const SGPath& absPath)
|
||||
{
|
||||
DirectoryWithPath p(path);
|
||||
DirectoryVector::iterator it = std::find_if(directories.begin(), directories.end(), p);
|
||||
DirectoryWithPath p(relPath);
|
||||
auto it = std::find_if(directories.begin(), directories.end(), p);
|
||||
if (it != directories.end()) {
|
||||
HTTPDirectory* d = *it;
|
||||
assert(d->absolutePath() == absPath);
|
||||
directories.erase(it);
|
||||
Dir dir(d->absolutePath());
|
||||
bool result = dir.remove(true);
|
||||
|
||||
// update the hash cache too
|
||||
updatedFileContents(d->absolutePath(), std::string());
|
||||
|
||||
delete d;
|
||||
} else {
|
||||
// we encounter this code path when deleting an orphaned directory
|
||||
}
|
||||
|
||||
Dir dir(absPath);
|
||||
bool result = dir.remove(true);
|
||||
|
||||
return result;
|
||||
}
|
||||
// update the hash cache too
|
||||
updatedFileContents(absPath, std::string());
|
||||
|
||||
return false;
|
||||
return result;
|
||||
}
|
||||
|
||||
void HTTPRepoPrivate::makeRequest(RepoRequestPtr req)
|
||||
|
||||
@@ -38,20 +38,17 @@
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/io/iostreams/zlibstream.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/sg_inlines.h>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
using std::string;
|
||||
using traits = std::char_traits<char>;
|
||||
using simgear::enumValue;
|
||||
|
||||
// Cast an enum value to its underlying type
|
||||
template <typename T>
|
||||
static constexpr typename std::underlying_type<T>::type enumValue(T e) {
|
||||
return static_cast<typename std::underlying_type<T>::type>(e);
|
||||
}
|
||||
using traits = std::char_traits<char>;
|
||||
|
||||
// Private utility function
|
||||
static string zlibErrorMessage(const z_stream& zstream, int errorCode)
|
||||
|
||||
@@ -32,10 +32,12 @@
|
||||
#include <simgear_config.h>
|
||||
#include "sg_netChannel.hxx"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
@@ -258,18 +260,10 @@ NetChannelPoller::removeChannel(NetChannel* channel)
|
||||
assert(channel);
|
||||
assert(channel->poller == this);
|
||||
channel->poller = NULL;
|
||||
|
||||
// portability: MSVC throws assertion failure when empty
|
||||
if (channels.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ChannelList::iterator it = channels.begin();
|
||||
for (; it != channels.end(); ++it) {
|
||||
if (*it == channel) {
|
||||
channels.erase(it);
|
||||
return;
|
||||
}
|
||||
auto it = std::find(channels.begin(), channels.end(), channel);
|
||||
if (it != channels.end()) {
|
||||
channels.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -183,15 +183,6 @@ public:
|
||||
int requestContentLength;
|
||||
};
|
||||
|
||||
class EraseIfClosed
|
||||
{
|
||||
public:
|
||||
bool operator()(simgear::NetChannel* chan) const
|
||||
{
|
||||
return chan->isClosed();
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class TestServer : public NetChannel
|
||||
{
|
||||
@@ -202,7 +193,6 @@ public:
|
||||
{
|
||||
Socket::initSockets();
|
||||
|
||||
|
||||
open();
|
||||
bind(NULL, 2000); // localhost, any port
|
||||
listen(16);
|
||||
@@ -212,6 +202,7 @@ public:
|
||||
|
||||
virtual ~TestServer()
|
||||
{
|
||||
_poller.removeChannel(this);
|
||||
}
|
||||
|
||||
virtual bool writable (void) { return false ; }
|
||||
@@ -231,15 +222,16 @@ public:
|
||||
{
|
||||
_poller.poll();
|
||||
|
||||
typename std::vector<T*>::iterator it;
|
||||
it = std::remove_if(_channels.begin(), _channels.end(), EraseIfClosed());
|
||||
|
||||
for (typename std::vector<T*>::iterator it2 = it; it2 != _channels.end(); ++it2) {
|
||||
delete *it2;
|
||||
}
|
||||
auto it = std::remove_if(_channels.begin(), _channels.end(), [&](T* channel) {
|
||||
if (channel->isClosed()) {
|
||||
_poller.removeChannel(channel);
|
||||
delete channel;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
_channels.erase(it, _channels.end());
|
||||
|
||||
}
|
||||
|
||||
int connectCount()
|
||||
|
||||
@@ -33,6 +33,7 @@ void testTarGz()
|
||||
|
||||
SG_VERIFY(TarExtractor::isTarData(buf, bufSize));
|
||||
|
||||
f.close();
|
||||
}
|
||||
|
||||
void testPlainTar()
|
||||
@@ -48,12 +49,13 @@ void testPlainTar()
|
||||
|
||||
SG_VERIFY(TarExtractor::isTarData(buf, bufSize));
|
||||
|
||||
f.close();
|
||||
}
|
||||
|
||||
int main (int ac, char ** av)
|
||||
int main(int ac, char ** av)
|
||||
{
|
||||
testTarGz();
|
||||
testPlainTar();
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#include <simgear/sg_inlines.h>
|
||||
#include <simgear/io/sg_file.hxx>
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
|
||||
@@ -92,7 +93,8 @@ public:
|
||||
END_OF_ARCHIVE,
|
||||
ERROR_STATE, ///< states above this are error conditions
|
||||
BAD_ARCHIVE,
|
||||
BAD_DATA
|
||||
BAD_DATA,
|
||||
FILTER_STOPPED
|
||||
} State;
|
||||
|
||||
SGPath path;
|
||||
@@ -110,10 +112,13 @@ public:
|
||||
bool haveInitedZLib;
|
||||
bool uncompressedData; // set if reading a plain .tar (not tar.gz)
|
||||
uint8_t* headerPtr;
|
||||
|
||||
TarExtractorPrivate() :
|
||||
TarExtractor* outer;
|
||||
bool skipCurrentEntry = false;
|
||||
|
||||
TarExtractorPrivate(TarExtractor* o) :
|
||||
haveInitedZLib(false),
|
||||
uncompressedData(false)
|
||||
uncompressedData(false),
|
||||
outer(o)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -129,7 +134,10 @@ public:
|
||||
}
|
||||
|
||||
if (state == READING_FILE) {
|
||||
currentFile->close();
|
||||
if (currentFile) {
|
||||
currentFile->close();
|
||||
currentFile.reset();
|
||||
}
|
||||
size_t pad = currentFileSize % TAR_HEADER_BLOCK_SIZE;
|
||||
if (pad) {
|
||||
bytesRemaining = TAR_HEADER_BLOCK_SIZE - pad;
|
||||
@@ -177,26 +185,36 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
skipCurrentEntry = false;
|
||||
std::string tarPath = std::string(header.prefix) + std::string(header.fileName);
|
||||
|
||||
if (!isSafePath(tarPath)) {
|
||||
//state = BAD_ARCHIVE;
|
||||
SG_LOG(SG_IO, SG_WARN, "bad tar path:" << tarPath);
|
||||
//return;
|
||||
skipCurrentEntry = true;
|
||||
}
|
||||
|
||||
SGPath p = path;
|
||||
p.append(tarPath);
|
||||
|
||||
auto result = outer->filterPath(tarPath);
|
||||
if (result == TarExtractor::Stop) {
|
||||
setState(FILTER_STOPPED);
|
||||
return;
|
||||
} else if (result == TarExtractor::Skipped) {
|
||||
skipCurrentEntry = true;
|
||||
}
|
||||
|
||||
SGPath p = path / tarPath;
|
||||
if (header.typeflag == DIRTYPE) {
|
||||
Dir dir(p);
|
||||
dir.create(0755);
|
||||
if (!skipCurrentEntry) {
|
||||
Dir dir(p);
|
||||
dir.create(0755);
|
||||
}
|
||||
setState(READING_HEADER);
|
||||
} else if ((header.typeflag == REGTYPE) || (header.typeflag == AREGTYPE)) {
|
||||
currentFileSize = ::strtol(header.size, NULL, 8);
|
||||
bytesRemaining = currentFileSize;
|
||||
currentFile.reset(new SGBinaryFile(p));
|
||||
currentFile->open(SG_IO_OUT);
|
||||
if (!skipCurrentEntry) {
|
||||
currentFile.reset(new SGBinaryFile(p));
|
||||
currentFile->open(SG_IO_OUT);
|
||||
}
|
||||
setState(READING_FILE);
|
||||
} else {
|
||||
SG_LOG(SG_IO, SG_WARN, "Unsupported tar file type:" << header.typeflag);
|
||||
@@ -212,7 +230,9 @@ public:
|
||||
|
||||
size_t curBytes = std::min(bytesRemaining, count);
|
||||
if (state == READING_FILE) {
|
||||
currentFile->write(bytes, curBytes);
|
||||
if (currentFile) {
|
||||
currentFile->write(bytes, curBytes);
|
||||
}
|
||||
bytesRemaining -= curBytes;
|
||||
} else if ((state == READING_HEADER) || (state == PRE_END_OF_ARCHVE) || (state == END_OF_ARCHIVE)) {
|
||||
memcpy(headerPtr, bytes, curBytes);
|
||||
@@ -264,7 +284,7 @@ public:
|
||||
};
|
||||
|
||||
TarExtractor::TarExtractor(const SGPath& rootPath) :
|
||||
d(new TarExtractorPrivate)
|
||||
d(new TarExtractorPrivate(this))
|
||||
{
|
||||
|
||||
d->path = rootPath;
|
||||
@@ -383,22 +403,26 @@ bool TarExtractor::isTarData(const uint8_t* bytes, size_t count)
|
||||
z.avail_in = count;
|
||||
|
||||
if (inflateInit2(&z, ZLIB_INFLATE_WINDOW_BITS | ZLIB_DECODE_GZIP_HEADER) != Z_OK) {
|
||||
inflateEnd(&z);
|
||||
return false;
|
||||
}
|
||||
|
||||
int result = inflate(&z, Z_SYNC_FLUSH);
|
||||
if (result != Z_OK) {
|
||||
SG_LOG(SG_IO, SG_WARN, "inflate failed:" << result);
|
||||
inflateEnd(&z);
|
||||
return false; // not tar data
|
||||
}
|
||||
|
||||
size_t written = 4096 - z.avail_out;
|
||||
if (written < TAR_HEADER_BLOCK_SIZE) {
|
||||
SG_LOG(SG_IO, SG_WARN, "insufficient data for header");
|
||||
inflateEnd(&z);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
header = reinterpret_cast<UstarHeaderBlock*>(zlibOutput);
|
||||
inflateEnd(&z);
|
||||
} else {
|
||||
// uncompressed tar
|
||||
if (count < TAR_HEADER_BLOCK_SIZE) {
|
||||
@@ -417,4 +441,11 @@ bool TarExtractor::isTarData(const uint8_t* bytes, size_t count)
|
||||
return true;
|
||||
}
|
||||
|
||||
auto TarExtractor::filterPath(std::string& pathToExtract)
|
||||
-> PathResult
|
||||
{
|
||||
SG_UNUSED(pathToExtract);
|
||||
return Accepted;
|
||||
}
|
||||
|
||||
} // of simgear
|
||||
|
||||
@@ -43,7 +43,17 @@ public:
|
||||
|
||||
bool hasError() const;
|
||||
|
||||
protected:
|
||||
enum PathResult {
|
||||
Accepted,
|
||||
Skipped,
|
||||
Modified,
|
||||
Stop
|
||||
};
|
||||
|
||||
virtual PathResult filterPath(std::string& pathToExtract);
|
||||
private:
|
||||
friend class TarExtractorPrivate;
|
||||
std::unique_ptr<TarExtractorPrivate> d;
|
||||
};
|
||||
|
||||
|
||||
@@ -155,6 +155,12 @@ public:
|
||||
{
|
||||
return std::isnan(v);
|
||||
}
|
||||
|
||||
static bool eq(const T& a, const T& b, const T& epsilon = SGLimits<T>::epsilon())
|
||||
{ return std::abs(a - b) < epsilon; }
|
||||
|
||||
static bool neq(const T& a, const T& b, const T& epsilon = SGLimits<T>::epsilon())
|
||||
{ return !eq(a, b, epsilon); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
@@ -286,6 +290,49 @@ void test_permissions()
|
||||
SG_CHECK_EQUAL(fileInRW.canWrite(), false);
|
||||
}
|
||||
|
||||
void test_comparisons()
|
||||
{
|
||||
std::cout << "Testing comparisons\n";
|
||||
|
||||
SG_CHECK_EQUAL(SGPath("/abc/def ghi"), SGPath("/abc/def ghi"));
|
||||
SG_CHECK_NE(SGPath("/abc"), SGPath("abc"));
|
||||
SG_CHECK_LT(SGPath(""), SGPath("/"));
|
||||
SG_CHECK_LT(SGPath("A"), SGPath("a"));
|
||||
SG_CHECK_LE(SGPath(""), SGPath("/"));
|
||||
SG_CHECK_LE(SGPath("/"), SGPath("/"));
|
||||
SG_CHECK_GT(SGPath("a"), SGPath("A"));
|
||||
SG_CHECK_GE(SGPath("a"), SGPath("A"));
|
||||
SG_CHECK_GE(SGPath("a"), SGPath("a"));
|
||||
|
||||
std::vector<SGPath> origVector({
|
||||
std::string("/zer/gh/tr aze"),
|
||||
std::string("/abc/def/ttt"),
|
||||
std::string("/abc/def/ddd"),
|
||||
std::string("/a"),
|
||||
std::string("")});
|
||||
std::vector<SGPath> sortedVector({
|
||||
std::string(""),
|
||||
std::string("/a"),
|
||||
std::string("/abc/def/ddd"),
|
||||
std::string("/abc/def/ttt"),
|
||||
std::string("/zer/gh/tr aze")});
|
||||
|
||||
std::sort(origVector.begin(), origVector.end());
|
||||
SG_CHECK_EQUAL_NOSTREAM(origVector, sortedVector);
|
||||
}
|
||||
|
||||
void test_hash_function()
|
||||
{
|
||||
std::cout << "Testing the std::hash<SGPath> specialization\n";
|
||||
|
||||
const SGPath nullPath{};
|
||||
const SGPath p{"/abc/def"};
|
||||
|
||||
SG_CHECK_EQUAL(std::hash<SGPath>{}(nullPath), std::hash<SGPath>{}(nullPath));
|
||||
SG_CHECK_EQUAL(std::hash<SGPath>{}(p), std::hash<SGPath>{}(p));
|
||||
SG_CHECK_NE(std::hash<SGPath>{}(p), std::hash<SGPath>{}(p / "foobar"));
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
SGPath pa;
|
||||
@@ -389,12 +436,11 @@ int main(int argc, char* argv[])
|
||||
SG_CHECK_EQUAL(pp.canWrite(), false);
|
||||
|
||||
test_dir();
|
||||
|
||||
test_path_dir();
|
||||
|
||||
test_path_dir();
|
||||
test_permissions();
|
||||
|
||||
test_update_dir();
|
||||
test_update_dir();
|
||||
test_comparisons();
|
||||
test_hash_function();
|
||||
|
||||
cout << "all tests passed OK" << endl;
|
||||
return 0; // passed
|
||||
|
||||
@@ -296,7 +296,10 @@ bool Dir::isEmpty() const
|
||||
|
||||
int n = 0;
|
||||
dirent* d;
|
||||
while( (d = readdir(dp)) !=NULL && (n < 4) ) n++;
|
||||
while (n < 3 && (d = readdir(dp)) != nullptr) {
|
||||
n++;
|
||||
}
|
||||
|
||||
closedir(dp);
|
||||
|
||||
return (n == 2); // '.' and '..' always exist
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
#include "sg_dir.hxx"
|
||||
@@ -34,11 +35,35 @@ void test_tempDir()
|
||||
d.remove();
|
||||
}
|
||||
|
||||
void test_isEmpty()
|
||||
{
|
||||
simgear::Dir d = simgear::Dir::tempDir("FlightGear");
|
||||
SG_VERIFY(!d.isNull() && d.exists() && d.isEmpty());
|
||||
SGPath f = d.file("some file");
|
||||
|
||||
{ sg_ofstream file(f); } // create and close the file
|
||||
SG_VERIFY(!d.isEmpty());
|
||||
|
||||
f.remove();
|
||||
SG_VERIFY(d.isEmpty());
|
||||
|
||||
simgear::Dir subDir{d.file("some subdir")};
|
||||
subDir.create(0777);
|
||||
SG_VERIFY(!d.isEmpty());
|
||||
|
||||
subDir.remove();
|
||||
SG_VERIFY(d.isEmpty());
|
||||
|
||||
d.remove();
|
||||
SG_VERIFY(d.isEmpty()); // eek, but that's how it is
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
test_isNull();
|
||||
test_setRemoveOnDestroy();
|
||||
test_tempDir();
|
||||
test_isEmpty();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -35,9 +35,16 @@
|
||||
#include <fstream>
|
||||
#include <cstdlib>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <direct.h>
|
||||
#if !defined(SG_WINDOWS)
|
||||
# include <sys/types.h>
|
||||
# include <utime.h>
|
||||
#endif
|
||||
|
||||
#if defined(SG_WINDOWS)
|
||||
# include <direct.h>
|
||||
# include <sys/utime.h>
|
||||
#endif
|
||||
|
||||
#include "sg_path.hxx"
|
||||
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
@@ -52,13 +59,13 @@ using simgear::strutils::starts_with;
|
||||
static const char sgDirPathSep = '/';
|
||||
static const char sgDirPathSepBad = '\\';
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(SG_WINDOWS)
|
||||
const char SGPath::pathListSep[] = ";"; // this is null-terminated
|
||||
#else
|
||||
const char SGPath::pathListSep[] = ":"; // ditto
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(SG_WINDOWS)
|
||||
#include <ShlObj.h> // for CSIDL
|
||||
// TODO: replace this include file with the official <versionhelpers.h> header
|
||||
// included in the Windows 8.1 SDK
|
||||
@@ -240,44 +247,6 @@ SGPath SGPath::fromUtf8(const std::string& bytes, PermissionChecker p)
|
||||
return SGPath(bytes, p);
|
||||
}
|
||||
|
||||
|
||||
SGPath::SGPath(const SGPath& p) :
|
||||
path(p.path),
|
||||
_permission_checker(p._permission_checker),
|
||||
_cached(p._cached),
|
||||
_rwCached(p._rwCached),
|
||||
_cacheEnabled(p._cacheEnabled),
|
||||
_canRead(p._canRead),
|
||||
_canWrite(p._canWrite),
|
||||
_exists(p._exists),
|
||||
_isDir(p._isDir),
|
||||
_isFile(p._isFile),
|
||||
_modTime(p._modTime),
|
||||
_size(p._size)
|
||||
{
|
||||
}
|
||||
|
||||
SGPath& SGPath::operator=(const SGPath& p)
|
||||
{
|
||||
path = p.path;
|
||||
_permission_checker = p._permission_checker,
|
||||
_cached = p._cached;
|
||||
_rwCached = p._rwCached;
|
||||
_cacheEnabled = p._cacheEnabled;
|
||||
_canRead = p._canRead;
|
||||
_canWrite = p._canWrite;
|
||||
_exists = p._exists;
|
||||
_isDir = p._isDir;
|
||||
_isFile = p._isFile;
|
||||
_modTime = p._modTime;
|
||||
_size = p._size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// destructor
|
||||
SGPath::~SGPath() {
|
||||
}
|
||||
|
||||
// set path
|
||||
void SGPath::set( const string& p ) {
|
||||
path = p;
|
||||
@@ -780,6 +749,18 @@ bool SGPath::operator!=(const SGPath& other) const
|
||||
return (path != other.path);
|
||||
}
|
||||
|
||||
bool operator<(const SGPath& lhs, const SGPath& rhs)
|
||||
{ return lhs.path < rhs.path; }
|
||||
|
||||
bool operator>(const SGPath& lhs, const SGPath& rhs)
|
||||
{ return operator<(rhs, lhs); }
|
||||
|
||||
bool operator<=(const SGPath& lhs, const SGPath& rhs)
|
||||
{ return !operator>(lhs, rhs); }
|
||||
|
||||
bool operator>=(const SGPath& lhs, const SGPath& rhs)
|
||||
{ return !operator<(lhs, rhs); }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool SGPath::rename(const SGPath& newName)
|
||||
{
|
||||
@@ -1074,3 +1055,40 @@ std::string SGPath::fileUrl() const
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool SGPath::touch()
|
||||
{
|
||||
if (!permissionsAllowsWrite())
|
||||
{
|
||||
SG_LOG(SG_IO, SG_WARN, "file touch failed: (" << *this << ")"
|
||||
" reason: access denied" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!exists()) {
|
||||
SG_LOG(SG_IO, SG_WARN, "file touch failed: (" << *this << ")"
|
||||
" reason: missing file");
|
||||
return false;
|
||||
}
|
||||
#if defined(SG_WINDOWS)
|
||||
auto ws = wstr();
|
||||
// set this link for docs on behaviour here, about passing nullptr
|
||||
// https://msdn.microsoft.com/en-us/library/aa273399(v=vs.60).aspx
|
||||
if (_wutime(ws.c_str(), nullptr) != 0) {
|
||||
SG_LOG(SG_IO, SG_WARN, "file touch failed: (" << *this << ")"
|
||||
" reason: _wutime failed with error:" << simgear::strutils::error_string(errno));
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (::utime(path.c_str(), nullptr) != 0) {
|
||||
SG_LOG(SG_IO, SG_WARN, "file touch failed: (" << *this << ")"
|
||||
" reason: utime failed with error:" << simgear::strutils::error_string(errno));
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// reset the cache flag so we re-stat() on next request
|
||||
_cached = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -28,12 +28,14 @@
|
||||
#ifndef _SG_PATH_HXX
|
||||
#define _SG_PATH_HXX
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
|
||||
#include <simgear/math/sg_types.hxx>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
@@ -65,11 +67,6 @@ public:
|
||||
/** Default constructor */
|
||||
explicit SGPath(PermissionChecker validator = NULL);
|
||||
|
||||
/** Copy contructor */
|
||||
SGPath(const SGPath& p);
|
||||
|
||||
SGPath& operator=(const SGPath& p);
|
||||
|
||||
/**
|
||||
* Construct a path based on the starting path provided.
|
||||
* @param p initial path
|
||||
@@ -87,9 +84,6 @@ public:
|
||||
const std::string& r,
|
||||
PermissionChecker validator = NULL );
|
||||
|
||||
/** Destructor */
|
||||
~SGPath();
|
||||
|
||||
/**
|
||||
* Set path to a new value
|
||||
* @param p new path
|
||||
@@ -99,6 +93,8 @@ public:
|
||||
|
||||
bool operator==(const SGPath& other) const;
|
||||
bool operator!=(const SGPath& other) const;
|
||||
// Other comparison operators are declared below
|
||||
friend bool operator<(const SGPath& lhs, const SGPath& rhs);
|
||||
|
||||
void setPermissionChecker(PermissionChecker validator);
|
||||
PermissionChecker getPermissionChecker() const;
|
||||
@@ -190,8 +186,8 @@ public:
|
||||
* Get the path string
|
||||
* @return path string
|
||||
*/
|
||||
std::string str() const { return path; }
|
||||
std::string utf8Str() const { return path; }
|
||||
std::string str() const noexcept { return path; }
|
||||
std::string utf8Str() const noexcept { return path; }
|
||||
|
||||
std::string local8BitStr() const;
|
||||
|
||||
@@ -286,6 +282,13 @@ public:
|
||||
*/
|
||||
std::string fileUrl() const;
|
||||
|
||||
/**
|
||||
* Update the file modification timestamp to be 'now'. The contents will
|
||||
* not be changed. (Same as POSIX 'touch' command). Will fail if the file
|
||||
* does not exist or permissions do not allow writing.
|
||||
*/
|
||||
bool touch();
|
||||
|
||||
enum StandardLocation
|
||||
{
|
||||
HOME,
|
||||
@@ -357,6 +360,24 @@ private:
|
||||
mutable size_t _size;
|
||||
};
|
||||
|
||||
// Other comparison operators are in the class definition block
|
||||
bool operator> (const SGPath& lhs, const SGPath& rhs);
|
||||
bool operator<=(const SGPath& lhs, const SGPath& rhs);
|
||||
bool operator>=(const SGPath& lhs, const SGPath& rhs);
|
||||
|
||||
// Hash function for SGPath
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct hash<SGPath>
|
||||
{
|
||||
std::size_t operator()(const SGPath& path) const noexcept
|
||||
{
|
||||
return std::hash<std::string>{}(path.utf8Str());
|
||||
}
|
||||
};
|
||||
} // of namespace std
|
||||
|
||||
/// Output to an ostream
|
||||
template<typename char_type, typename traits_type>
|
||||
inline
|
||||
|
||||
@@ -29,10 +29,7 @@
|
||||
#include <cstring> // strerror_r() and strerror_s()
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
|
||||
#if defined(HAVE_CPP11_CODECVT)
|
||||
#include <codecvt> // new in C++11
|
||||
#endif
|
||||
#include <cassert>
|
||||
|
||||
#include "strutils.hxx"
|
||||
|
||||
@@ -44,6 +41,8 @@
|
||||
|
||||
#if defined(SG_WINDOWS)
|
||||
#include <windows.h>
|
||||
#include <codecvt>
|
||||
#include <locale>
|
||||
#endif
|
||||
|
||||
using std::string;
|
||||
@@ -538,6 +537,33 @@ namespace simgear {
|
||||
unsigned long long readNonNegativeInt<unsigned long long, 16>(
|
||||
const std::string& s);
|
||||
#endif
|
||||
|
||||
// parse a time string ([+/-]%f[:%f[:%f]]) into hours
|
||||
double readTime(const string& time_in)
|
||||
{
|
||||
if (time_in.empty()) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
const bool negativeSign = time_in.front() == '-';
|
||||
const string_list pieces = split(time_in, ":");
|
||||
if (pieces.size() > 3) {
|
||||
throw sg_format_exception("Unable to parse time string, too many pieces", time_in);
|
||||
}
|
||||
|
||||
const int hours = std::abs(to_int(pieces.front()));
|
||||
int minutes = 0, seconds = 0;
|
||||
if (pieces.size() > 1) {
|
||||
minutes = to_int(pieces.at(1));
|
||||
if (pieces.size() > 2) {
|
||||
seconds = to_int(pieces.at(2));
|
||||
}
|
||||
}
|
||||
|
||||
double result = hours + (minutes / 60.0) + (seconds / 3600.0);
|
||||
return negativeSign ? -result : result;
|
||||
}
|
||||
|
||||
|
||||
int compare_versions(const string& v1, const string& v2, int maxComponents)
|
||||
{
|
||||
@@ -629,25 +655,85 @@ static std::string convertWStringToMultiByte(DWORD encoding, const std::wstring&
|
||||
|
||||
std::wstring convertUtf8ToWString(const std::string& a)
|
||||
{
|
||||
#ifdef SG_WINDOWS
|
||||
return convertMultiByteToWString(CP_UTF8, a);
|
||||
#elif defined(HAVE_CPP11_CODECVT)
|
||||
#if defined(SG_WINDOWS)
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> ucs2conv;
|
||||
return ucs2conv.from_bytes(a);
|
||||
#else
|
||||
return std::wstring();
|
||||
assert(sizeof(wchar_t) == 4);
|
||||
std::wstring result;
|
||||
int expectedContinuationCount = 0;
|
||||
wchar_t wc = 0;
|
||||
|
||||
for (uint8_t utf8CodePoint : a) {
|
||||
// ASCII 7-bit range
|
||||
if (utf8CodePoint <= 0x7f) {
|
||||
if (expectedContinuationCount != 0) {
|
||||
throw sg_format_exception();
|
||||
}
|
||||
|
||||
result.push_back(static_cast<wchar_t>(utf8CodePoint));
|
||||
} else if (expectedContinuationCount > 0) {
|
||||
if ((utf8CodePoint & 0xC0) != 0x80) {
|
||||
throw sg_format_exception();
|
||||
}
|
||||
|
||||
wc = (wc << 6) | (utf8CodePoint & 0x3F);
|
||||
if (--expectedContinuationCount == 0) {
|
||||
result.push_back(wc);
|
||||
}
|
||||
} else {
|
||||
if ((utf8CodePoint & 0xE0) == 0xC0) {
|
||||
expectedContinuationCount = 1;
|
||||
wc = utf8CodePoint & 0x1f;
|
||||
} else if ((utf8CodePoint & 0xF0) == 0xE0) {
|
||||
expectedContinuationCount = 2;
|
||||
wc = utf8CodePoint & 0x0f;
|
||||
} else if ((utf8CodePoint & 0xF8) == 0xF0) {
|
||||
expectedContinuationCount = 3;
|
||||
wc =utf8CodePoint & 0x07;
|
||||
} else {
|
||||
// illegal UTF-8 encoding
|
||||
throw sg_format_exception();
|
||||
}
|
||||
}
|
||||
} // of UTF-8 code point iteration
|
||||
|
||||
return result;
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
std::string convertWStringToUtf8(const std::wstring& w)
|
||||
{
|
||||
#ifdef SG_WINDOWS
|
||||
return convertWStringToMultiByte(CP_UTF8, w);
|
||||
#elif defined(HAVE_CPP11_CODECVT)
|
||||
#if defined(SG_WINDOWS)
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> ucs2conv;
|
||||
return ucs2conv.to_bytes(w);
|
||||
#else
|
||||
return std::string();
|
||||
assert(sizeof(wchar_t) == 4);
|
||||
std::string result;
|
||||
|
||||
for (wchar_t cp : w) {
|
||||
if (cp <= 0x7f) {
|
||||
result.push_back(static_cast<uint8_t>(cp));
|
||||
} else if (cp <= 0x07ff) {
|
||||
result.push_back(0xC0 | ((cp >> 6) & 0x1f));
|
||||
result.push_back(0x80 | (cp & 0x3f));
|
||||
} else if (cp <= 0xffff) {
|
||||
result.push_back(0xE0 | ((cp >> 12) & 0x0f));
|
||||
result.push_back(0x80 | ((cp >> 6) & 0x3f));
|
||||
result.push_back(0x80 | (cp & 0x3f));
|
||||
} else if (cp < 0x10ffff) {
|
||||
result.push_back(0xF0 | ((cp >> 18) & 0x07));
|
||||
result.push_back(0x80 | ((cp >> 12) & 0x3f));
|
||||
result.push_back(0x80 | ((cp >> 6) & 0x3f));
|
||||
result.push_back(0x80 | (cp & 0x3f));
|
||||
} else {
|
||||
throw sg_format_exception();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -209,6 +209,16 @@ namespace simgear {
|
||||
typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
|
||||
T readNonNegativeInt(const std::string& s);
|
||||
|
||||
|
||||
/**
|
||||
* Read a time value, seperated by colons, as a value in hours.
|
||||
* Allowable input is ([+/-]%f[:%f[:%f]])
|
||||
* i.e 15:04:35 is parsed as 15 + (04 / 60) + (35 / 2600)
|
||||
* This code is moved from flightgear's options.cxx where it was called
|
||||
* parse_time(),
|
||||
*/
|
||||
double readTime(const std::string& s);
|
||||
|
||||
/**
|
||||
* Convert a string representing a boolean, to a bool.
|
||||
* Accepted values include YES, true, 0, 1, false, no, True,
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/constants.h>
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
@@ -581,6 +582,43 @@ void test_error_string()
|
||||
SG_CHECK_GT(strutils::error_string(saved_errno).size(), 0);
|
||||
}
|
||||
|
||||
void test_readTime()
|
||||
{
|
||||
SG_CHECK_EQUAL_EP(strutils::readTime(""), 0.0);
|
||||
|
||||
SG_CHECK_EQUAL_EP(strutils::readTime("11"), 11.0);
|
||||
SG_CHECK_EQUAL_EP(strutils::readTime("+11"), 11.0);
|
||||
SG_CHECK_EQUAL_EP(strutils::readTime("-11"), -11.0);
|
||||
|
||||
SG_CHECK_EQUAL_EP(strutils::readTime("11:30"), 11.5);
|
||||
SG_CHECK_EQUAL_EP(strutils::readTime("+11:15"), 11.25);
|
||||
SG_CHECK_EQUAL_EP(strutils::readTime("-11:45"), -11.75);
|
||||
|
||||
const double seconds = 1 / 3600.0;
|
||||
SG_CHECK_EQUAL_EP(strutils::readTime("11:30:00"), 11.5);
|
||||
SG_CHECK_EQUAL_EP(strutils::readTime("+11:15:05"), 11.25 + 5 * seconds);
|
||||
SG_CHECK_EQUAL_EP(strutils::readTime("-11:45:15"), -(11.75 + 15 * seconds));
|
||||
|
||||
SG_CHECK_EQUAL_EP(strutils::readTime("0:0:0"), 0);
|
||||
|
||||
SG_CHECK_EQUAL_EP(strutils::readTime("0:0:28"), 28 * seconds);
|
||||
SG_CHECK_EQUAL_EP(strutils::readTime("-0:0:28"), -28 * seconds);
|
||||
}
|
||||
|
||||
void test_utf8Convert()
|
||||
{
|
||||
// F, smiley emoticon, Maths summation symbol, section sign
|
||||
std::wstring a(L"\u0046\U0001F600\u2211\u00A7");
|
||||
|
||||
|
||||
std::string utf8A = strutils::convertWStringToUtf8(a);
|
||||
SG_VERIFY(utf8A == std::string("F\xF0\x9F\x98\x80\xE2\x88\x91\xC2\xA7"));
|
||||
|
||||
|
||||
std::wstring aRoundTrip = strutils::convertUtf8ToWString(utf8A);
|
||||
SG_VERIFY(a == aRoundTrip);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
test_strip();
|
||||
@@ -599,6 +637,8 @@ int main(int argc, char* argv[])
|
||||
test_md5_hex();
|
||||
test_error_string();
|
||||
test_propPathMatch();
|
||||
|
||||
test_readTime();
|
||||
test_utf8Convert();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -787,6 +787,11 @@ void naGCRelease(int key)
|
||||
naHash_delete(globals->save_hash, naNum(key));
|
||||
}
|
||||
|
||||
int naNumSaved()
|
||||
{
|
||||
return naHash_size(globals->save_hash) + naVec_size(globals->save);
|
||||
}
|
||||
|
||||
void naClearSaved()
|
||||
{
|
||||
naContext c;
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
include (SimGearComponent)
|
||||
|
||||
set(HEADERS
|
||||
cppbind_fwd.hxx
|
||||
Ghost.hxx
|
||||
NasalCallContext.hxx
|
||||
NasalContext.hxx
|
||||
NasalHash.hxx
|
||||
NasalMe.hxx
|
||||
NasalMethodHolder.hxx
|
||||
NasalObject.hxx
|
||||
NasalObjectHolder.hxx
|
||||
NasalString.hxx
|
||||
@@ -13,11 +16,8 @@ set(HEADERS
|
||||
)
|
||||
|
||||
set(DETAIL_HEADERS
|
||||
detail/from_nasal_function_templates.hxx
|
||||
detail/from_nasal_helper.hxx
|
||||
detail/functor_templates.hxx
|
||||
detail/nasal_traits.hxx
|
||||
detail/NasalObject_callMethod_templates.hxx
|
||||
detail/to_nasal_helper.hxx
|
||||
)
|
||||
|
||||
@@ -52,4 +52,4 @@ add_boost_test(nasal_gc_test
|
||||
add_boost_test(nasal_num
|
||||
SOURCES test/nasal_num_test.cxx
|
||||
LIBRARIES ${TEST_LIBS}
|
||||
)
|
||||
)
|
||||
|
||||
@@ -24,17 +24,16 @@
|
||||
#include "NasalObjectHolder.hxx"
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/std/integer_sequence.hxx>
|
||||
#include <simgear/std/type_traits.hxx>
|
||||
#include <simgear/structure/SGWeakReferenced.hxx>
|
||||
#include <simgear/structure/SGWeakPtr.hxx>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/call_traits.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/lambda/lambda.hpp>
|
||||
#include <boost/mpl/has_xxx.hpp>
|
||||
#include <boost/preprocessor/iteration/iterate.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/utility/enable_if.hpp>
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -62,10 +61,7 @@ namespace osg
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline typename boost::enable_if<
|
||||
boost::is_pointer<T>,
|
||||
T
|
||||
>::type
|
||||
inline std::enable_if_t<std::is_pointer<T>::value, T>
|
||||
get_pointer(T ptr)
|
||||
{
|
||||
return ptr;
|
||||
@@ -101,8 +97,8 @@ namespace nasal
|
||||
class GhostMetadata
|
||||
{
|
||||
public:
|
||||
typedef void(*Deleter)(void*);
|
||||
typedef std::vector<std::pair<Deleter, void*> > DestroyList;
|
||||
using Deleter = void(*)(void*);
|
||||
using DestroyList = std::vector<std::pair<Deleter, void*>>;
|
||||
|
||||
static DestroyList _destroy_list;
|
||||
|
||||
@@ -154,19 +150,6 @@ namespace nasal
|
||||
};
|
||||
|
||||
BOOST_MPL_HAS_XXX_TRAIT_DEF(element_type)
|
||||
|
||||
template<class T>
|
||||
struct reduced_type
|
||||
{
|
||||
typedef typename boost::remove_cv<
|
||||
typename boost::remove_reference<T>::type
|
||||
>::type type;
|
||||
};
|
||||
|
||||
template<class T1, class T2>
|
||||
struct reduced_is_same:
|
||||
public boost::is_same<typename reduced_type<T1>::type, T2>
|
||||
{};
|
||||
}
|
||||
|
||||
/** @brief Destroy all ghost queued for deletion.
|
||||
@@ -178,8 +161,8 @@ namespace nasal
|
||||
*/
|
||||
void ghostProcessDestroyList();
|
||||
|
||||
typedef SGSharedPtr<internal::MethodHolder> MethodHolderPtr;
|
||||
typedef SGWeakPtr<internal::MethodHolder> MethodHolderWeakPtr;
|
||||
using MethodHolderPtr = SGSharedPtr<internal::MethodHolder>;
|
||||
using MethodHolderWeakPtr = SGWeakPtr<internal::MethodHolder>;
|
||||
|
||||
// Dummy template to create shorter and easy to understand compile errors if
|
||||
// trying to wrap the wrong type as a Ghost.
|
||||
@@ -187,9 +170,10 @@ namespace nasal
|
||||
class Ghost
|
||||
{
|
||||
public:
|
||||
BOOST_STATIC_ASSERT(("Ghost can only wrap shared pointer!"
|
||||
&& is_strong_ref<T>::value
|
||||
));
|
||||
static_assert(
|
||||
is_strong_ref<T>::value,
|
||||
"Ghost can only wrap shared pointer!"
|
||||
);
|
||||
|
||||
static Ghost& init(const std::string& name);
|
||||
static bool isInit();
|
||||
@@ -209,7 +193,7 @@ namespace nasal
|
||||
* int myMember();
|
||||
* void doSomethingElse(const nasal::CallContext& ctx);
|
||||
* }
|
||||
* typedef boost::shared_ptr<MyClass> MyClassPtr;
|
||||
* using MyClassPtr = boost::shared_ptr<MyClass>;
|
||||
*
|
||||
* std::string myOtherFreeMember(int num);
|
||||
*
|
||||
@@ -235,29 +219,25 @@ namespace nasal
|
||||
* @endcode
|
||||
*/
|
||||
template<class T>
|
||||
class Ghost<T, typename boost::enable_if<is_strong_ref<T> >::type>:
|
||||
class Ghost<T, std::enable_if_t<is_strong_ref<T>::value>>:
|
||||
public internal::GhostMetadata
|
||||
{
|
||||
// Shared pointer required for Ghost (no weak pointer!)
|
||||
BOOST_STATIC_ASSERT((is_strong_ref<T>::value));
|
||||
|
||||
public:
|
||||
typedef typename T::element_type raw_type;
|
||||
typedef typename shared_ptr_traits<T>::strong_ref strong_ref;
|
||||
typedef typename shared_ptr_traits<T>::weak_ref weak_ref;
|
||||
typedef naRef (raw_type::*member_func_t)(const CallContext&);
|
||||
typedef naRef (*free_func_t)(raw_type&, const CallContext&);
|
||||
typedef boost::function<naRef(raw_type&, naContext)> getter_t;
|
||||
typedef boost::function<void( raw_type&, naContext, naRef)> setter_t;
|
||||
typedef boost::function<naRef(raw_type&, const CallContext&)> method_t;
|
||||
typedef boost::function<bool( raw_type&,
|
||||
naContext,
|
||||
const std::string&,
|
||||
naRef& )> fallback_getter_t;
|
||||
typedef boost::function<bool( raw_type&,
|
||||
naContext,
|
||||
const std::string&,
|
||||
naRef )> fallback_setter_t;
|
||||
using raw_type = typename T::element_type;
|
||||
using strong_ref = typename shared_ptr_traits<T>::strong_ref;
|
||||
using weak_ref = typename shared_ptr_traits<T>::weak_ref;
|
||||
using member_func_t = naRef (raw_type::*)(const CallContext&);
|
||||
using free_func_t = naRef (*)(raw_type&, const CallContext&);
|
||||
using getter_t = boost::function<naRef(raw_type&, naContext)>;
|
||||
using setter_t = boost::function<void( raw_type&, naContext, naRef)>;
|
||||
using method_t = boost::function<naRef(raw_type&, const CallContext&)>;
|
||||
using fallback_getter_t =
|
||||
boost::function<bool(raw_type&, naContext, const std::string&, naRef&)>;
|
||||
using fallback_setter_t =
|
||||
boost::function<bool(raw_type&, naContext, const std::string&, naRef)>;
|
||||
|
||||
template<class Ret, class... Args>
|
||||
using method_variadic_t = boost::function<Ret (raw_type&, Args...)>;
|
||||
|
||||
class MethodHolder:
|
||||
public internal::MethodHolder
|
||||
@@ -269,8 +249,8 @@ namespace nasal
|
||||
|
||||
protected:
|
||||
|
||||
typedef SGSharedPtr<MethodHolder> SharedPtr;
|
||||
typedef SGWeakPtr<MethodHolder> WeakPtr;
|
||||
using SharedPtr = SGSharedPtr<MethodHolder>;
|
||||
using WeakPtr = SGWeakPtr<MethodHolder>;
|
||||
|
||||
method_t _method;
|
||||
|
||||
@@ -369,7 +349,7 @@ namespace nasal
|
||||
MethodHolderPtr func;
|
||||
};
|
||||
|
||||
typedef std::map<std::string, member_t> MemberMap;
|
||||
using MemberMap = std::map<std::string, member_t>;
|
||||
|
||||
/**
|
||||
* Register a new ghost type.
|
||||
@@ -406,19 +386,18 @@ namespace nasal
|
||||
* @endcode
|
||||
*/
|
||||
template<class BaseGhost>
|
||||
typename boost::enable_if
|
||||
<
|
||||
boost::is_base_of<GhostMetadata, BaseGhost>,
|
||||
Ghost
|
||||
>::type&
|
||||
std::enable_if_t<
|
||||
std::is_base_of<GhostMetadata, BaseGhost>::value,
|
||||
Ghost&
|
||||
>
|
||||
bases()
|
||||
{
|
||||
BOOST_STATIC_ASSERT
|
||||
((
|
||||
boost::is_base_of<typename BaseGhost::raw_type, raw_type>::value
|
||||
));
|
||||
static_assert(
|
||||
std::is_base_of<typename BaseGhost::raw_type, raw_type>::value,
|
||||
"Not a base class!"
|
||||
);
|
||||
|
||||
typedef typename BaseGhost::strong_ref base_ref;
|
||||
using base_ref = typename BaseGhost::strong_ref;
|
||||
|
||||
BaseGhost* base = BaseGhost::getSingletonPtr();
|
||||
base->addDerived(
|
||||
@@ -429,17 +408,14 @@ namespace nasal
|
||||
|
||||
// Replace any getter that is not available in the current class.
|
||||
// TODO check if this is the correct behavior of function overriding
|
||||
for( typename BaseGhost::MemberMap::const_iterator member =
|
||||
base->_members.begin();
|
||||
member != base->_members.end();
|
||||
++member )
|
||||
for(auto const& base_member: base->_members)
|
||||
{
|
||||
if( _members.find(member->first) == _members.end() )
|
||||
_members[member->first] = member_t
|
||||
if( _members.find(base_member.first) == _members.end() )
|
||||
_members[base_member.first] = member_t
|
||||
(
|
||||
member->second.getter,
|
||||
member->second.setter,
|
||||
member->second.func
|
||||
base_member.second.getter,
|
||||
base_member.second.setter,
|
||||
base_member.second.func
|
||||
);
|
||||
}
|
||||
|
||||
@@ -464,17 +440,16 @@ namespace nasal
|
||||
* @endcode
|
||||
*/
|
||||
template<class Base>
|
||||
typename boost::disable_if
|
||||
<
|
||||
boost::is_base_of<GhostMetadata, Base>,
|
||||
Ghost
|
||||
>::type&
|
||||
std::enable_if_t<
|
||||
!std::is_base_of<GhostMetadata, Base>::value,
|
||||
Ghost&
|
||||
>
|
||||
bases()
|
||||
{
|
||||
BOOST_STATIC_ASSERT
|
||||
((
|
||||
boost::is_base_of<typename Ghost<Base>::raw_type, raw_type>::value
|
||||
));
|
||||
static_assert(
|
||||
std::is_base_of<typename Ghost<Base>::raw_type, raw_type>::value,
|
||||
"Not a base class!"
|
||||
);
|
||||
|
||||
return bases< Ghost<Base> >();
|
||||
}
|
||||
@@ -770,7 +745,7 @@ namespace nasal
|
||||
|
||||
/**
|
||||
* Register anything that accepts an object instance and a
|
||||
* nasal::CallContext whith automatic conversion of the return type to
|
||||
* nasal::CallContext with automatic conversion of the return type to
|
||||
* Nasal.
|
||||
*
|
||||
* @code{cpp}
|
||||
@@ -791,14 +766,73 @@ namespace nasal
|
||||
return method(name, boost::bind(method_invoker<Ret>, func, _1, _2));
|
||||
}
|
||||
|
||||
// Build dependency for CMake, gcc, etc.
|
||||
#define SG_DONT_DO_ANYTHING
|
||||
# include <simgear/nasal/cppbind/detail/functor_templates.hxx>
|
||||
#undef SG_DONT_DO_ANYTHING
|
||||
/**
|
||||
* Bind any callable entity accepting an instance of raw_type and an
|
||||
* arbitrary number of arguments as method.
|
||||
*
|
||||
* The std::index_sequence specifies the order of the arguments
|
||||
*/
|
||||
template<class Ret, class... Args, std::size_t... Indices>
|
||||
Ghost& method( const std::string& name,
|
||||
const method_variadic_t<Ret, Args...>& func,
|
||||
std::index_sequence<Indices...> )
|
||||
{
|
||||
return method<Ret>
|
||||
(
|
||||
name,
|
||||
typename boost::function<Ret (raw_type&, const CallContext&)>
|
||||
( boost::bind(
|
||||
func,
|
||||
_1,
|
||||
boost::bind(&Ghost::arg_from_nasal<Args>, _2, Indices)...
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#define BOOST_PP_ITERATION_LIMITS (0, 9)
|
||||
#define BOOST_PP_FILENAME_1 <simgear/nasal/cppbind/detail/functor_templates.hxx>
|
||||
#include BOOST_PP_ITERATE()
|
||||
template<class Ret, class... Args>
|
||||
Ghost& method( const std::string& name,
|
||||
const method_variadic_t<Ret, Args...>& func )
|
||||
{
|
||||
return method(name, func, std::index_sequence_for<Args...>{});
|
||||
}
|
||||
|
||||
/**\
|
||||
* Bind a member function with an arbitrary number of arguments as method.
|
||||
*/
|
||||
template<class Ret, class... Args>
|
||||
Ghost& method( const std::string& name,
|
||||
Ret (raw_type::*fn)(Args...) )
|
||||
{
|
||||
return method(name, method_variadic_t<Ret, Args...>(fn));
|
||||
}
|
||||
|
||||
template<class Ret, class... Args>
|
||||
Ghost& method( const std::string& name,
|
||||
Ret (raw_type::*fn)(Args...) const )
|
||||
{
|
||||
return method(name, method_variadic_t<Ret, Args...>(fn));
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind free function accepting an instance of raw_type and an arbitrary
|
||||
* number of arguments as method.
|
||||
*/
|
||||
template<class Ret, class Type, class... Args>
|
||||
Ghost& method
|
||||
(
|
||||
const std::string& name,
|
||||
Ret (*fn)(Type, Args ... args)
|
||||
)
|
||||
{
|
||||
static_assert(
|
||||
std::is_convertible<raw_type&, Type>::value,
|
||||
//|| std::is_convertible<raw_type*, Type>::value
|
||||
// TODO check how to do it with pointer...
|
||||
"First parameter can not be converted from the Ghost raw_type!"
|
||||
);
|
||||
|
||||
return method(name, method_variadic_t<Ret, Args...>(fn));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a shared pointer on the heap to handle the reference counting
|
||||
@@ -806,11 +840,11 @@ namespace nasal
|
||||
*/
|
||||
template<class RefType>
|
||||
static
|
||||
typename boost::enable_if_c<
|
||||
boost::is_same<RefType, strong_ref>::value
|
||||
|| boost::is_same<RefType, weak_ref>::value,
|
||||
std::enable_if_t<
|
||||
std::is_same<RefType, strong_ref>::value
|
||||
|| std::is_same<RefType, weak_ref>::value,
|
||||
naRef
|
||||
>::type
|
||||
>
|
||||
makeGhost(naContext c, RefType const& ref_ptr)
|
||||
{
|
||||
strong_ref ref(ref_ptr);
|
||||
@@ -848,10 +882,9 @@ namespace nasal
|
||||
}
|
||||
|
||||
// otherwise try the derived classes
|
||||
for( typename DerivedList::reverse_iterator
|
||||
derived = getSingletonPtr()->_derived_types.rbegin();
|
||||
derived != getSingletonPtr()->_derived_types.rend();
|
||||
++derived )
|
||||
for( auto derived = getSingletonPtr()->_derived_types.rbegin();
|
||||
derived != getSingletonPtr()->_derived_types.rend();
|
||||
++derived )
|
||||
{
|
||||
strong_ref ref = (derived->from_nasal)(c, me);
|
||||
|
||||
@@ -865,13 +898,10 @@ namespace nasal
|
||||
if( !naIsVector(na_parents) )
|
||||
return strong_ref();
|
||||
|
||||
typedef std::vector<naRef> naRefs;
|
||||
naRefs parents = from_nasal<naRefs>(c, na_parents);
|
||||
for( naRefs::const_iterator parent = parents.begin();
|
||||
parent != parents.end();
|
||||
++parent )
|
||||
auto parents = from_nasal<std::vector<naRef>>(c, na_parents);
|
||||
for(auto parent: parents)
|
||||
{
|
||||
strong_ref ref = fromNasal(c, *parent);
|
||||
strong_ref ref = fromNasal(c, parent);
|
||||
if( get_pointer(ref) )
|
||||
return ref;
|
||||
}
|
||||
@@ -923,8 +953,9 @@ namespace nasal
|
||||
static naGhostType _ghost_type_strong, //!< Stored as shared pointer
|
||||
_ghost_type_weak; //!< Stored as weak shared pointer
|
||||
|
||||
typedef naRef (*to_nasal_t)(naContext, const strong_ref&, bool);
|
||||
typedef strong_ref (*from_nasal_t)(naContext, naRef);
|
||||
using to_nasal_t = naRef (*)(naContext, const strong_ref&, bool);
|
||||
using from_nasal_t = strong_ref (*)(naContext, naRef);
|
||||
|
||||
struct DerivedInfo
|
||||
{
|
||||
to_nasal_t to_nasal;
|
||||
@@ -937,7 +968,7 @@ namespace nasal
|
||||
{}
|
||||
};
|
||||
|
||||
typedef std::vector<DerivedInfo> DerivedList;
|
||||
using DerivedList = std::vector<DerivedInfo>;
|
||||
DerivedList _derived_types;
|
||||
|
||||
static bool isInstance(naGhostType* ghost_type, bool& is_weak)
|
||||
@@ -951,13 +982,10 @@ namespace nasal
|
||||
|
||||
template<class RefPtr, bool is_weak>
|
||||
static
|
||||
typename boost::enable_if_c<
|
||||
!is_weak,
|
||||
RefPtr
|
||||
>::type
|
||||
std::enable_if_t<!is_weak, RefPtr>
|
||||
getPtr(void* ptr)
|
||||
{
|
||||
typedef shared_ptr_storage<strong_ref> storage_type;
|
||||
using storage_type = shared_ptr_storage<strong_ref>;
|
||||
if( ptr )
|
||||
return storage_type::template get<RefPtr>(
|
||||
static_cast<typename storage_type::storage_type*>(ptr)
|
||||
@@ -968,13 +996,13 @@ namespace nasal
|
||||
|
||||
template<class RefPtr, bool is_weak>
|
||||
static
|
||||
typename boost::enable_if_c<
|
||||
std::enable_if_t<
|
||||
is_weak && supports_weak_ref<T>::value,
|
||||
RefPtr
|
||||
>::type
|
||||
>
|
||||
getPtr(void* ptr)
|
||||
{
|
||||
typedef shared_ptr_storage<weak_ref> storage_type;
|
||||
using storage_type = shared_ptr_storage<weak_ref>;
|
||||
if( ptr )
|
||||
return storage_type::template get<RefPtr>(
|
||||
static_cast<typename storage_type::storage_type*>(ptr)
|
||||
@@ -985,10 +1013,10 @@ namespace nasal
|
||||
|
||||
template<class RefPtr, bool is_weak>
|
||||
static
|
||||
typename boost::enable_if_c<
|
||||
std::enable_if_t<
|
||||
is_weak && !supports_weak_ref<T>::value,
|
||||
RefPtr
|
||||
>::type
|
||||
>
|
||||
getPtr(void* ptr)
|
||||
{
|
||||
return RefPtr();
|
||||
@@ -1004,10 +1032,10 @@ namespace nasal
|
||||
|
||||
template<class BaseGhost>
|
||||
static
|
||||
typename boost::enable_if
|
||||
< boost::is_polymorphic<typename BaseGhost::raw_type>,
|
||||
naRef
|
||||
>::type
|
||||
std::enable_if_t<
|
||||
std::is_polymorphic<typename BaseGhost::raw_type>::value,
|
||||
naRef
|
||||
>
|
||||
toNasal( naContext c,
|
||||
const typename BaseGhost::strong_ref& base_ref,
|
||||
bool strong )
|
||||
@@ -1016,10 +1044,10 @@ namespace nasal
|
||||
|
||||
// Check first if passed pointer can by converted to instance of class
|
||||
// this ghost wraps.
|
||||
if( !boost::is_same
|
||||
< typename BaseGhost::raw_type,
|
||||
typename Ghost::raw_type
|
||||
>::value
|
||||
if( !std::is_same<
|
||||
typename BaseGhost::raw_type,
|
||||
typename Ghost::raw_type
|
||||
>::value
|
||||
&& dynamic_cast<const typename Ghost::raw_type*>(ptr) != ptr )
|
||||
return naNil();
|
||||
|
||||
@@ -1038,10 +1066,9 @@ namespace nasal
|
||||
static_pointer_cast<typename Ghost::raw_type>(base_ref);
|
||||
|
||||
// Now check if we can further downcast to one of our derived classes.
|
||||
for( typename DerivedList::reverse_iterator
|
||||
derived = getSingletonPtr()->_derived_types.rbegin();
|
||||
derived != getSingletonPtr()->_derived_types.rend();
|
||||
++derived )
|
||||
for( auto derived = getSingletonPtr()->_derived_types.rbegin();
|
||||
derived != getSingletonPtr()->_derived_types.rend();
|
||||
++derived )
|
||||
{
|
||||
naRef ghost = (derived->to_nasal)(c, ref, strong);
|
||||
|
||||
@@ -1058,13 +1085,13 @@ namespace nasal
|
||||
|
||||
template<class BaseGhost>
|
||||
static
|
||||
typename boost::disable_if
|
||||
< boost::is_polymorphic<typename BaseGhost::raw_type>,
|
||||
naRef
|
||||
>::type
|
||||
toNasal( naContext c,
|
||||
const typename BaseGhost::strong_ref& ref,
|
||||
bool strong )
|
||||
std::enable_if_t<
|
||||
!std::is_polymorphic<typename BaseGhost::raw_type>::value,
|
||||
naRef
|
||||
>
|
||||
toNasal( naContext c,
|
||||
const typename BaseGhost::strong_ref& ref,
|
||||
bool strong )
|
||||
{
|
||||
// For non polymorphic classes there is no possibility to get the actual
|
||||
// dynamic type, therefore we can only use its static type.
|
||||
@@ -1087,7 +1114,7 @@ namespace nasal
|
||||
template<class Ret>
|
||||
getter_t to_getter(Ret (raw_type::*getter)() const)
|
||||
{
|
||||
typedef typename boost::call_traits<Ret>::param_type param_type;
|
||||
using param_type = typename boost::call_traits<Ret>::param_type;
|
||||
naRef(*to_nasal_)(naContext, param_type) = &to_nasal;
|
||||
|
||||
// Getter signature: naRef(raw_type&, naContext)
|
||||
@@ -1140,7 +1167,7 @@ namespace nasal
|
||||
*/
|
||||
template<class Ret>
|
||||
static
|
||||
typename boost::disable_if<boost::is_void<Ret>, naRef>::type
|
||||
std::enable_if_t<!std::is_void<Ret>::value, naRef>
|
||||
method_invoker
|
||||
(
|
||||
const boost::function<Ret (raw_type&, const CallContext&)>& func,
|
||||
@@ -1148,7 +1175,7 @@ namespace nasal
|
||||
const CallContext& ctx
|
||||
)
|
||||
{
|
||||
return (*to_nasal_ptr<Ret>::get())(ctx.c, func(obj, ctx));
|
||||
return (*to_nasal_ptr<Ret>::get())(ctx.c_ctx(), func(obj, ctx));
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1156,7 +1183,7 @@ namespace nasal
|
||||
*/
|
||||
template<class Ret>
|
||||
static
|
||||
typename boost::enable_if<boost::is_void<Ret>, naRef>::type
|
||||
std::enable_if_t<std::is_void<Ret>::value, naRef>
|
||||
method_invoker
|
||||
(
|
||||
const boost::function<Ret (raw_type&, const CallContext&)>& func,
|
||||
@@ -1174,10 +1201,10 @@ namespace nasal
|
||||
*/
|
||||
template<class Arg>
|
||||
static
|
||||
typename boost::disable_if<
|
||||
internal::reduced_is_same<Arg, CallContext>,
|
||||
std::enable_if_t<
|
||||
!std::is_same<std::remove_cvref_t<Arg>, CallContext>::value,
|
||||
typename from_nasal_ptr<Arg>::return_type
|
||||
>::type
|
||||
>
|
||||
arg_from_nasal(const CallContext& ctx, size_t index)
|
||||
{
|
||||
return ctx.requireArg<Arg>(index);
|
||||
@@ -1188,19 +1215,21 @@ namespace nasal
|
||||
*/
|
||||
template<class Arg>
|
||||
static
|
||||
typename boost::enable_if<
|
||||
internal::reduced_is_same<Arg, CallContext>,
|
||||
std::enable_if_t<
|
||||
std::is_same<std::remove_cvref_t<Arg>, CallContext>::value,
|
||||
typename from_nasal_ptr<Arg>::return_type
|
||||
>::type
|
||||
>
|
||||
arg_from_nasal(const CallContext& ctx, size_t)
|
||||
{
|
||||
// Either const CallContext& or CallContext, non-const reference
|
||||
// does not make sense.
|
||||
BOOST_STATIC_ASSERT( (!boost::is_same<Arg, CallContext&>::value) );
|
||||
static_assert(
|
||||
!boost::is_same<Arg, CallContext&>::value,
|
||||
"Only const reference and value make sense!");
|
||||
return ctx;
|
||||
};
|
||||
|
||||
typedef std::unique_ptr<Ghost> GhostPtr;
|
||||
using GhostPtr = std::unique_ptr<Ghost>;
|
||||
MemberMap _members;
|
||||
fallback_getter_t _fallback_getter;
|
||||
fallback_setter_t _fallback_setter;
|
||||
@@ -1240,13 +1269,10 @@ namespace nasal
|
||||
|
||||
template<bool is_weak>
|
||||
static
|
||||
typename boost::enable_if_c<
|
||||
!is_weak,
|
||||
naRef
|
||||
>::type
|
||||
std::enable_if_t<!is_weak, naRef>
|
||||
create(naContext c, const strong_ref& ref_ptr)
|
||||
{
|
||||
typedef shared_ptr_storage<strong_ref> storage_type;
|
||||
using storage_type = shared_ptr_storage<strong_ref>;
|
||||
return naNewGhost2( c,
|
||||
&Ghost::_ghost_type_strong,
|
||||
storage_type::ref(ref_ptr) );
|
||||
@@ -1254,13 +1280,13 @@ namespace nasal
|
||||
|
||||
template<bool is_weak>
|
||||
static
|
||||
typename boost::enable_if_c<
|
||||
std::enable_if_t<
|
||||
is_weak && supports_weak_ref<T>::value,
|
||||
naRef
|
||||
>::type
|
||||
>
|
||||
create(naContext c, const strong_ref& ref_ptr)
|
||||
{
|
||||
typedef shared_ptr_storage<weak_ref> storage_type;
|
||||
using storage_type = shared_ptr_storage<weak_ref>;
|
||||
return naNewGhost2( c,
|
||||
&Ghost::_ghost_type_weak,
|
||||
storage_type::ref(ref_ptr) );
|
||||
@@ -1268,10 +1294,10 @@ namespace nasal
|
||||
|
||||
template<bool is_weak>
|
||||
static
|
||||
typename boost::enable_if_c<
|
||||
std::enable_if_t<
|
||||
is_weak && !supports_weak_ref<T>::value,
|
||||
naRef
|
||||
>::type
|
||||
>
|
||||
create(naContext, const strong_ref&)
|
||||
{
|
||||
return naNil();
|
||||
@@ -1280,7 +1306,7 @@ namespace nasal
|
||||
template<class Type>
|
||||
static void destroy(void *ptr)
|
||||
{
|
||||
typedef shared_ptr_storage<Type> storage_type;
|
||||
using storage_type = shared_ptr_storage<Type>;
|
||||
storage_type::unref(
|
||||
static_cast<typename storage_type::storage_type*>(ptr)
|
||||
);
|
||||
@@ -1322,9 +1348,7 @@ namespace nasal
|
||||
// return "";
|
||||
// }
|
||||
|
||||
typename MemberMap::iterator member =
|
||||
getSingletonPtr()->_members.find(key_str);
|
||||
|
||||
auto member = getSingletonPtr()->_members.find(key_str);
|
||||
if( member == getSingletonPtr()->_members.end() )
|
||||
{
|
||||
fallback_getter_t fallback_get = getSingletonPtr()->_fallback_getter;
|
||||
@@ -1374,7 +1398,7 @@ namespace nasal
|
||||
const std::string key = nasal::from_nasal<std::string>(c, field);
|
||||
const MemberMap& members = getSingletonPtr()->_members;
|
||||
|
||||
typename MemberMap::const_iterator member = members.find(key);
|
||||
auto member = members.find(key);
|
||||
if( member == members.end() )
|
||||
{
|
||||
fallback_setter_t fallback_set = getSingletonPtr()->_fallback_setter;
|
||||
@@ -1415,13 +1439,11 @@ namespace nasal
|
||||
|
||||
template<class T>
|
||||
naGhostType
|
||||
Ghost<T, typename boost::enable_if<is_strong_ref<T> >::type>
|
||||
::_ghost_type_strong;
|
||||
Ghost<T, std::enable_if_t<is_strong_ref<T>::value>>::_ghost_type_strong;
|
||||
|
||||
template<class T>
|
||||
naGhostType
|
||||
Ghost<T, typename boost::enable_if<is_strong_ref<T> >::type>
|
||||
::_ghost_type_weak;
|
||||
Ghost<T, std::enable_if_t<is_strong_ref<T>::value>>::_ghost_type_weak;
|
||||
|
||||
} // namespace nasal
|
||||
|
||||
@@ -1430,15 +1452,13 @@ namespace nasal
|
||||
* Convert every shared pointer to a ghost.
|
||||
*/
|
||||
template<class T>
|
||||
typename boost::enable_if<
|
||||
nasal::internal::has_element_type<
|
||||
typename nasal::internal::reduced_type<T>::type
|
||||
>,
|
||||
std::enable_if_t<
|
||||
nasal::internal::has_element_type<std::remove_cvref_t<T>>::value,
|
||||
naRef
|
||||
>::type
|
||||
>
|
||||
to_nasal_helper(naContext c, T ptr)
|
||||
{
|
||||
typedef typename nasal::shared_ptr_traits<T>::strong_ref strong_ref;
|
||||
using strong_ref = typename nasal::shared_ptr_traits<T>::strong_ref;
|
||||
return nasal::Ghost<strong_ref>::makeGhost(c, ptr);
|
||||
}
|
||||
|
||||
@@ -1446,15 +1466,13 @@ to_nasal_helper(naContext c, T ptr)
|
||||
* Convert nasal ghosts/hashes to shared pointer (of a ghost).
|
||||
*/
|
||||
template<class T>
|
||||
typename boost::enable_if<
|
||||
nasal::internal::has_element_type<
|
||||
typename nasal::internal::reduced_type<T>::type
|
||||
>,
|
||||
std::enable_if_t<
|
||||
nasal::internal::has_element_type<std::remove_cvref_t<T>>::value,
|
||||
T
|
||||
>::type
|
||||
>
|
||||
from_nasal_helper(naContext c, naRef ref, const T*)
|
||||
{
|
||||
typedef typename nasal::shared_ptr_traits<T>::strong_ref strong_ref;
|
||||
using strong_ref = typename nasal::shared_ptr_traits<T>::strong_ref;
|
||||
return T(nasal::Ghost<strong_ref>::fromNasalChecked(c, ref));
|
||||
}
|
||||
|
||||
@@ -1462,11 +1480,11 @@ from_nasal_helper(naContext c, naRef ref, const T*)
|
||||
* Convert any pointer to a SGReferenced based object to a ghost.
|
||||
*/
|
||||
template<class T>
|
||||
typename boost::enable_if_c<
|
||||
boost::is_base_of<SGReferenced, T>::value
|
||||
|| boost::is_base_of<SGWeakReferenced, T>::value,
|
||||
std::enable_if_t<
|
||||
std::is_base_of<SGReferenced, T>::value
|
||||
|| std::is_base_of<SGWeakReferenced, T>::value,
|
||||
naRef
|
||||
>::type
|
||||
>
|
||||
to_nasal_helper(naContext c, T* ptr)
|
||||
{
|
||||
return nasal::Ghost<SGSharedPtr<T> >::makeGhost(c, SGSharedPtr<T>(ptr));
|
||||
@@ -1476,20 +1494,14 @@ to_nasal_helper(naContext c, T* ptr)
|
||||
* Convert nasal ghosts/hashes to pointer (of a SGReferenced based ghost).
|
||||
*/
|
||||
template<class T>
|
||||
typename boost::enable_if_c<
|
||||
boost::is_base_of<
|
||||
SGReferenced,
|
||||
typename boost::remove_pointer<T>::type
|
||||
>::value
|
||||
|| boost::is_base_of<
|
||||
SGWeakReferenced,
|
||||
typename boost::remove_pointer<T>::type
|
||||
>::value,
|
||||
std::enable_if_t<
|
||||
std::is_base_of<SGReferenced, std::remove_pointer_t<T>>::value
|
||||
|| std::is_base_of<SGWeakReferenced, std::remove_pointer_t<T>>::value,
|
||||
T
|
||||
>::type
|
||||
>
|
||||
from_nasal_helper(naContext c, naRef ref, const T*)
|
||||
{
|
||||
typedef SGSharedPtr<typename boost::remove_pointer<T>::type> TypeRef;
|
||||
using TypeRef = SGSharedPtr<std::remove_pointer_t<T>>;
|
||||
return T(nasal::Ghost<TypeRef>::fromNasalChecked(c, ref));
|
||||
}
|
||||
|
||||
@@ -1497,10 +1509,10 @@ from_nasal_helper(naContext c, naRef ref, const T*)
|
||||
* Convert any pointer to a osg::Referenced based object to a ghost.
|
||||
*/
|
||||
template<class T>
|
||||
typename boost::enable_if<
|
||||
boost::is_base_of<osg::Referenced, T>,
|
||||
std::enable_if_t<
|
||||
std::is_base_of<osg::Referenced, T>::value,
|
||||
naRef
|
||||
>::type
|
||||
>
|
||||
to_nasal_helper(naContext c, T* ptr)
|
||||
{
|
||||
return nasal::Ghost<osg::ref_ptr<T> >::makeGhost(c, osg::ref_ptr<T>(ptr));
|
||||
@@ -1510,13 +1522,13 @@ to_nasal_helper(naContext c, T* ptr)
|
||||
* Convert nasal ghosts/hashes to pointer (of a osg::Referenced based ghost).
|
||||
*/
|
||||
template<class T>
|
||||
typename boost::enable_if<
|
||||
boost::is_base_of<osg::Referenced, typename boost::remove_pointer<T>::type>,
|
||||
std::enable_if_t<
|
||||
std::is_base_of<osg::Referenced, std::remove_pointer_t<T>>::value,
|
||||
T
|
||||
>::type
|
||||
>
|
||||
from_nasal_helper(naContext c, naRef ref, const T*)
|
||||
{
|
||||
typedef osg::ref_ptr<typename boost::remove_pointer<T>::type> TypeRef;
|
||||
using TypeRef = osg::ref_ptr<std::remove_pointer_t<T>>;
|
||||
return T(nasal::Ghost<TypeRef>::fromNasalChecked(c, ref));
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,7 @@
|
||||
#ifndef SG_NASAL_CALL_CONTEXT_HXX_
|
||||
#define SG_NASAL_CALL_CONTEXT_HXX_
|
||||
|
||||
#include "from_nasal.hxx"
|
||||
#include "to_nasal.hxx"
|
||||
#include "NasalContext.hxx"
|
||||
|
||||
namespace nasal
|
||||
{
|
||||
@@ -29,11 +28,12 @@ namespace nasal
|
||||
/**
|
||||
* Context passed to a function/method being called from Nasal
|
||||
*/
|
||||
class CallContext
|
||||
class CallContext:
|
||||
public ContextWrapper
|
||||
{
|
||||
public:
|
||||
CallContext(naContext c, naRef me, size_t argc, naRef* args):
|
||||
c(c),
|
||||
ContextWrapper(c),
|
||||
me(me),
|
||||
argc(argc),
|
||||
args(args)
|
||||
@@ -102,28 +102,14 @@ namespace nasal
|
||||
requireArg(size_t index) const
|
||||
{
|
||||
if( index >= argc )
|
||||
naRuntimeError(c, "Missing required arg #%d", index);
|
||||
runtimeError("Missing required arg #%d", index);
|
||||
|
||||
return from_nasal<T>(args[index]);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
naRef to_nasal(T arg) const
|
||||
{
|
||||
return nasal::to_nasal(c, arg);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename from_nasal_ptr<T>::return_type
|
||||
from_nasal(naRef ref) const
|
||||
{
|
||||
return (*from_nasal_ptr<T>::get())(c, ref);
|
||||
}
|
||||
|
||||
naContext c;
|
||||
naRef me;
|
||||
size_t argc;
|
||||
naRef *args;
|
||||
naRef me;
|
||||
size_t argc;
|
||||
naRef *args;
|
||||
};
|
||||
|
||||
} // namespace nasal
|
||||
|
||||
@@ -17,13 +17,79 @@
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "NasalContext.hxx"
|
||||
#include "NasalHash.hxx"
|
||||
#include "NasalString.hxx"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace nasal
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ContextWrapper::ContextWrapper(naContext ctx):
|
||||
_ctx(ctx)
|
||||
{
|
||||
assert(_ctx);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ContextWrapper::operator naContext()
|
||||
{
|
||||
return _ctx;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
naContext ContextWrapper::c_ctx() const
|
||||
{
|
||||
return const_cast<naContext>(_ctx);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Hash ContextWrapper::newHash()
|
||||
{
|
||||
return Hash(_ctx);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
String ContextWrapper::newString(const char* str)
|
||||
{
|
||||
return String(_ctx, str);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
naRef ContextWrapper::callMethod( Me me,
|
||||
naRef code,
|
||||
std::initializer_list<naRef> args )
|
||||
{
|
||||
naRef ret = naCallMethodCtx(
|
||||
_ctx,
|
||||
code,
|
||||
me,
|
||||
args.size(),
|
||||
const_cast<naRef*>(args.begin()),
|
||||
naNil() // locals
|
||||
);
|
||||
|
||||
if( const char* error = naGetError(_ctx) )
|
||||
throw std::runtime_error(error);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
naRef ContextWrapper::newVector(std::initializer_list<naRef> vals)
|
||||
{
|
||||
naRef vec = naNewVector(_ctx);
|
||||
naVec_setsize(_ctx, vec, vals.size());
|
||||
int i = 0;
|
||||
for(naRef val: vals)
|
||||
naVec_set(vec, i++, val);
|
||||
return vec;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Context::Context():
|
||||
_ctx(naNewContext())
|
||||
ContextWrapper(naNewContext())
|
||||
{
|
||||
|
||||
}
|
||||
@@ -32,18 +98,7 @@ namespace nasal
|
||||
Context::~Context()
|
||||
{
|
||||
naFreeContext(_ctx);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Context::operator naContext()
|
||||
{
|
||||
return _ctx;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Hash Context::newHash()
|
||||
{
|
||||
return Hash(_ctx);
|
||||
_ctx = nullptr;
|
||||
}
|
||||
|
||||
} // namespace nasal
|
||||
|
||||
@@ -19,28 +19,110 @@
|
||||
#ifndef SG_NASAL_CONTEXT_HXX_
|
||||
#define SG_NASAL_CONTEXT_HXX_
|
||||
|
||||
#include "NasalHash.hxx"
|
||||
#include "cppbind_fwd.hxx"
|
||||
#include "NasalMe.hxx"
|
||||
|
||||
#include <boost/call_traits.hpp>
|
||||
#include <initializer_list>
|
||||
|
||||
namespace nasal
|
||||
{
|
||||
|
||||
/**
|
||||
* Manage lifetime and encapsulate a Nasal context.
|
||||
* Wraps a nasal ::naContext without taking ownership/managing its lifetime
|
||||
*/
|
||||
class Context
|
||||
class ContextWrapper
|
||||
{
|
||||
public:
|
||||
explicit ContextWrapper(naContext ctx);
|
||||
|
||||
operator naContext();
|
||||
|
||||
/** Convert to non-const ::naContext for usage with C-APIs */
|
||||
naContext c_ctx() const;
|
||||
|
||||
Hash newHash();
|
||||
String newString(const char* str);
|
||||
|
||||
/** Raise a nasal runtime error */
|
||||
template<class... Args>
|
||||
void runtimeError(const char* fmt, Args ... args) const
|
||||
{
|
||||
naRuntimeError(c_ctx(), fmt, args...);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
naRef to_nasal(T arg) const
|
||||
{
|
||||
return nasal::to_nasal(_ctx, arg);
|
||||
}
|
||||
|
||||
template<class T, std::size_t N>
|
||||
naRef to_nasal(const T(&array)[N]) const
|
||||
{
|
||||
return nasal::to_nasal(_ctx, array);
|
||||
}
|
||||
|
||||
/** Create a nasal vector filled with the given values */
|
||||
template<class... Vals>
|
||||
naRef to_nasal_vec(Vals ... vals)
|
||||
{
|
||||
return newVector({to_nasal(vals)...});
|
||||
}
|
||||
|
||||
template<class T>
|
||||
Me to_me(T arg) const
|
||||
{
|
||||
return Me{ to_nasal(arg) };
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename from_nasal_ptr<T>::return_type
|
||||
from_nasal(naRef ref) const
|
||||
{
|
||||
return (*from_nasal_ptr<T>::get())(_ctx, ref);
|
||||
}
|
||||
|
||||
naRef callMethod(Me me, naRef code, std::initializer_list<naRef> args);
|
||||
|
||||
template<class Ret, class... Args>
|
||||
Ret callMethod( Me me,
|
||||
naRef code,
|
||||
typename boost::call_traits<Args>::param_type ... args )
|
||||
{
|
||||
// TODO warn if with Ret == void something different to nil is returned?
|
||||
return from_nasal<Ret>(callMethod(
|
||||
me,
|
||||
code,
|
||||
{ to_nasal<typename boost::call_traits<Args>::param_type>(args)... }
|
||||
));
|
||||
}
|
||||
|
||||
protected:
|
||||
naContext _ctx;
|
||||
|
||||
// Not exposed to avoid confusion of intializer_list<naRef> to variadic
|
||||
// arguments
|
||||
naRef newVector(std::initializer_list<naRef> vals);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates and manages the lifetime of a ::naContext
|
||||
*/
|
||||
class Context:
|
||||
public ContextWrapper
|
||||
{
|
||||
public:
|
||||
Context();
|
||||
~Context();
|
||||
|
||||
operator naContext();
|
||||
|
||||
Hash newHash();
|
||||
|
||||
protected:
|
||||
naContext _ctx;
|
||||
Context(const Context&) = delete;
|
||||
Context& operator=(const Context&) = delete;
|
||||
};
|
||||
|
||||
} // namespace nasal
|
||||
|
||||
#include "from_nasal.hxx"
|
||||
#include "to_nasal.hxx"
|
||||
|
||||
#endif /* SG_NASAL_CONTEXT_HXX_ */
|
||||
|
||||
44
simgear/nasal/cppbind/NasalMe.hxx
Normal file
44
simgear/nasal/cppbind/NasalMe.hxx
Normal file
@@ -0,0 +1,44 @@
|
||||
///@file
|
||||
//
|
||||
// Copyright (C) 2018 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 SG_NASAL_ME_HXX_
|
||||
#define SG_NASAL_ME_HXX_
|
||||
|
||||
#include <simgear/nasal/nasal.h>
|
||||
|
||||
namespace nasal
|
||||
{
|
||||
|
||||
/**
|
||||
* Wrap a naRef to indicate it references the self/me object in Nasal method
|
||||
* calls.
|
||||
*/
|
||||
struct Me
|
||||
{
|
||||
naRef _ref;
|
||||
|
||||
explicit Me(naRef ref = naNil()):
|
||||
_ref(ref)
|
||||
{}
|
||||
|
||||
operator naRef() { return _ref; }
|
||||
};
|
||||
|
||||
} // namespace nasal
|
||||
|
||||
#endif /* SG_NASAL_ME_HXX_ */
|
||||
75
simgear/nasal/cppbind/NasalMethodHolder.hxx
Normal file
75
simgear/nasal/cppbind/NasalMethodHolder.hxx
Normal file
@@ -0,0 +1,75 @@
|
||||
///@file
|
||||
//
|
||||
// Copyright (C) 2018 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 SG_NASAL_METHOD_HOLDER_HXX_
|
||||
#define SG_NASAL_METHOD_HOLDER_HXX_
|
||||
|
||||
#include "NasalContext.hxx"
|
||||
#include "NasalObjectHolder.hxx"
|
||||
|
||||
namespace nasal
|
||||
{
|
||||
|
||||
/**
|
||||
* Hold any callable function in Nasal and call it from C++
|
||||
*/
|
||||
template<class Ret, class... Args>
|
||||
class NasalMethodHolder
|
||||
{
|
||||
using Holder = ObjectHolder<SGReferenced>;
|
||||
|
||||
public:
|
||||
NasalMethodHolder(naRef code):
|
||||
_code(Holder::makeShared(code))
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Call the function with the given arguments
|
||||
*
|
||||
* If the first argument is nasal::Me it will be passed as 'me' object and
|
||||
* not as argument.
|
||||
*/
|
||||
Ret operator()(Args ... args)
|
||||
{
|
||||
return call(args...);
|
||||
}
|
||||
|
||||
private:
|
||||
Holder::Ref _code;
|
||||
|
||||
template<class... CArgs>
|
||||
Ret call(Me self, CArgs ... args)
|
||||
{
|
||||
nasal::Context ctx;
|
||||
return ctx.callMethod<Ret, CArgs...>(
|
||||
self,
|
||||
_code->get_naRef(),
|
||||
args...
|
||||
);
|
||||
}
|
||||
|
||||
template<class... CArgs>
|
||||
Ret call(CArgs ... args)
|
||||
{
|
||||
return call(Me{}, args...);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace nasal
|
||||
|
||||
#endif /* SG_NASAL_METHOD_HOLDER_HXX_ */
|
||||
@@ -23,9 +23,6 @@
|
||||
#include "NasalObjectHolder.hxx"
|
||||
#include "Ghost.hxx"
|
||||
|
||||
#include <boost/preprocessor/iteration/iterate.hpp>
|
||||
#include <boost/preprocessor/repetition/enum_trailing_binary_params.hpp>
|
||||
|
||||
namespace nasal
|
||||
{
|
||||
/**
|
||||
@@ -48,14 +45,21 @@ namespace nasal
|
||||
|
||||
bool valid() const;
|
||||
|
||||
// Build dependency for CMake, gcc, etc.
|
||||
#define SG_DONT_DO_ANYTHING
|
||||
# include <simgear/nasal/cppbind/detail/NasalObject_callMethod_templates.hxx>
|
||||
#undef SG_DONT_DO_ANYTHING
|
||||
template<class Ret, class... Args>
|
||||
Ret callMethod(const std::string& name, Args ... args)
|
||||
{
|
||||
if( !_nasal_impl.valid() )
|
||||
return Ret();
|
||||
|
||||
#define BOOST_PP_ITERATION_LIMITS (0, 9)
|
||||
#define BOOST_PP_FILENAME_1 <simgear/nasal/cppbind/detail/NasalObject_callMethod_templates.hxx>
|
||||
#include BOOST_PP_ITERATE()
|
||||
Context ctx;
|
||||
auto func = get_member<boost::function<Ret (Me, Args...)>>(
|
||||
ctx, _nasal_impl.get_naRef(), name
|
||||
);
|
||||
if( func )
|
||||
return func(Me(ctx.to_nasal(this)), args...);
|
||||
|
||||
return Ret();
|
||||
}
|
||||
|
||||
bool _set(naContext c, const std::string& key, naRef val);
|
||||
bool _get(naContext c, const std::string& key, naRef& out);
|
||||
|
||||
@@ -42,6 +42,8 @@ namespace nasal
|
||||
{
|
||||
public:
|
||||
|
||||
using Ref = SGSharedPtr<ObjectHolder<Base>>;
|
||||
|
||||
/**
|
||||
* @param obj Object to save
|
||||
*/
|
||||
@@ -84,7 +86,7 @@ namespace nasal
|
||||
*
|
||||
* @param obj Object to save
|
||||
*/
|
||||
static SGSharedPtr<ObjectHolder<Base> > makeShared(naRef obj);
|
||||
static Ref makeShared(naRef obj);
|
||||
|
||||
protected:
|
||||
naRef _ref;
|
||||
@@ -139,10 +141,9 @@ namespace nasal
|
||||
template<class Base>
|
||||
ObjectHolder<Base>::ObjectHolder(naRef obj):
|
||||
_ref(obj),
|
||||
_gc_key(0)
|
||||
_gc_key(naIsNil(obj) ? 0 : naGCSave(obj))
|
||||
{
|
||||
if( !naIsNil(obj) )
|
||||
naGCSave(obj);
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -156,10 +157,10 @@ namespace nasal
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
template<class Base>
|
||||
SGSharedPtr<ObjectHolder<Base> >
|
||||
typename ObjectHolder<Base>::Ref
|
||||
ObjectHolder<Base>::makeShared(naRef obj)
|
||||
{
|
||||
return SGSharedPtr<ObjectHolder<Base> >( new ObjectHolder<SGReferenced>(obj) );
|
||||
return Ref( new ObjectHolder<Base>(obj) );
|
||||
}
|
||||
|
||||
} // namespace nasal
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace nasal
|
||||
*
|
||||
* @param str Existing Nasal String
|
||||
*/
|
||||
String(naRef str);
|
||||
explicit String(naRef str);
|
||||
|
||||
const char* c_str() const;
|
||||
const char* begin() const;
|
||||
|
||||
60
simgear/nasal/cppbind/cppbind_fwd.hxx
Normal file
60
simgear/nasal/cppbind/cppbind_fwd.hxx
Normal file
@@ -0,0 +1,60 @@
|
||||
///@file
|
||||
/// Nasal C++ Bindings forward declarations
|
||||
///
|
||||
// Copyright (C) 2018 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 SG_NASAL_CPPBIND_FWD_HXX_
|
||||
#define SG_NASAL_CPPBIND_FWD_HXX_
|
||||
|
||||
#include <simgear/nasal/nasal.h>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
namespace nasal
|
||||
{
|
||||
|
||||
class bad_nasal_cast;
|
||||
|
||||
class CallContext;
|
||||
class Context;
|
||||
class ContextWrapper;
|
||||
class Hash;
|
||||
struct Me;
|
||||
class Object;
|
||||
class String;
|
||||
|
||||
template<class, class>
|
||||
class Ghost;
|
||||
|
||||
template<class T>
|
||||
naRef to_nasal(naContext c, T arg);
|
||||
|
||||
template<class T, std::size_t N>
|
||||
naRef to_nasal(naContext c, const T(&array)[N]);
|
||||
|
||||
template<class T>
|
||||
T from_nasal(naContext c, naRef ref);
|
||||
|
||||
template<class Var>
|
||||
struct from_nasal_ptr;
|
||||
|
||||
template<class T>
|
||||
T get_member(naContext c, naRef obj, const std::string& name);
|
||||
|
||||
} // namespace nasal
|
||||
|
||||
#endif /* SG_NASAL_CPPBIND_FWD_HXX_ */
|
||||
@@ -1,35 +0,0 @@
|
||||
#ifndef SG_NASAL_OBJECT_HXX_
|
||||
# error Nasal cppbind - do not include this file!
|
||||
#endif
|
||||
|
||||
#ifndef SG_DONT_DO_ANYTHING
|
||||
#define n BOOST_PP_ITERATION()
|
||||
|
||||
#define SG_CALL_ARG(z, n, dummy)\
|
||||
to_nasal<typename boost::call_traits<A##n>::param_type>(ctx, a##n)
|
||||
|
||||
template<
|
||||
class Ret
|
||||
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
|
||||
>
|
||||
Ret callMethod( const std::string& name
|
||||
BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(n, A, a) )
|
||||
{
|
||||
if( !_nasal_impl.valid() )
|
||||
return Ret();
|
||||
|
||||
typedef boost::function<Ret (nasal::Me BOOST_PP_ENUM_TRAILING_PARAMS(n, A))>
|
||||
MemFunc;
|
||||
|
||||
Context ctx;
|
||||
MemFunc f = get_member<MemFunc>(ctx, _nasal_impl.get_naRef(), name.c_str());
|
||||
if( f )
|
||||
return f(nasal::to_nasal(ctx, this) BOOST_PP_ENUM_TRAILING_PARAMS(n, a));
|
||||
|
||||
return Ret();
|
||||
}
|
||||
|
||||
#undef SG_CALL_ARG
|
||||
|
||||
#undef n
|
||||
#endif // SG_DONT_DO_ANYTHING
|
||||
@@ -1,131 +0,0 @@
|
||||
#ifndef SG_FROM_NASAL_HELPER_HXX_
|
||||
# error Nasal cppbind - do not include this file!
|
||||
#endif
|
||||
|
||||
#ifndef SG_DONT_DO_ANYTHING
|
||||
#define n BOOST_PP_ITERATION()
|
||||
|
||||
#ifndef SG_BOOST_FUNCTION_FROM_NASAL_FWD
|
||||
# define SG_CALL_TRAITS_PARAM(z, n, dummy)\
|
||||
typename boost::call_traits<A##n>::param_type a##n
|
||||
# define SG_CALL_ARG(z, n, dummy)\
|
||||
to_nasal<typename boost::call_traits<A##n>::param_type>(ctx, a##n)
|
||||
|
||||
template<
|
||||
class Ret
|
||||
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
|
||||
>
|
||||
typename boost::disable_if<boost::is_void<Ret>, Ret>::type
|
||||
callNasalMethod( const ObjectHolder<SGReferenced>* holder,
|
||||
Me self
|
||||
BOOST_PP_ENUM_TRAILING(n, SG_CALL_TRAITS_PARAM, 0) )
|
||||
{
|
||||
naContext ctx = naNewContext();
|
||||
#if n
|
||||
naRef args[n] = {
|
||||
BOOST_PP_ENUM(n, SG_CALL_ARG, 0)
|
||||
};
|
||||
#else
|
||||
naRef* args = NULL;
|
||||
#endif
|
||||
|
||||
naRef result =
|
||||
naCallMethodCtx(ctx, holder->get_naRef(), self, n, args, naNil());
|
||||
|
||||
const char* error = naGetError(ctx);
|
||||
std::string error_str(error ? error : "");
|
||||
|
||||
Ret r = Ret();
|
||||
if( !error )
|
||||
r = from_nasal_helper(ctx, result, static_cast<Ret*>(0));
|
||||
|
||||
naFreeContext(ctx);
|
||||
|
||||
if( error )
|
||||
throw std::runtime_error(error_str);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
template<
|
||||
class Ret
|
||||
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
|
||||
>
|
||||
typename boost::enable_if<boost::is_void<Ret>, Ret>::type
|
||||
callNasalMethod( const ObjectHolder<SGReferenced>* holder,
|
||||
Me self
|
||||
BOOST_PP_ENUM_TRAILING(n, SG_CALL_TRAITS_PARAM, 0) )
|
||||
{
|
||||
callNasalMethod<
|
||||
naRef // do not do any conversion and just ignore the return value
|
||||
// TODO warn if something different to nil is returned?
|
||||
BOOST_PP_COMMA_IF(n)
|
||||
BOOST_PP_ENUM_PARAMS(n, A)
|
||||
>
|
||||
(
|
||||
holder,
|
||||
self
|
||||
BOOST_PP_ENUM_TRAILING_PARAMS(n, a)
|
||||
);
|
||||
}
|
||||
|
||||
# undef SG_CALL_TRAITS_PARAM
|
||||
# undef SG_CALL_ARG
|
||||
#endif
|
||||
|
||||
template<
|
||||
class Ret
|
||||
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
|
||||
>
|
||||
typename boost::disable_if<
|
||||
// free function if first argument is not nasal::Me or no argument at all
|
||||
boost::is_same<BOOST_PP_IF(n, A0, void), Me>,
|
||||
boost::function<Ret (BOOST_PP_ENUM_PARAMS(n, A))>
|
||||
>::type
|
||||
boostFunctionFromNasal(naRef code, Ret (*)(BOOST_PP_ENUM_PARAMS(n, A)))
|
||||
#ifdef SG_BOOST_FUNCTION_FROM_NASAL_FWD
|
||||
;
|
||||
#else
|
||||
{
|
||||
return boost::bind
|
||||
(
|
||||
&callNasalMethod<Ret BOOST_PP_ENUM_TRAILING_PARAMS(n, A)>,
|
||||
ObjectHolder<SGReferenced>::makeShared(code),
|
||||
boost::bind(naNil)
|
||||
BOOST_PP_COMMA_IF(n)
|
||||
BOOST_PP_ENUM_SHIFTED_PARAMS(BOOST_PP_INC(n), _)
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if n > 0
|
||||
template<
|
||||
class Ret
|
||||
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
|
||||
>
|
||||
typename boost::enable_if<
|
||||
// method if type of first argument is nasal::Me
|
||||
boost::is_same<A0, Me>,
|
||||
boost::function<Ret (BOOST_PP_ENUM_PARAMS(n, A))>
|
||||
>::type
|
||||
boostFunctionFromNasal(naRef code, Ret (*)(BOOST_PP_ENUM_PARAMS(n, A)))
|
||||
#ifdef SG_BOOST_FUNCTION_FROM_NASAL_FWD
|
||||
;
|
||||
#else
|
||||
{
|
||||
return boost::bind
|
||||
(
|
||||
&callNasalMethod<
|
||||
Ret
|
||||
BOOST_PP_COMMA_IF(BOOST_PP_DEC(n))
|
||||
BOOST_PP_ENUM_SHIFTED_PARAMS(n, A)
|
||||
>,
|
||||
ObjectHolder<SGReferenced>::makeShared(code),
|
||||
BOOST_PP_ENUM_SHIFTED_PARAMS(BOOST_PP_INC(n), _)
|
||||
);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#undef n
|
||||
#endif // SG_DONT_DO_ANYTHING
|
||||
@@ -39,7 +39,7 @@ namespace nasal
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bad_nasal_cast::~bad_nasal_cast() throw()
|
||||
bad_nasal_cast::~bad_nasal_cast()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -24,23 +24,17 @@
|
||||
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
#include <simgear/math/SGRect.hxx>
|
||||
#include <simgear/nasal/nasal.h>
|
||||
#include <simgear/nasal/cppbind/NasalContext.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalMe.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalMethodHolder.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalObjectHolder.hxx>
|
||||
#include <simgear/nasal/cppbind/to_nasal.hxx>
|
||||
#include <simgear/std/type_traits.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/call_traits.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/preprocessor/control/if.hpp>
|
||||
#include <boost/preprocessor/iteration/iterate.hpp>
|
||||
#include <boost/preprocessor/repetition/enum_trailing.hpp>
|
||||
#include <boost/preprocessor/repetition/enum_trailing_params.hpp>
|
||||
#include <boost/preprocessor/repetition/enum_shifted_params.hpp>
|
||||
#include <boost/type_traits.hpp>
|
||||
#include <boost/utility/enable_if.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -48,8 +42,6 @@ class SGPath;
|
||||
|
||||
namespace nasal
|
||||
{
|
||||
class Hash;
|
||||
class String;
|
||||
|
||||
/**
|
||||
* Thrown when converting a type from/to Nasal has failed
|
||||
@@ -70,33 +62,25 @@ namespace nasal
|
||||
*/
|
||||
explicit bad_nasal_cast(const std::string& msg);
|
||||
|
||||
virtual ~bad_nasal_cast() throw();
|
||||
virtual ~bad_nasal_cast();
|
||||
|
||||
protected:
|
||||
std::string _msg;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrap a naRef to indicate it references the self/me object in Nasal method
|
||||
* calls.
|
||||
*/
|
||||
struct Me
|
||||
{
|
||||
naRef _ref;
|
||||
|
||||
Me(naRef ref):
|
||||
_ref(ref)
|
||||
{}
|
||||
|
||||
operator naRef() { return _ref; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple pass through for unified handling also of naRef.
|
||||
*/
|
||||
inline naRef from_nasal_helper(naContext, naRef ref, const naRef*)
|
||||
{ return ref; }
|
||||
|
||||
/**
|
||||
* Ignore return value
|
||||
*/
|
||||
// TODO show some warning when something is returned but ignored?
|
||||
inline void from_nasal_helper(naContext, naRef, const void*)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Convert Nasal string to std::string
|
||||
*/
|
||||
@@ -125,52 +109,40 @@ namespace nasal
|
||||
*/
|
||||
bool from_nasal_helper(naContext c, naRef ref, const bool*);
|
||||
|
||||
namespace detail
|
||||
{
|
||||
#define SG_BOOST_FUNCTION_FROM_NASAL_FWD
|
||||
#define BOOST_PP_ITERATION_LIMITS (0, 9)
|
||||
#define BOOST_PP_FILENAME_1 <simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx>
|
||||
#include BOOST_PP_ITERATE()
|
||||
#undef SG_BOOST_FUNCTION_FROM_NASAL_FWD
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Nasal function to a boost::function with the given signature.
|
||||
*
|
||||
* @tparam Sig Signature of returned function (arguments and return value
|
||||
* are automatically converted using from_nasal/to_nasal)
|
||||
*/
|
||||
template<class Sig>
|
||||
boost::function<Sig>
|
||||
from_nasal_helper(naContext c, naRef ref, boost::function<Sig>*)
|
||||
template<class Ret, class... Args>
|
||||
boost::function<Ret (Args...)>
|
||||
from_nasal_helper(naContext c, naRef ref, const boost::function<Ret (Args...)>*)
|
||||
{
|
||||
if( naIsNil(ref) )
|
||||
return boost::function<Sig>();
|
||||
return {};
|
||||
|
||||
if( !naIsCode(ref)
|
||||
&& !naIsCCode(ref)
|
||||
&& !naIsFunc(ref) )
|
||||
throw bad_nasal_cast("not a function");
|
||||
|
||||
return detail::boostFunctionFromNasal(ref, static_cast<Sig*>(0));
|
||||
return NasalMethodHolder<Ret, Args...>(ref);
|
||||
}
|
||||
|
||||
template<class Sig>
|
||||
typename boost::enable_if< boost::is_function<Sig>,
|
||||
boost::function<Sig>
|
||||
>::type
|
||||
from_nasal_helper(naContext c, naRef ref, Sig*)
|
||||
template<class Ret, class... Args>
|
||||
boost::function<Ret (Args...)>
|
||||
from_nasal_helper(naContext c, naRef ref, Ret (*const)(Args...))
|
||||
{
|
||||
return from_nasal_helper(c, ref, static_cast<boost::function<Sig>*>(0));
|
||||
return
|
||||
from_nasal_helper(c, ref, static_cast<boost::function<Ret (Args...)>*>(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Nasal number to a C++ numeric type
|
||||
*/
|
||||
template<class T>
|
||||
typename boost::enable_if< boost::is_arithmetic<T>,
|
||||
T
|
||||
>::type
|
||||
std::enable_if_t<std::is_arithmetic<T>::value, T>
|
||||
from_nasal_helper(naContext c, naRef ref, const T*)
|
||||
{
|
||||
naRef num = naNumValue(ref);
|
||||
@@ -199,17 +171,39 @@ namespace nasal
|
||||
return vec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Nasal vector to a std::array
|
||||
*/
|
||||
template<class T, std::size_t N>
|
||||
std::array<T, N>
|
||||
from_nasal_helper(naContext c, naRef ref, const std::array<T, N>*)
|
||||
{
|
||||
if( !naIsVector(ref) )
|
||||
throw bad_nasal_cast("Not a vector");
|
||||
|
||||
if( naVec_size(ref) != N )
|
||||
throw bad_nasal_cast(
|
||||
"Expected vector with " + std::to_string(N) + " elements"
|
||||
);
|
||||
|
||||
std::array<T, N> arr;
|
||||
|
||||
for(std::size_t i = 0; i < N; ++i)
|
||||
arr[i] = from_nasal_helper(c, naVec_get(ref, i), static_cast<T*>(0));
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Nasal vector of 2 elements to a 2d vector
|
||||
*/
|
||||
template<class Vec2>
|
||||
typename boost::enable_if<is_vec2<Vec2>, Vec2>::type
|
||||
std::enable_if_t<is_vec2<Vec2>::value, Vec2>
|
||||
from_nasal_helper(naContext c, naRef ref, const Vec2*)
|
||||
{
|
||||
std::vector<double> vec =
|
||||
from_nasal_helper(c, ref, static_cast<std::vector<double>*>(0));
|
||||
if( vec.size() != 2 )
|
||||
throw bad_nasal_cast("Expected vector with two elements");
|
||||
auto vec =
|
||||
from_nasal_helper(c, ref, static_cast<std::array<double, 2>*>(0));
|
||||
|
||||
return Vec2(vec[0], vec[1]);
|
||||
}
|
||||
|
||||
@@ -219,29 +213,12 @@ namespace nasal
|
||||
template<class T>
|
||||
SGRect<T> from_nasal_helper(naContext c, naRef ref, const SGRect<T>*)
|
||||
{
|
||||
std::vector<double> vec =
|
||||
from_nasal_helper(c, ref, static_cast<std::vector<double>*>(0));
|
||||
if( vec.size() != 4 )
|
||||
throw bad_nasal_cast("Expected vector with four elements");
|
||||
auto vec =
|
||||
from_nasal_helper(c, ref, static_cast<std::array<double, 4>*>(0));
|
||||
|
||||
return SGRect<T>(vec[0], vec[1], vec[2], vec[3]);
|
||||
}
|
||||
|
||||
// Helpers for wrapping calls to Nasal functions into boost::function
|
||||
namespace detail
|
||||
{
|
||||
// Dummy include to add a build dependency on this file for gcc/CMake/etc.
|
||||
#define SG_DONT_DO_ANYTHING
|
||||
# include <simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx>
|
||||
#undef SG_DONT_DO_ANYTHING
|
||||
|
||||
// Now the actual include (we are limited to 8 arguments (+me) here because
|
||||
// boost::bind has an upper limit of 9)
|
||||
#define BOOST_PP_ITERATION_LIMITS (0, 8)
|
||||
#define BOOST_PP_FILENAME_1 <simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx>
|
||||
#include BOOST_PP_ITERATE()
|
||||
}
|
||||
|
||||
} // namespace nasal
|
||||
|
||||
#endif /* SG_FROM_NASAL_HELPER_HXX_ */
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
#ifndef SG_NASAL_GHOST_HXX_
|
||||
# error Nasal cppbind - do not include this file!
|
||||
#endif
|
||||
|
||||
#ifndef SG_DONT_DO_ANYTHING
|
||||
#define n BOOST_PP_ITERATION()
|
||||
|
||||
#define SG_GHOST_FUNC_TYPE\
|
||||
boost::function<Ret (raw_type& BOOST_PP_ENUM_TRAILING_PARAMS(n,A))>
|
||||
|
||||
/**
|
||||
* Bind any callable entity accepting an instance of raw_type and an arbitrary
|
||||
* number of arguments as method.
|
||||
*/
|
||||
template<
|
||||
class Ret
|
||||
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
|
||||
>
|
||||
Ghost& method(const std::string& name, const SG_GHOST_FUNC_TYPE& func)
|
||||
{
|
||||
#if defined(SG_GCC_VERSION) && SG_GCC_VERSION < 40407
|
||||
// The old version of g++ used on Jenkins (16.11.2012) only compiles this
|
||||
// version.
|
||||
# define SG_GHOST_REQUIRE_ARG(z, n, dummy)\
|
||||
boost::bind(&arg_from_nasal<A##n>, _2, n)
|
||||
#else
|
||||
// VS (2008, 2010, ... ?) only allow this version.
|
||||
# define SG_GHOST_REQUIRE_ARG(z, n, dummy)\
|
||||
boost::bind(&Ghost::arg_from_nasal<A##n>, _2, n)
|
||||
#endif
|
||||
|
||||
return method<Ret>
|
||||
(
|
||||
name,
|
||||
typename boost::function<Ret (raw_type&, const CallContext&)>
|
||||
( boost::bind(
|
||||
func,
|
||||
_1
|
||||
BOOST_PP_ENUM_TRAILING(n, SG_GHOST_REQUIRE_ARG, 0)
|
||||
))
|
||||
);
|
||||
|
||||
#undef SG_GHOST_REQUIRE_ARG
|
||||
}
|
||||
|
||||
#define SG_GHOST_MEM_FN(cv)\
|
||||
/**\
|
||||
* Bind a member function with an arbitrary number of arguments as method.\
|
||||
*/\
|
||||
template<\
|
||||
class Ret\
|
||||
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)\
|
||||
>\
|
||||
Ghost& method\
|
||||
(\
|
||||
const std::string& name,\
|
||||
Ret (raw_type::*fn)(BOOST_PP_ENUM_PARAMS(n,A)) cv\
|
||||
)\
|
||||
{\
|
||||
return method<\
|
||||
Ret\
|
||||
BOOST_PP_ENUM_TRAILING_PARAMS(n,A)\
|
||||
>(name, SG_GHOST_FUNC_TYPE(fn));\
|
||||
}
|
||||
|
||||
// Work around MSVC warning C4003: not enough actual parameters for macro
|
||||
// We really do not want to pass a parameter, even if MSVC can not believe it.
|
||||
#define SG_GHOST_NO_CV
|
||||
|
||||
SG_GHOST_MEM_FN(const)
|
||||
SG_GHOST_MEM_FN(SG_GHOST_NO_CV)
|
||||
|
||||
#undef SG_GHOST_MEM_FN
|
||||
#undef SG_GHOST_NO_CV
|
||||
|
||||
/**
|
||||
* Bind free function accepting an instance of raw_type and an arbitrary
|
||||
* number of arguments as method.
|
||||
*/
|
||||
template<
|
||||
class Ret,
|
||||
class Type
|
||||
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
|
||||
>
|
||||
Ghost& method
|
||||
(
|
||||
const std::string& name,
|
||||
Ret (*fn)(Type BOOST_PP_ENUM_TRAILING_PARAMS(n,A))
|
||||
)
|
||||
{
|
||||
BOOST_STATIC_ASSERT
|
||||
(( boost::is_convertible<raw_type&, Type>::value
|
||||
//|| boost::is_convertible<raw_type*, Type>::value
|
||||
// TODO check how to do it with pointer...
|
||||
));
|
||||
return method<Ret>(name, SG_GHOST_FUNC_TYPE(fn));
|
||||
}
|
||||
|
||||
#undef n
|
||||
#undef SG_GHOST_TYPEDEF_FUNC_TYPE
|
||||
#endif // SG_DONT_DO_ANYTHING
|
||||
@@ -20,10 +20,7 @@
|
||||
#ifndef SG_NASAL_TRAITS_HXX_
|
||||
#define SG_NASAL_TRAITS_HXX_
|
||||
|
||||
#include <boost/mpl/logical.hpp>
|
||||
#include <boost/type_traits/integral_constant.hpp>
|
||||
#include <boost/type_traits/is_base_of.hpp>
|
||||
#include <boost/utility/enable_if.hpp>
|
||||
#include <simgear/std/type_traits.hxx>
|
||||
|
||||
// Forward declarations
|
||||
class SGReferenced;
|
||||
@@ -56,12 +53,11 @@ namespace osg
|
||||
namespace nasal
|
||||
{
|
||||
template<class T>
|
||||
struct is_vec2: public boost::integral_constant<bool, false> {};
|
||||
struct is_vec2: public std::false_type {};
|
||||
|
||||
#define SG_MAKE_TRAIT(templ,type,attr)\
|
||||
template templ\
|
||||
struct attr< type >:\
|
||||
public boost::integral_constant<bool, true> {};
|
||||
struct attr< type >: public std::true_type {};
|
||||
|
||||
SG_MAKE_TRAIT(<class T>, SGVec2<T>, is_vec2)
|
||||
SG_MAKE_TRAIT(<>, osg::Vec2b, is_vec2)
|
||||
@@ -75,42 +71,34 @@ SG_MAKE_TRAIT(<>, osg::Vec2s, is_vec2)
|
||||
struct shared_ptr_traits;
|
||||
|
||||
template<class T>
|
||||
struct is_strong_ref:
|
||||
public boost::integral_constant<bool, false>
|
||||
{};
|
||||
struct is_strong_ref: public std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_weak_ref:
|
||||
public boost::integral_constant<bool, false>
|
||||
{};
|
||||
struct is_weak_ref: public std::false_type {};
|
||||
|
||||
#define SG_MAKE_SHARED_PTR_TRAIT(strong, weak, intrusive)\
|
||||
template<class T>\
|
||||
struct shared_ptr_traits<strong<T> >\
|
||||
{\
|
||||
typedef strong<T> strong_ref;\
|
||||
typedef weak<T> weak_ref;\
|
||||
typedef T element_type;\
|
||||
typedef boost::integral_constant<bool, true> is_strong;\
|
||||
typedef boost::integral_constant<bool, intrusive> is_intrusive;\
|
||||
using strong_ref = strong<T>;\
|
||||
using weak_ref = weak<T>;\
|
||||
using element_type = T;\
|
||||
using is_strong = std::true_type;\
|
||||
using is_intrusive = std::bool_constant<intrusive>;\
|
||||
};\
|
||||
template<class T>\
|
||||
struct shared_ptr_traits<weak<T> >\
|
||||
{\
|
||||
typedef strong<T> strong_ref;\
|
||||
typedef weak<T> weak_ref;\
|
||||
typedef T element_type;\
|
||||
typedef boost::integral_constant<bool, false> is_strong;\
|
||||
typedef boost::integral_constant<bool, intrusive> is_intrusive;\
|
||||
using strong_ref = strong<T>;\
|
||||
using weak_ref = weak<T>;\
|
||||
using element_type = T;\
|
||||
using is_strong = std::false_type;\
|
||||
using is_intrusive = std::bool_constant<intrusive>;\
|
||||
};\
|
||||
template<class T>\
|
||||
struct is_strong_ref<strong<T> >:\
|
||||
public boost::integral_constant<bool, true>\
|
||||
{};\
|
||||
struct is_strong_ref<strong<T> >: public std::true_type {};\
|
||||
template<class T>\
|
||||
struct is_weak_ref<weak<T> >:\
|
||||
public boost::integral_constant<bool, true>\
|
||||
{};
|
||||
struct is_weak_ref<weak<T> >: public std::true_type {};
|
||||
|
||||
SG_MAKE_SHARED_PTR_TRAIT(SGSharedPtr, SGWeakPtr, true)
|
||||
SG_MAKE_SHARED_PTR_TRAIT(osg::ref_ptr, osg::observer_ptr, true)
|
||||
@@ -119,25 +107,20 @@ SG_MAKE_TRAIT(<>, osg::Vec2s, is_vec2)
|
||||
#undef SG_MAKE_SHARED_PTR_TRAIT
|
||||
|
||||
template<class T>
|
||||
struct supports_weak_ref:
|
||||
public boost::integral_constant<bool, true>
|
||||
{};
|
||||
struct supports_weak_ref: public std::true_type {};
|
||||
|
||||
template<class T>
|
||||
struct supports_weak_ref<SGSharedPtr<T> >:
|
||||
public boost::integral_constant<
|
||||
bool,
|
||||
boost::is_base_of<SGWeakReferenced, T>::value
|
||||
>
|
||||
public std::bool_constant<std::is_base_of<SGWeakReferenced, T>::value>
|
||||
{};
|
||||
|
||||
template<class T>
|
||||
struct shared_ptr_storage
|
||||
{
|
||||
typedef T storage_type;
|
||||
typedef typename T::element_type element_type;
|
||||
typedef typename shared_ptr_traits<T>::strong_ref strong_ref;
|
||||
typedef typename shared_ptr_traits<T>::weak_ref weak_ref;
|
||||
using storage_type = T;
|
||||
using element_type = typename T::element_type;
|
||||
using strong_ref = typename shared_ptr_traits<T>::strong_ref;
|
||||
using weak_ref = typename shared_ptr_traits<T>::weak_ref;
|
||||
|
||||
template<class U>
|
||||
static storage_type* ref(U ptr)
|
||||
@@ -151,39 +134,30 @@ SG_MAKE_TRAIT(<>, osg::Vec2s, is_vec2)
|
||||
|
||||
template<class U>
|
||||
static
|
||||
typename boost::enable_if<
|
||||
boost::is_same<U, element_type*>,
|
||||
element_type*
|
||||
>::type
|
||||
std::enable_if_t<std::is_same<U, element_type*>::value, element_type*>
|
||||
get(storage_type* ptr)
|
||||
{
|
||||
return get_pointer(*ptr);
|
||||
}
|
||||
|
||||
template<class U>
|
||||
static
|
||||
typename boost::enable_if<
|
||||
boost::mpl::or_<
|
||||
boost::is_same<U, strong_ref>,
|
||||
boost::mpl::and_<
|
||||
boost::is_same<U, weak_ref>,
|
||||
supports_weak_ref<U>
|
||||
>
|
||||
>,
|
||||
std::enable_if_t<
|
||||
std::is_same<U, strong_ref>::value
|
||||
|| (std::is_same<U, weak_ref>::value && supports_weak_ref<U>::value),
|
||||
U
|
||||
>::type
|
||||
>
|
||||
get(storage_type* ptr)
|
||||
{
|
||||
return U(*ptr);
|
||||
}
|
||||
|
||||
template<class U>
|
||||
static
|
||||
typename boost::enable_if<
|
||||
boost::mpl::and_<
|
||||
boost::is_same<U, weak_ref>,
|
||||
boost::mpl::not_<supports_weak_ref<U> >
|
||||
>,
|
||||
std::enable_if_t<
|
||||
std::is_same<U, weak_ref>::value && !supports_weak_ref<U>::value,
|
||||
U
|
||||
>::type
|
||||
>
|
||||
get(storage_type* ptr)
|
||||
{
|
||||
return U();
|
||||
@@ -195,46 +169,37 @@ SG_MAKE_TRAIT(<>, osg::Vec2s, is_vec2)
|
||||
template<class T>
|
||||
struct intrusive_ptr_storage
|
||||
{
|
||||
typedef typename T::element_type storage_type;
|
||||
typedef typename T::element_type element_type;
|
||||
typedef typename shared_ptr_traits<T>::strong_ref strong_ref;
|
||||
typedef typename shared_ptr_traits<T>::weak_ref weak_ref;
|
||||
using storage_type = typename T::element_type;
|
||||
using element_type = typename T::element_type;
|
||||
using strong_ref = typename shared_ptr_traits<T>::strong_ref;
|
||||
using weak_ref = typename shared_ptr_traits<T>::weak_ref;
|
||||
|
||||
template<class U>
|
||||
static
|
||||
typename boost::enable_if<
|
||||
boost::is_same<U, element_type*>,
|
||||
element_type*
|
||||
>::type
|
||||
std::enable_if_t<std::is_same<U, element_type*>::value, element_type*>
|
||||
get(storage_type* ptr)
|
||||
{
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template<class U>
|
||||
static
|
||||
typename boost::enable_if<
|
||||
boost::mpl::or_<
|
||||
boost::is_same<U, strong_ref>,
|
||||
boost::mpl::and_<
|
||||
boost::is_same<U, weak_ref>,
|
||||
supports_weak_ref<U>
|
||||
>
|
||||
>,
|
||||
std::enable_if_t<
|
||||
std::is_same<U, strong_ref>::value
|
||||
|| (std::is_same<U, weak_ref>::value && supports_weak_ref<U>::value),
|
||||
U
|
||||
>::type
|
||||
>
|
||||
get(storage_type* ptr)
|
||||
{
|
||||
return U(ptr);
|
||||
}
|
||||
|
||||
template<class U>
|
||||
static
|
||||
typename boost::enable_if<
|
||||
boost::mpl::and_<
|
||||
boost::is_same<U, weak_ref>,
|
||||
boost::mpl::not_<supports_weak_ref<U> >
|
||||
>,
|
||||
std::enable_if_t<
|
||||
std::is_same<U, weak_ref>::value && !supports_weak_ref<U>::value,
|
||||
U
|
||||
>::type
|
||||
>
|
||||
get(storage_type* ptr)
|
||||
{
|
||||
return U();
|
||||
@@ -246,8 +211,8 @@ SG_MAKE_TRAIT(<>, osg::Vec2s, is_vec2)
|
||||
struct shared_ptr_storage<SGSharedPtr<T> >:
|
||||
public internal::intrusive_ptr_storage<SGSharedPtr<T> >
|
||||
{
|
||||
typedef T storage_type;
|
||||
typedef T element_type;
|
||||
using storage_type = T;
|
||||
using element_type = T;
|
||||
|
||||
static storage_type* ref(element_type* ptr)
|
||||
{
|
||||
@@ -265,8 +230,8 @@ SG_MAKE_TRAIT(<>, osg::Vec2s, is_vec2)
|
||||
struct shared_ptr_storage<osg::ref_ptr<T> >:
|
||||
public internal::intrusive_ptr_storage<osg::ref_ptr<T> >
|
||||
{
|
||||
typedef T storage_type;
|
||||
typedef T element_type;
|
||||
using storage_type = T;
|
||||
using element_type = T;
|
||||
|
||||
|
||||
static storage_type* ref(element_type* ptr)
|
||||
|
||||
@@ -24,13 +24,14 @@
|
||||
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
#include <simgear/math/SGRect.hxx>
|
||||
#include <simgear/nasal/nasal.h>
|
||||
#include <simgear/nasal/cppbind/cppbind_fwd.hxx>
|
||||
#include <simgear/std/type_traits.hxx>
|
||||
|
||||
#include <boost/function/function_fwd.hpp>
|
||||
#include <boost/utility/enable_if.hpp>
|
||||
#include <boost/call_traits.hpp>
|
||||
#include <boost/type_traits.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -40,8 +41,6 @@ class SGPath;
|
||||
|
||||
namespace nasal
|
||||
{
|
||||
class CallContext;
|
||||
class Hash;
|
||||
|
||||
typedef boost::function<naRef (CallContext)> free_function_t;
|
||||
|
||||
@@ -77,11 +76,66 @@ namespace nasal
|
||||
|
||||
naRef to_nasal_helper(naContext c, const free_function_t& func);
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template<class T>
|
||||
naRef array_to_nasal(naContext c, const T* arr, size_t size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a fixed size array to a Nasal vector
|
||||
*/
|
||||
template<class T, size_t N>
|
||||
naRef to_nasal_helper(naContext c, const T(&array)[N])
|
||||
{
|
||||
return detail::array_to_nasal(c, array, N);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a fixed size C++ array to a Nasal vector
|
||||
*/
|
||||
template<class T, size_t N>
|
||||
naRef to_nasal_helper(naContext c, const std::array<T, N>& array)
|
||||
{
|
||||
return detail::array_to_nasal(c, array.data(), N);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a std::initializer_list to a Nasal vector
|
||||
*/
|
||||
template<class T>
|
||||
naRef to_nasal_helper(naContext c, std::initializer_list<T> list)
|
||||
{
|
||||
return detail::array_to_nasal(c, list.begin(), list.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert std::vector to a Nasal vector
|
||||
*/
|
||||
template< template<class T, class Alloc> class Vector,
|
||||
class T,
|
||||
class Alloc
|
||||
>
|
||||
std::enable_if_t<
|
||||
std::is_same<Vector<T,Alloc>, std::vector<T,Alloc>>::value,
|
||||
naRef
|
||||
>
|
||||
to_nasal_helper(naContext c, const Vector<T, Alloc>& vec)
|
||||
{
|
||||
return detail::array_to_nasal(c, vec.data(), vec.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a std::map to a Nasal Hash
|
||||
*/
|
||||
template<class Value>
|
||||
naRef to_nasal_helper(naContext c, const std::map<std::string, Value>& map);
|
||||
|
||||
/**
|
||||
* Convert an enum value to the according numeric value
|
||||
*/
|
||||
template<class T>
|
||||
typename boost::enable_if< boost::is_enum<T>, naRef >::type
|
||||
std::enable_if_t<std::is_enum<T>::value, naRef>
|
||||
to_nasal_helper(naContext c, T val)
|
||||
{
|
||||
return naNum(val);
|
||||
@@ -91,7 +145,7 @@ namespace nasal
|
||||
* Convert a numeric type to Nasal number
|
||||
*/
|
||||
template<class T>
|
||||
typename boost::enable_if< boost::is_arithmetic<T>, naRef >::type
|
||||
std::enable_if_t<std::is_arithmetic<T>::value, naRef>
|
||||
to_nasal_helper(naContext c, T num)
|
||||
{
|
||||
return naNum(num);
|
||||
@@ -101,67 +155,40 @@ namespace nasal
|
||||
* Convert a 2d vector to Nasal vector with 2 elements
|
||||
*/
|
||||
template<class Vec2>
|
||||
typename boost::enable_if<is_vec2<Vec2>, naRef>::type
|
||||
to_nasal_helper(naContext c, const Vec2& vec);
|
||||
|
||||
/**
|
||||
* Convert a std::map to a Nasal Hash
|
||||
*/
|
||||
template<class Value>
|
||||
naRef to_nasal_helper(naContext c, const std::map<std::string, Value>& map);
|
||||
|
||||
/**
|
||||
* Convert a fixed size array to a Nasal vector
|
||||
*/
|
||||
template<class T, size_t N>
|
||||
naRef to_nasal_helper(naContext c, const T(&array)[N]);
|
||||
|
||||
/**
|
||||
* Convert std::vector to Nasal vector
|
||||
*/
|
||||
template< template<class T, class Alloc> class Vector,
|
||||
class T,
|
||||
class Alloc
|
||||
>
|
||||
typename boost::enable_if< boost::is_same< Vector<T,Alloc>,
|
||||
std::vector<T,Alloc>
|
||||
>,
|
||||
naRef
|
||||
>::type
|
||||
to_nasal_helper(naContext c, const Vector<T, Alloc>& vec)
|
||||
{
|
||||
naRef ret = naNewVector(c);
|
||||
naVec_setsize(c, ret, static_cast<int>(vec.size()));
|
||||
for(int i = 0; i < static_cast<int>(vec.size()); ++i)
|
||||
naVec_set(ret, i, to_nasal_helper(c, vec[i]));
|
||||
return ret;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
template<class Vec2>
|
||||
typename boost::enable_if<is_vec2<Vec2>, naRef>::type
|
||||
std::enable_if_t<is_vec2<Vec2>::value, naRef>
|
||||
to_nasal_helper(naContext c, const Vec2& vec)
|
||||
{
|
||||
// We take just double because in Nasal every number is represented as
|
||||
// double
|
||||
double nasal_vec[2] = {
|
||||
return to_nasal_helper(c, {
|
||||
// We take just double because in Nasal every number is represented as
|
||||
// double
|
||||
static_cast<double>(vec[0]),
|
||||
static_cast<double>(vec[1])
|
||||
};
|
||||
return to_nasal_helper(c, nasal_vec);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a SGRect to a Nasal vector with position and size of the rect
|
||||
*/
|
||||
template<class T>
|
||||
naRef to_nasal_helper(naContext c, const SGRect<T>& rect)
|
||||
{
|
||||
return to_nasal_helper(c, {
|
||||
static_cast<double>(rect.x()),
|
||||
static_cast<double>(rect.y()),
|
||||
static_cast<double>(rect.width()),
|
||||
static_cast<double>(rect.height())
|
||||
});
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
template<class T>
|
||||
naRef to_nasal_helper(naContext c, const SGRect<T>& rect)
|
||||
naRef detail::array_to_nasal(naContext c, const T* arr, size_t size)
|
||||
{
|
||||
std::vector<double> vec(4);
|
||||
vec[0] = rect.x();
|
||||
vec[1] = rect.y();
|
||||
vec[2] = rect.width();
|
||||
vec[3] = rect.height();
|
||||
|
||||
return to_nasal_helper(c, vec);
|
||||
naRef ret = naNewVector(c);
|
||||
naVec_setsize(c, ret, static_cast<int>(size));
|
||||
for(int i = 0; i < static_cast<int>(size); ++i)
|
||||
naVec_set(ret, i, to_nasal_helper(c, arr[i]));
|
||||
return ret;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -184,17 +211,6 @@ namespace nasal
|
||||
return hash;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
template<class T, size_t N>
|
||||
naRef to_nasal_helper(naContext c, const T(&array)[N])
|
||||
{
|
||||
naRef ret = naNewVector(c);
|
||||
naVec_setsize(c, ret, static_cast<int>(N));
|
||||
for(int i = 0; i < static_cast<int>(N); ++i)
|
||||
naVec_set(ret, i, to_nasal_helper(c, array[i]));
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace nasal
|
||||
|
||||
#endif /* SG_TO_NASAL_HELPER_HXX_ */
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#define SG_FROM_NASAL_HXX_
|
||||
|
||||
#include <simgear/nasal/cppbind/detail/from_nasal_helper.hxx>
|
||||
#include <type_traits>
|
||||
|
||||
namespace nasal
|
||||
{
|
||||
@@ -52,8 +53,8 @@ namespace nasal
|
||||
template<class Var>
|
||||
struct from_nasal_ptr
|
||||
{
|
||||
typedef typename boost::remove_const
|
||||
< typename boost::remove_reference<Var>::type
|
||||
typedef typename std::remove_const
|
||||
< typename std::remove_reference<Var>::type
|
||||
>::type return_type;
|
||||
typedef return_type(*type)(naContext, naRef);
|
||||
|
||||
|
||||
@@ -20,50 +20,29 @@
|
||||
#ifndef SG_NASAL_TESTCONTEXT_HXX_
|
||||
#define SG_NASAL_TESTCONTEXT_HXX_
|
||||
|
||||
#include <simgear/nasal/cppbind/NasalCallContext.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalContext.hxx>
|
||||
|
||||
class TestContext:
|
||||
public nasal::CallContext
|
||||
public nasal::Context
|
||||
{
|
||||
public:
|
||||
TestContext():
|
||||
CallContext(naNewContext(), naNil(), 0, 0)
|
||||
{}
|
||||
|
||||
~TestContext()
|
||||
{
|
||||
naFreeContext(c);
|
||||
}
|
||||
|
||||
void runGC()
|
||||
{
|
||||
naFreeContext(c);
|
||||
naFreeContext(_ctx);
|
||||
naGC();
|
||||
c = naNewContext();
|
||||
_ctx = naNewContext();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T from_str(const std::string& str)
|
||||
template<class T = naRef>
|
||||
T exec(const std::string& code, nasal::Me me)
|
||||
{
|
||||
return from_nasal<T>(to_nasal(str));
|
||||
return from_nasal<T>(execImpl(code, me));
|
||||
}
|
||||
|
||||
naRef exec(const std::string& code_str, nasal::Me me)
|
||||
{
|
||||
int err_line = -1;
|
||||
naRef code = naParseCode( c, to_nasal("<TextContext::exec>"), 0,
|
||||
(char*)code_str.c_str(), code_str.length(),
|
||||
&err_line );
|
||||
if( !naIsCode(code) )
|
||||
throw std::runtime_error("Failed to parse code: " + code_str);
|
||||
|
||||
return naCallMethod(code, me, 0, 0, naNil());
|
||||
}
|
||||
|
||||
template<class T>
|
||||
template<class T = naRef>
|
||||
T exec(const std::string& code)
|
||||
{
|
||||
return from_nasal<T>(exec(code, naNil()));
|
||||
return from_nasal<T>(execImpl(code, nasal::Me{}));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
@@ -71,6 +50,26 @@ class TestContext:
|
||||
{
|
||||
return from_nasal<T>(to_nasal(str));
|
||||
}
|
||||
|
||||
protected:
|
||||
naRef execImpl(const std::string& code_str, nasal::Me me)
|
||||
{
|
||||
int err_line = -1;
|
||||
naRef code = naParseCode( _ctx, to_nasal("<TextContext::exec>"), 0,
|
||||
(char*)code_str.c_str(), code_str.length(),
|
||||
&err_line );
|
||||
if( !naIsCode(code) )
|
||||
throw std::runtime_error("Failed to parse code: " + code_str);
|
||||
|
||||
naRef ret = naCallMethod(code, me, 0, 0, naNil());
|
||||
|
||||
if( char* err = naGetError(_ctx) )
|
||||
throw std::runtime_error(
|
||||
"Failed to executed code: " + std::string(err)
|
||||
);
|
||||
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* SG_NASAL_TESTCONTEXT_HXX_ */
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#define BOOST_TEST_MODULE cppbind
|
||||
#include <BoostTestTargetConfig.h>
|
||||
|
||||
#include "TestContext.hxx"
|
||||
|
||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalHash.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalString.hxx>
|
||||
@@ -115,44 +117,89 @@ naRef f_derivedGetX(const Derived& d, naContext c)
|
||||
}
|
||||
naRef f_freeFunction(nasal::CallContext c) { return c.requireArg<naRef>(0); }
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<class T, std::size_t N>
|
||||
ostream& operator<<(ostream& strm, const array<T, N>& vec)
|
||||
{
|
||||
for(auto const& v: vec)
|
||||
strm << "'" << v << "',";
|
||||
return strm;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( cppbind_arrays )
|
||||
{
|
||||
TestContext ctx;
|
||||
|
||||
naRef na_vec = ctx.to_nasal_vec(1., 2., 3.42);
|
||||
BOOST_REQUIRE( naIsVector(na_vec) );
|
||||
BOOST_CHECK_EQUAL(ctx.from_nasal<int>(naVec_get(na_vec, 0)), 1);
|
||||
BOOST_CHECK_EQUAL(ctx.from_nasal<int>(naVec_get(na_vec, 1)), 2);
|
||||
BOOST_CHECK_EQUAL(ctx.from_nasal<double>(naVec_get(na_vec, 2)), 3.42);
|
||||
|
||||
na_vec = ctx.to_nasal(std::initializer_list<double>({1., 2., 3.42}));
|
||||
BOOST_REQUIRE( naIsVector(na_vec) );
|
||||
BOOST_CHECK_EQUAL(ctx.from_nasal<int>(naVec_get(na_vec, 0)), 1);
|
||||
BOOST_CHECK_EQUAL(ctx.from_nasal<int>(naVec_get(na_vec, 1)), 2);
|
||||
BOOST_CHECK_EQUAL(ctx.from_nasal<double>(naVec_get(na_vec, 2)), 3.42);
|
||||
|
||||
using arr_d3_t = std::array<double, 3>;
|
||||
arr_d3_t std_arr = {1., 2., 3.42};
|
||||
na_vec = ctx.to_nasal(std_arr);
|
||||
BOOST_REQUIRE( naIsVector(na_vec) );
|
||||
BOOST_CHECK_EQUAL(ctx.from_nasal<int>(naVec_get(na_vec, 0)), 1);
|
||||
BOOST_CHECK_EQUAL(ctx.from_nasal<int>(naVec_get(na_vec, 1)), 2);
|
||||
BOOST_CHECK_EQUAL(ctx.from_nasal<double>(naVec_get(na_vec, 2)), 3.42);
|
||||
|
||||
double d_arr[] = {1., 2., 3.42};
|
||||
na_vec = ctx.to_nasal(d_arr);
|
||||
BOOST_REQUIRE( naIsVector(na_vec) );
|
||||
BOOST_CHECK_EQUAL(ctx.from_nasal<int>(naVec_get(na_vec, 0)), 1);
|
||||
BOOST_CHECK_EQUAL(ctx.from_nasal<int>(naVec_get(na_vec, 1)), 2);
|
||||
BOOST_CHECK_EQUAL(ctx.from_nasal<double>(naVec_get(na_vec, 2)), 3.42);
|
||||
|
||||
BOOST_CHECK_EQUAL(std_arr, ctx.from_nasal<arr_d3_t>(na_vec));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
|
||||
{
|
||||
naContext c = naNewContext();
|
||||
TestContext c;
|
||||
naRef r;
|
||||
|
||||
using namespace nasal;
|
||||
|
||||
r = to_nasal(c, ENUM_ANOTHER);
|
||||
BOOST_CHECK_EQUAL(from_nasal<int>(c, r), ENUM_ANOTHER);
|
||||
r = c.to_nasal(ENUM_ANOTHER);
|
||||
BOOST_CHECK_EQUAL(c.from_nasal<int>(r), ENUM_ANOTHER);
|
||||
|
||||
r = to_nasal(c, "Test");
|
||||
r = c.to_nasal("Test");
|
||||
BOOST_CHECK( strncmp("Test", naStr_data(r), naStr_len(r)) == 0 );
|
||||
BOOST_CHECK_EQUAL(from_nasal<std::string>(c, r), "Test");
|
||||
BOOST_CHECK_EQUAL(c.from_nasal<std::string>(r), "Test");
|
||||
|
||||
r = to_nasal(c, std::string("Test"));
|
||||
r = c.to_nasal(std::string("Test"));
|
||||
BOOST_CHECK( strncmp("Test", naStr_data(r), naStr_len(r)) == 0 );
|
||||
BOOST_CHECK_EQUAL(from_nasal<std::string>(c, r), "Test");
|
||||
BOOST_CHECK_EQUAL(c.from_nasal<std::string>(r), "Test");
|
||||
|
||||
r = to_nasal(c, 42);
|
||||
r = c.to_nasal(42);
|
||||
BOOST_CHECK_EQUAL(naNumValue(r).num, 42);
|
||||
BOOST_CHECK_EQUAL(from_nasal<int>(c, r), 42);
|
||||
BOOST_CHECK_EQUAL(c.from_nasal<int>(r), 42);
|
||||
|
||||
r = to_nasal(c, 4.2f);
|
||||
r = c.to_nasal(4.2f);
|
||||
BOOST_CHECK_EQUAL(naNumValue(r).num, 4.2f);
|
||||
BOOST_CHECK_EQUAL(from_nasal<float>(c, r), 4.2f);
|
||||
BOOST_CHECK_EQUAL(c.from_nasal<float>(r), 4.2f);
|
||||
|
||||
float test_data[3] = {0, 4, 2};
|
||||
r = to_nasal(c, test_data);
|
||||
r = c.to_nasal(test_data);
|
||||
|
||||
SGVec2f vec(0,2);
|
||||
r = to_nasal(c, vec);
|
||||
BOOST_CHECK_EQUAL(from_nasal<SGVec2f>(c, r), vec);
|
||||
r = c.to_nasal(vec);
|
||||
BOOST_CHECK_EQUAL(c.from_nasal<SGVec2f>(r), vec);
|
||||
|
||||
std::vector<int> std_vec;
|
||||
r = to_nasal(c, std_vec);
|
||||
r = c.to_nasal(std_vec);
|
||||
|
||||
r = to_nasal(c, "string");
|
||||
BOOST_CHECK_THROW(from_nasal<int>(c, r), bad_nasal_cast);
|
||||
r = c.to_nasal("string");
|
||||
BOOST_CHECK_THROW(c.from_nasal<int>(r), bad_nasal_cast);
|
||||
|
||||
Hash hash(c);
|
||||
hash.set("vec", r);
|
||||
@@ -171,10 +218,10 @@ BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
|
||||
it1 = it2;
|
||||
it3 = it2;
|
||||
|
||||
r = to_nasal(c, hash);
|
||||
r = c.to_nasal(hash);
|
||||
BOOST_REQUIRE( naIsHash(r) );
|
||||
|
||||
simgear::StringMap string_map = from_nasal<simgear::StringMap>(c, r);
|
||||
simgear::StringMap string_map = c.from_nasal<simgear::StringMap>(r);
|
||||
BOOST_CHECK_EQUAL(string_map.at("vec"), "string");
|
||||
BOOST_CHECK_EQUAL(string_map.at("name"), "my-name");
|
||||
BOOST_CHECK_EQUAL(string_map.at("string"), "blub");
|
||||
@@ -216,7 +263,7 @@ BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
|
||||
// passed on to function
|
||||
typedef boost::function<int (Me, int)> MeIntFunc;
|
||||
MeIntFunc fmeint = hash.get<MeIntFunc>("func");
|
||||
BOOST_CHECK_EQUAL(fmeint(naNil(), 5), 5);
|
||||
BOOST_CHECK_EQUAL(fmeint(Me{}, 5), 5);
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Test exposing classes to Nasal
|
||||
@@ -260,81 +307,81 @@ BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
|
||||
Ghost<SGWeakRefBasedPtr>::init("SGWeakRefBasedPtr");
|
||||
|
||||
SGWeakRefBasedPtr weak_ptr(new SGWeakReferenceBasedClass());
|
||||
naRef nasal_ref = to_nasal(c, weak_ptr),
|
||||
nasal_ptr = to_nasal(c, weak_ptr.get());
|
||||
naRef nasal_ref = c.to_nasal(weak_ptr),
|
||||
nasal_ptr = c.to_nasal(weak_ptr.get());
|
||||
|
||||
BOOST_REQUIRE( naIsGhost(nasal_ref) );
|
||||
BOOST_REQUIRE( naIsGhost(nasal_ptr) );
|
||||
|
||||
SGWeakRefBasedPtr ptr1 = from_nasal<SGWeakRefBasedPtr>(c, nasal_ref),
|
||||
ptr2 = from_nasal<SGWeakRefBasedPtr>(c, nasal_ptr);
|
||||
SGWeakRefBasedPtr ptr1 = c.from_nasal<SGWeakRefBasedPtr>(nasal_ref),
|
||||
ptr2 = c.from_nasal<SGWeakRefBasedPtr>(nasal_ptr);
|
||||
|
||||
BOOST_CHECK_EQUAL(weak_ptr, ptr1);
|
||||
BOOST_CHECK_EQUAL(weak_ptr, ptr2);
|
||||
|
||||
|
||||
BOOST_REQUIRE( Ghost<BasePtr>::isInit() );
|
||||
nasal::to_nasal(c, DoubleDerived2Ptr());
|
||||
c.to_nasal(DoubleDerived2Ptr());
|
||||
|
||||
BasePtr d( new Derived );
|
||||
naRef derived = to_nasal(c, d);
|
||||
naRef derived = c.to_nasal(d);
|
||||
BOOST_REQUIRE( naIsGhost(derived) );
|
||||
BOOST_CHECK_EQUAL( std::string("DerivedPtr"), naGhost_type(derived)->name );
|
||||
|
||||
// Get member function from ghost...
|
||||
naRef thisGetter = naNil();
|
||||
BOOST_CHECK( naMember_get(c, derived, to_nasal(c, "this"), &thisGetter) );
|
||||
BOOST_CHECK( naMember_get(c, derived, c.to_nasal("this"), &thisGetter) );
|
||||
BOOST_CHECK( naIsFunc(thisGetter) );
|
||||
|
||||
// ...and check if it really gets passed the correct instance
|
||||
typedef boost::function<unsigned long (Me)> MemFunc;
|
||||
MemFunc fGetThis = from_nasal<MemFunc>(c, thisGetter);
|
||||
MemFunc fGetThis = c.from_nasal<MemFunc>(thisGetter);
|
||||
BOOST_REQUIRE( fGetThis );
|
||||
BOOST_CHECK_EQUAL( fGetThis(derived), (unsigned long)d.get() );
|
||||
BOOST_CHECK_EQUAL( fGetThis(Me{derived}), (unsigned long)d.get() );
|
||||
|
||||
BasePtr d2( new DoubleDerived );
|
||||
derived = to_nasal(c, d2);
|
||||
derived = c.to_nasal(d2);
|
||||
BOOST_CHECK( naIsGhost(derived) );
|
||||
BOOST_CHECK_EQUAL( std::string("DoubleDerivedPtr"),
|
||||
naGhost_type(derived)->name );
|
||||
|
||||
BasePtr d3( new DoubleDerived2 );
|
||||
derived = to_nasal(c, d3);
|
||||
derived = c.to_nasal(d3);
|
||||
BOOST_CHECK( naIsGhost(derived) );
|
||||
BOOST_CHECK_EQUAL( std::string("DoubleDerived2Ptr"),
|
||||
naGhost_type(derived)->name );
|
||||
|
||||
SGRefBasedPtr ref_based( new SGReferenceBasedClass );
|
||||
naRef na_ref_based = to_nasal(c, ref_based.get());
|
||||
naRef na_ref_based = c.to_nasal(ref_based.get());
|
||||
BOOST_CHECK( naIsGhost(na_ref_based) );
|
||||
BOOST_CHECK_EQUAL( from_nasal<SGReferenceBasedClass*>(c, na_ref_based),
|
||||
BOOST_CHECK_EQUAL( c.from_nasal<SGReferenceBasedClass*>(na_ref_based),
|
||||
ref_based.get() );
|
||||
BOOST_CHECK_EQUAL( from_nasal<SGRefBasedPtr>(c, na_ref_based), ref_based );
|
||||
BOOST_CHECK_EQUAL( c.from_nasal<SGRefBasedPtr>(na_ref_based), ref_based );
|
||||
|
||||
BOOST_CHECK_EQUAL( from_nasal<BasePtr>(c, derived), d3 );
|
||||
BOOST_CHECK_NE( from_nasal<BasePtr>(c, derived), d2 );
|
||||
BOOST_CHECK_EQUAL( from_nasal<DerivedPtr>(c, derived),
|
||||
BOOST_CHECK_EQUAL( c.from_nasal<BasePtr>(derived), d3 );
|
||||
BOOST_CHECK_NE( c.from_nasal<BasePtr>(derived), d2 );
|
||||
BOOST_CHECK_EQUAL( c.from_nasal<DerivedPtr>(derived),
|
||||
boost::dynamic_pointer_cast<Derived>(d3) );
|
||||
BOOST_CHECK_EQUAL( from_nasal<DoubleDerived2Ptr>(c, derived),
|
||||
BOOST_CHECK_EQUAL( c.from_nasal<DoubleDerived2Ptr>(derived),
|
||||
boost::dynamic_pointer_cast<DoubleDerived2>(d3) );
|
||||
BOOST_CHECK_THROW( from_nasal<DoubleDerivedPtr>(c, derived), bad_nasal_cast );
|
||||
BOOST_CHECK_THROW( c.from_nasal<DoubleDerivedPtr>(derived), bad_nasal_cast );
|
||||
|
||||
std::map<std::string, BasePtr> instances;
|
||||
BOOST_CHECK( naIsHash(to_nasal(c, instances)) );
|
||||
BOOST_CHECK( naIsHash(c.to_nasal(instances)) );
|
||||
|
||||
std::map<std::string, DerivedPtr> instances_d;
|
||||
BOOST_CHECK( naIsHash(to_nasal(c, instances_d)) );
|
||||
BOOST_CHECK( naIsHash(c.to_nasal(instances_d)) );
|
||||
|
||||
std::map<std::string, int> int_map;
|
||||
BOOST_CHECK( naIsHash(to_nasal(c, int_map)) );
|
||||
BOOST_CHECK( naIsHash(c.to_nasal(int_map)) );
|
||||
|
||||
std::map<std::string, std::vector<int> > int_vector_map;
|
||||
BOOST_CHECK( naIsHash(to_nasal(c, int_vector_map)) );
|
||||
BOOST_CHECK( naIsHash(c.to_nasal(int_vector_map)) );
|
||||
|
||||
simgear::StringMap dict =
|
||||
simgear::StringMap("hello", "value")
|
||||
("key2", "value2");
|
||||
naRef na_dict = to_nasal(c, dict);
|
||||
naRef na_dict = c.to_nasal(dict);
|
||||
BOOST_REQUIRE( naIsHash(na_dict) );
|
||||
BOOST_CHECK_EQUAL( Hash(na_dict, c).get<std::string>("key2"), "value2" );
|
||||
|
||||
@@ -346,65 +393,44 @@ BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
|
||||
|
||||
Hash obj(c);
|
||||
obj.set("parents", parents);
|
||||
BOOST_CHECK_EQUAL( from_nasal<BasePtr>(c, obj.get_naRef()), d3 );
|
||||
BOOST_CHECK_EQUAL( c.from_nasal<BasePtr>(obj.get_naRef()), d3 );
|
||||
|
||||
// Check recursive parents (aka parent-of-parent)
|
||||
std::vector<naRef> parents2;
|
||||
parents2.push_back(obj.get_naRef());
|
||||
Hash derived_obj(c);
|
||||
derived_obj.set("parents", parents2);
|
||||
BOOST_CHECK_EQUAL( from_nasal<BasePtr>(c, derived_obj.get_naRef()), d3 );
|
||||
BOOST_CHECK_EQUAL( c.from_nasal<BasePtr>(derived_obj.get_naRef()), d3 );
|
||||
|
||||
std::vector<naRef> nasal_objects;
|
||||
nasal_objects.push_back( Ghost<BasePtr>::makeGhost(c, d) );
|
||||
nasal_objects.push_back( Ghost<BasePtr>::makeGhost(c, d2) );
|
||||
nasal_objects.push_back( Ghost<BasePtr>::makeGhost(c, d3) );
|
||||
naRef obj_vec = to_nasal(c, nasal_objects);
|
||||
naRef obj_vec = c.to_nasal(nasal_objects);
|
||||
|
||||
std::vector<BasePtr> objects = from_nasal<std::vector<BasePtr> >(c, obj_vec);
|
||||
std::vector<BasePtr> objects = c.from_nasal<std::vector<BasePtr> >(obj_vec);
|
||||
BOOST_CHECK_EQUAL( objects[0], d );
|
||||
BOOST_CHECK_EQUAL( objects[1], d2 );
|
||||
BOOST_CHECK_EQUAL( objects[2], d3 );
|
||||
|
||||
{
|
||||
// Calling fallback setter for unset values
|
||||
const char* src_code = "me.test = 3;";
|
||||
int errLine = -1;
|
||||
naRef code = naParseCode( c, to_nasal(c, "source.nas"), 0,
|
||||
(char*)src_code, strlen(src_code),
|
||||
&errLine );
|
||||
ret = naCallMethod(code, derived, 0, 0, naNil());
|
||||
// Calling fallback setter for unset values
|
||||
BOOST_CHECK_EQUAL( c.exec<int>("me.test = 3;", Me{derived}), 3 );
|
||||
|
||||
BOOST_REQUIRE( !naGetError(c) ); // TODO real error check (this seems to
|
||||
// always return 0...
|
||||
BOOST_CHECK_EQUAL( from_nasal<int>(c, ret), 3 );
|
||||
}
|
||||
{
|
||||
// Calling generic (fallback) getter
|
||||
const char* src_code = "var a = me.get_test;";
|
||||
int errLine = -1;
|
||||
naRef code = naParseCode( c, to_nasal(c, "source.nas"), 0,
|
||||
(char*)src_code, strlen(src_code),
|
||||
&errLine );
|
||||
ret = naCallMethod(code, derived, 0, 0, naNil());
|
||||
|
||||
BOOST_REQUIRE( !naGetError(c) ); // TODO real error check (this seems to
|
||||
// always return 0...
|
||||
BOOST_CHECK_EQUAL( from_nasal<std::string>(c, ret), "generic-get" );
|
||||
}
|
||||
// Calling generic (fallback) getter
|
||||
BOOST_CHECK_EQUAL( c.exec<std::string>("var a = me.get_test;", Me{derived}),
|
||||
"generic-get" );
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Test nasal::CallContext
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
int int_vec[] = {1,2,3};
|
||||
std::map<std::string, std::string> map;
|
||||
naRef args[] = {
|
||||
to_nasal(c, std::string("test-arg")),
|
||||
to_nasal(c, 4),
|
||||
to_nasal(c, int_vec),
|
||||
to_nasal(c, map)
|
||||
c.to_nasal(std::string("test-arg")),
|
||||
c.to_nasal(4),
|
||||
c.to_nasal(int_vec),
|
||||
c.to_nasal(map)
|
||||
};
|
||||
CallContext cc(c, naNil(), sizeof(args)/sizeof(args[0]), args);
|
||||
BOOST_CHECK_EQUAL( cc.requireArg<std::string>(0), "test-arg" );
|
||||
@@ -419,15 +445,15 @@ BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
|
||||
BOOST_CHECK( cc.isVector(2) );
|
||||
BOOST_CHECK( cc.isHash(3) );
|
||||
|
||||
naRef args_vec = nasal::to_nasal(c, args);
|
||||
naRef args_vec = c.to_nasal(args);
|
||||
BOOST_CHECK( naIsVector(args_vec) );
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Test nasal::String
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
String string( to_nasal(c, "Test") );
|
||||
BOOST_CHECK_EQUAL( from_nasal<std::string>(c, string.get_naRef()), "Test" );
|
||||
String string( c.to_nasal("Test") );
|
||||
BOOST_CHECK_EQUAL( c.from_nasal<std::string>(string.get_naRef()), "Test" );
|
||||
BOOST_CHECK_EQUAL( string.c_str(), std::string("Test") );
|
||||
BOOST_CHECK( string.starts_with(string) );
|
||||
BOOST_CHECK( string.starts_with(String(c, "T")) );
|
||||
@@ -453,6 +479,16 @@ BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
|
||||
BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "Tse"), 2), 3 );
|
||||
BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "abc")), 0 );
|
||||
BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "abc"), 20), String::npos );
|
||||
|
||||
naFreeContext(c);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( cppbind_context )
|
||||
{
|
||||
nasal::Context ctx;
|
||||
naRef vec = ctx.to_nasal_vec(1, 2, 3.4, "test");
|
||||
BOOST_REQUIRE( naIsVector(vec) );
|
||||
|
||||
BOOST_CHECK_EQUAL(ctx.from_nasal<int>(naVec_get(vec, 0)), 1);
|
||||
BOOST_CHECK_EQUAL(ctx.from_nasal<int>(naVec_get(vec, 1)), 2);
|
||||
BOOST_CHECK_EQUAL(ctx.from_nasal<double>(naVec_get(vec, 2)), 3.4);
|
||||
BOOST_CHECK_EQUAL(ctx.from_nasal<std::string>(naVec_get(vec, 3)), "test");
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#define BOOST_TEST_MODULE cppbind
|
||||
#include <BoostTestTargetConfig.h>
|
||||
|
||||
#include "TestContext.hxx"
|
||||
|
||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalContext.hxx>
|
||||
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/weak_ptr.hpp>
|
||||
|
||||
@@ -186,3 +189,44 @@ BOOST_AUTO_TEST_CASE( storage_traits )
|
||||
|
||||
nasal::shared_ptr_storage<DerivedWeakPtr>::unref(d_weak);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( bind_methods )
|
||||
{
|
||||
struct TestClass
|
||||
{
|
||||
int arg1;
|
||||
std::string arg2;
|
||||
std::string arg3;
|
||||
int arg4;
|
||||
|
||||
void set(int a1, const std::string& a2, const std::string& a3, int a4)
|
||||
{
|
||||
arg1 = a1;
|
||||
arg2 = a2;
|
||||
arg3 = a3;
|
||||
arg4 = a4;
|
||||
}
|
||||
};
|
||||
using TestClassPtr = boost::shared_ptr<TestClass>;
|
||||
auto set_func = boost::function<
|
||||
void (TestClass&, int, const std::string&, const std::string&, int)
|
||||
>(&TestClass::set);
|
||||
nasal::Ghost<TestClassPtr>::init("TestClass")
|
||||
.method("set", set_func)
|
||||
.method("setReverse", set_func, std::index_sequence<3,2,1,0>{});
|
||||
|
||||
TestContext ctx;
|
||||
auto test = boost::make_shared<TestClass>();
|
||||
|
||||
ctx.exec("me.set(1, \"s2\", \"s3\", 4);", ctx.to_me(test));
|
||||
BOOST_CHECK_EQUAL(test->arg1, 1);
|
||||
BOOST_CHECK_EQUAL(test->arg2, "s2");
|
||||
BOOST_CHECK_EQUAL(test->arg3, "s3");
|
||||
BOOST_CHECK_EQUAL(test->arg4, 4);
|
||||
|
||||
ctx.exec("me.setReverse(1, \"s2\", \"s3\", 4);", ctx.to_me(test));
|
||||
BOOST_CHECK_EQUAL(test->arg1, 4);
|
||||
BOOST_CHECK_EQUAL(test->arg2, "s3");
|
||||
BOOST_CHECK_EQUAL(test->arg3, "s2");
|
||||
BOOST_CHECK_EQUAL(test->arg4, 1);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
#include <BoostTestTargetConfig.h>
|
||||
|
||||
#include "TestContext.hxx"
|
||||
|
||||
#include <simgear/nasal/cppbind/NasalObjectHolder.hxx>
|
||||
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
|
||||
@@ -22,7 +25,7 @@ static naGhostType ghost_type = {
|
||||
static naRef createTestGhost(TestContext& c, intptr_t p)
|
||||
{
|
||||
active_instances.insert(p);
|
||||
return naNewGhost(c.c, &ghost_type, (void*)p);
|
||||
return naNewGhost(c, &ghost_type, (void*)p);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -91,3 +94,47 @@ BOOST_AUTO_TEST_CASE( ghost_gc )
|
||||
|
||||
BOOST_REQUIRE(active_instances.empty());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
BOOST_AUTO_TEST_CASE( object_holder_gc )
|
||||
{
|
||||
TestContext c;
|
||||
BOOST_REQUIRE_EQUAL(naNumSaved(), 0);
|
||||
BOOST_REQUIRE(active_instances.empty());
|
||||
|
||||
//-----------------------------------------------
|
||||
// Put some ghosts in ObjectHolder and check if
|
||||
// they are saved from gc
|
||||
|
||||
naRef g1 = createTestGhost(c, 1),
|
||||
g2 = createTestGhost(c, 2);
|
||||
|
||||
nasal::ObjectHolder<> h1(g1);
|
||||
BOOST_CHECK_EQUAL(naNumSaved(), 1);
|
||||
BOOST_CHECK(naIsGhost(h1.get_naRef()));
|
||||
|
||||
nasal::ObjectHolder<> h2(g2);
|
||||
BOOST_CHECK_EQUAL(naNumSaved(), 2);
|
||||
BOOST_CHECK(naIsGhost(h2.get_naRef()));
|
||||
|
||||
c.runGC();
|
||||
|
||||
BOOST_CHECK_EQUAL(active_instances.size(), 2);
|
||||
BOOST_CHECK_EQUAL(naNumSaved(), 2);
|
||||
|
||||
h1.reset(naNum(1));
|
||||
h2.reset(naNum(2));
|
||||
BOOST_CHECK_EQUAL(naNumSaved(), 2);
|
||||
|
||||
//-----------------------------------------------
|
||||
// Check that the saved objects are released
|
||||
|
||||
h1.reset();
|
||||
BOOST_CHECK_EQUAL(naNumSaved(), 1);
|
||||
|
||||
h2.reset();
|
||||
BOOST_CHECK_EQUAL(naNumSaved(), 0);
|
||||
|
||||
c.runGC();
|
||||
BOOST_CHECK_EQUAL(active_instances.size(), 0);
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ static naRef f_fmod(naContext c, naRef me, int argc, naRef* args)
|
||||
naRef b = naNumValue(argc > 1 ? args[1] : naNil());
|
||||
if(naIsNil(a) || naIsNil(b))
|
||||
naRuntimeError(c, "non numeric arguments to fmod()");
|
||||
|
||||
|
||||
a.num = fmod(a.num, b.num);
|
||||
return VALIDATE(a);
|
||||
}
|
||||
@@ -119,7 +119,7 @@ static naRef f_clamp(naContext c, naRef me, int argc, naRef* args)
|
||||
naRef a = naNumValue(argc > 0 ? args[0] : naNil());
|
||||
naRef min = naNumValue(argc > 1 ? args[1] : naNil());
|
||||
naRef max = naNumValue(argc > 2 ? args[2] : naNil());
|
||||
|
||||
|
||||
if(naIsNil(a) || naIsNil(min) || naIsNil(max))
|
||||
naRuntimeError(c, "non numeric arguments to clamp()");
|
||||
|
||||
@@ -133,10 +133,10 @@ static naRef f_periodic(naContext c, naRef me, int argc, naRef* args)
|
||||
naRef a = naNumValue(argc > 0 ? args[0] : naNil());
|
||||
naRef b = naNumValue(argc > 1 ? args[1] : naNil());
|
||||
naRef x = naNumValue(argc > 2 ? args[2] : naNil());
|
||||
|
||||
|
||||
if(naIsNil(a) || naIsNil(b) || naIsNil(x))
|
||||
naRuntimeError(c, "non numeric arguments to periodic()");
|
||||
|
||||
|
||||
range = b.num - a.num;
|
||||
x.num = x.num - range*floor((x.num - a.num)/range);
|
||||
// two security checks that can only happen due to roundoff
|
||||
@@ -145,7 +145,7 @@ static naRef f_periodic(naContext c, naRef me, int argc, naRef* args)
|
||||
if (b.num <= x.num)
|
||||
x.num = b.num;
|
||||
return VALIDATE(x);
|
||||
|
||||
|
||||
// x.num = SGMiscd::normalizePeriodic(a, b, x);
|
||||
return VALIDATE(x);
|
||||
}
|
||||
@@ -156,7 +156,7 @@ static naRef f_round(naContext c, naRef me, int argc, naRef* args)
|
||||
naRef b = naNumValue(argc > 1 ? args[1] : naNil());
|
||||
#ifdef _MSC_VER
|
||||
double x,y;
|
||||
#endif
|
||||
#endif
|
||||
if(naIsNil(a))
|
||||
naRuntimeError(c, "non numeric arguments to round()");
|
||||
if (naIsNil(b))
|
||||
@@ -169,21 +169,31 @@ static naRef f_round(naContext c, naRef me, int argc, naRef* args)
|
||||
double x = round(a.num / b.num);
|
||||
#endif
|
||||
a.num = x * b.num;
|
||||
|
||||
|
||||
return VALIDATE(a);
|
||||
}
|
||||
|
||||
|
||||
static naRef f_tan(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
naRef a = naNumValue(argc > 0 ? args[0] : naNil());
|
||||
naRef a = naNumValue(argc > 0 ? args[0] : naNil());
|
||||
if(naIsNil(a))
|
||||
naRuntimeError(c, "non numeric arguments to tan()");
|
||||
|
||||
|
||||
a.num = tan(a.num);
|
||||
return VALIDATE(a);
|
||||
}
|
||||
|
||||
static naRef f_atan(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
naRef a = naNumValue(argc > 0 ? args[0] : naNil());
|
||||
if(naIsNil(a))
|
||||
naRuntimeError(c, "non numeric arguments to tan()");
|
||||
|
||||
a.num = atan(a.num);
|
||||
return VALIDATE(a);
|
||||
}
|
||||
|
||||
static naRef f_asin(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
naRef a = naNumValue(argc > 0 ? args[0] : naNil());
|
||||
@@ -210,15 +220,16 @@ static naCFuncItem funcs[] = {
|
||||
{ "pow", f_pow },
|
||||
{ "sqrt", f_sqrt },
|
||||
{ "atan2", f_atan2 },
|
||||
{ "atan", f_atan },
|
||||
{ "floor", f_floor },
|
||||
{ "ceil", f_ceil },
|
||||
{ "fmod", f_fmod },
|
||||
{ "clamp", f_clamp },
|
||||
{ "periodic", f_periodic },
|
||||
{ "round", f_round },
|
||||
{ "tan", f_tan },
|
||||
{ "periodic", f_periodic },
|
||||
{ "round", f_round },
|
||||
{ "tan", f_tan },
|
||||
{ "acos", f_acos },
|
||||
{ "asin", f_asin },
|
||||
{ "asin", f_asin },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
///@file
|
||||
/// The Nasal scripting language
|
||||
///
|
||||
#ifndef _NASAL_H
|
||||
#define _NASAL_H
|
||||
#ifdef __cplusplus
|
||||
@@ -15,12 +18,14 @@ extern "C" {
|
||||
#define GCC_PURE
|
||||
#endif
|
||||
|
||||
/** Nasal context pointer */
|
||||
typedef struct Context* naContext;
|
||||
|
||||
// The function signature for an extension function:
|
||||
/** Function signature for an extension function */
|
||||
typedef naRef (*naCFunction)(naContext ctx, naRef me, int argc, naRef* args);
|
||||
|
||||
// The function signature for an extension function with userdata passed back:
|
||||
/** Function signature for an extension function with @p user_data passed back
|
||||
*/
|
||||
typedef naRef (*naCFunctionU)
|
||||
(naContext ctx, naRef me, int argc, naRef* args, void* user_data);
|
||||
|
||||
@@ -58,6 +63,10 @@ int naGCSave(naRef obj);
|
||||
// by the garbage collector.
|
||||
void naGCRelease(int key);
|
||||
|
||||
// Get the number of currently saved and not yet again released objects
|
||||
// (saved by naSave or naGCSave)
|
||||
int naNumSaved();
|
||||
|
||||
// Drop all saved references
|
||||
void naClearSaved();
|
||||
|
||||
@@ -254,7 +263,7 @@ void naHash_set(naRef hash, naRef key, naRef val);
|
||||
void naHash_cset(naRef hash, char* key, naRef val);
|
||||
void naHash_delete(naRef hash, naRef key);
|
||||
/**
|
||||
* Store the keys in ::hash into the vector at ::dst
|
||||
* Store the keys in @p hash into the vector at @p dst
|
||||
*
|
||||
* @see ::naNewVector
|
||||
*/
|
||||
@@ -269,25 +278,25 @@ typedef struct naGhostType {
|
||||
} naGhostType;
|
||||
|
||||
/**
|
||||
* Create a ghost for an object without any attributes. If ::t contains pointers
|
||||
* to get_member or set_member function they will be ignored.
|
||||
* Create a ghost for an object without any attributes. If @p t contains
|
||||
* pointers to get_member or set_member function they will be ignored.
|
||||
*/
|
||||
naRef naNewGhost(naContext c, naGhostType* t, void* ghost);
|
||||
/**
|
||||
* Create a ghost for an object. This version uses the get_member and set_member
|
||||
* function pointers in ::t upon trying to get or set a member respectively from
|
||||
* Nasal.
|
||||
* function pointers in @p t upon trying to get or set a member respectively
|
||||
* from Nasal.
|
||||
*/
|
||||
naRef naNewGhost2(naContext c, naGhostType* t, void* ghost);
|
||||
naGhostType* naGhost_type(naRef ghost);
|
||||
void* naGhost_ptr(naRef ghost);
|
||||
/**
|
||||
* Attach a nasal object to the given ghost. Binds the lifetime of @a data to
|
||||
* the lifetime of the @a ghost.
|
||||
* Attach a nasal object to the given ghost. Binds the lifetime of @p data to
|
||||
* the lifetime of the @p ghost.
|
||||
*/
|
||||
void naGhost_setData(naRef ghost, naRef data);
|
||||
/**
|
||||
* Retrieve the object attached to the @a ghost, previously set with
|
||||
* Retrieve the object attached to the @p ghost, previously set with
|
||||
* naGhost_setData().
|
||||
*/
|
||||
naRef naGhost_data(naRef ghost);
|
||||
|
||||
@@ -151,25 +151,36 @@ public:
|
||||
std::string u = dl->realUrl();
|
||||
if (status == Delegate::STATUS_SUCCESS) {
|
||||
thumbnailCache[u].requestPending = false;
|
||||
fireDataForThumbnail(u, reinterpret_cast<const uint8_t*>(bytes.data()), bytes.size());
|
||||
|
||||
// if this was a network load, rather than a re-load from the disk cache,
|
||||
// then persist to disk now.
|
||||
if (strutils::starts_with(request->url(), "http")) {
|
||||
addToPersistentCache(u, bytes);
|
||||
}
|
||||
|
||||
fireDataForThumbnail(u, reinterpret_cast<const uint8_t*>(bytes.data()), bytes.size());
|
||||
} else if (status == Delegate::FAIL_HTTP_FORBIDDEN) {
|
||||
// treat this as rate-limiting failure, at least from some mirrors
|
||||
// (eg Ibiblio) and retry up to the max count
|
||||
const int retries = (thumbnailCache[u].retryCount++);
|
||||
if (retries < 3) {
|
||||
SG_LOG(SG_IO, SG_INFO, "Download failed for: " << u << ", will retry");
|
||||
SG_LOG(SG_IO, SG_DEBUG, "Download failed for: " << u << ", will retry");
|
||||
thumbnailCache[u].requestPending = true;
|
||||
pendingThumbnails.push_back(u);
|
||||
}
|
||||
} else {
|
||||
// any other failure.
|
||||
thumbnailCache[u].requestPending = false;
|
||||
|
||||
// if this was a cache refresh, let's report the cached data instead
|
||||
SGPath cachePath = pathInCache(u);
|
||||
if (cachePath.exists()) {
|
||||
SG_LOG(SG_IO, SG_WARN, "Download failed for: " << u << ", will use old cached data");
|
||||
cachePath.touch(); // touch the file so we don't repeat this danxce
|
||||
// kick a load from the cache
|
||||
|
||||
queueLoadFromPersistentCache(u, cachePath);
|
||||
}
|
||||
}
|
||||
|
||||
downloadNextPendingThumbnail();
|
||||
@@ -207,40 +218,45 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void addToPersistentCache(const std::string& url, const std::string& imageBytes)
|
||||
SGPath pathInCache(const std::string& url) const
|
||||
{
|
||||
std::string hash = hashForUrl(url);
|
||||
const auto hash = hashForUrl(url);
|
||||
// append the correct file suffix
|
||||
auto pos = url.rfind('.');
|
||||
if (pos == std::string::npos) {
|
||||
return;
|
||||
return SGPath();
|
||||
}
|
||||
|
||||
SGPath cachePath = path / "ThumbnailCache" / (hash + url.substr(pos));
|
||||
return path / "ThumbnailCache" / (hash + url.substr(pos));
|
||||
}
|
||||
|
||||
void addToPersistentCache(const std::string& url, const std::string& imageBytes)
|
||||
{
|
||||
// this will over-write the existing file if we are refreshing,
|
||||
// since we use 'truncatr' to open the new file
|
||||
SGPath cachePath = pathInCache(url);
|
||||
sg_ofstream fstream(cachePath, std::ios::out | std::ios::trunc | std::ios::binary);
|
||||
fstream.write(imageBytes.data(), imageBytes.size());
|
||||
fstream.close();
|
||||
|
||||
auto it = thumbnailCache.find(url);
|
||||
assert(it != thumbnailCache.end());
|
||||
it->second.pathOnDisk = cachePath;
|
||||
}
|
||||
|
||||
bool checkPersistentCache(const std::string& url)
|
||||
{
|
||||
std::string hash = hashForUrl(url);
|
||||
// append the correct file suffix
|
||||
auto pos = url.rfind('.');
|
||||
if (pos == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SGPath cachePath = path / "ThumbnailCache" / (hash + url.substr(pos));
|
||||
SGPath cachePath = pathInCache(url);
|
||||
if (!cachePath.exists()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check age, if it's too old, expire and download again
|
||||
int age = time(nullptr) - cachePath.modTime();
|
||||
if (age > SECONDS_PER_DAY * 7) { // cache for seven days
|
||||
SG_LOG(SG_IO, SG_INFO, "expiring old cached thumbnail " << url);
|
||||
cachePath.remove();
|
||||
const int cacheMaxAge = SECONDS_PER_DAY * 7;
|
||||
if (age > cacheMaxAge) { // cache for seven days
|
||||
// note we do *not* remove the file data here, since the
|
||||
// cache refresh might fail
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -258,7 +274,7 @@ public:
|
||||
entry.pathOnDisk = path;
|
||||
it = thumbnailCache.insert(it, std::make_pair(url, entry));
|
||||
} else {
|
||||
assert(it->second.pathOnDisk == path);
|
||||
assert(it->second.pathOnDisk.isNull() || (it->second.pathOnDisk == path));
|
||||
}
|
||||
|
||||
if (it->second.requestPending) {
|
||||
@@ -549,12 +565,10 @@ void Root::addDelegate(simgear::pkg::Delegate *aDelegate)
|
||||
|
||||
void Root::removeDelegate(simgear::pkg::Delegate *aDelegate)
|
||||
{
|
||||
DelegateVec::iterator it = std::find(d->delegates.begin(),
|
||||
d->delegates.end(), aDelegate);
|
||||
if (it == d->delegates.end()) {
|
||||
throw sg_exception("unknown delegate in removeDelegate");
|
||||
auto it = std::find(d->delegates.begin(), d->delegates.end(), aDelegate);
|
||||
if (it != d->delegates.end()) {
|
||||
d->delegates.erase(it);
|
||||
}
|
||||
d->delegates.erase(it);
|
||||
}
|
||||
|
||||
void Root::setLocale(const std::string& aLocale)
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "PropertyBasedElement.hxx"
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
@@ -130,7 +130,7 @@ namespace simgear
|
||||
// character that followed it by the same character converted to ASCII
|
||||
// uppercase.
|
||||
|
||||
if( !boost::starts_with(name, DATA_PREFIX) )
|
||||
if( !strutils::starts_with(name, DATA_PREFIX) )
|
||||
return std::string();
|
||||
|
||||
std::string data_name;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user