Compare commits

..

2 Commits

Author SHA1 Message Date
Automatic Release Builder
e084bd89e7 new version: 2017.1.2 2017-03-01 20:40:03 +01:00
James Turner
cfe9ddc2de Fix issues with package upgrades on Windows. 2017-02-23 13:27:02 +00:00
348 changed files with 7415 additions and 21430 deletions

1
.gitignore vendored
View File

@@ -14,4 +14,3 @@ CTestTestfile.cmake
install_manifest.txt
build*
Build
CMakeLists.txt.user

View File

@@ -2,6 +2,8 @@ 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 Normal file
View File

@@ -0,0 +1,17 @@
include (SimGearComponent)
set(HEADERS
source/utf8.h
)
set(HEADERS_utf8
source/utf8/checked.h
source/utf8/core.h
source/utf8/unchecked.h
)
set(SOURCES
)
simgear_component(utf8 3rdparty/utf8 "${SOURCES}" "${HEADERS}")
simgear_component(utf8-internal 3rdparty/utf8/utf8 "" "${HEADERS_utf8}")

12
3rdparty/utf8/doc/ReleaseNotes vendored Normal file
View File

@@ -0,0 +1,12 @@
utf8 cpp library
Release 2.3.4
A minor bug fix release. Thanks to all who reported bugs.
Note: Version 2.3.3 contained a regression, and therefore was removed.
Changes from version 2.3.2
- Bug fix [39]: checked.h Line 273 and unchecked.h Line 182 have an extra ';'
- Bug fix [36]: replace_invalid() only works with back_inserter
Files included in the release: utf8.h, core.h, checked.h, unchecked.h, utf8cpp.html, ReleaseNotes

1789
3rdparty/utf8/doc/utf8cpp.html vendored Normal file

File diff suppressed because it is too large Load Diff

34
3rdparty/utf8/source/utf8.h vendored Normal file
View File

@@ -0,0 +1,34 @@
// Copyright 2006 Nemanja Trifunovic
/*
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#ifndef UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731
#define UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731
#include "utf8/checked.h"
#include "utf8/unchecked.h"
#endif // header guard

327
3rdparty/utf8/source/utf8/checked.h vendored Normal file
View File

@@ -0,0 +1,327 @@
// Copyright 2006 Nemanja Trifunovic
/*
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
#define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
#include "core.h"
#include <stdexcept>
namespace utf8
{
// Base for the exceptions that may be thrown from the library
class exception : public ::std::exception {
};
// Exceptions that may be thrown from the library functions.
class invalid_code_point : public exception {
uint32_t cp;
public:
invalid_code_point(uint32_t cp) : cp(cp) {}
virtual const char* what() const throw() { return "Invalid code point"; }
uint32_t code_point() const {return cp;}
};
class invalid_utf8 : public exception {
uint8_t u8;
public:
invalid_utf8 (uint8_t u) : u8(u) {}
virtual const char* what() const throw() { return "Invalid UTF-8"; }
uint8_t utf8_octet() const {return u8;}
};
class invalid_utf16 : public exception {
uint16_t u16;
public:
invalid_utf16 (uint16_t u) : u16(u) {}
virtual const char* what() const throw() { return "Invalid UTF-16"; }
uint16_t utf16_word() const {return u16;}
};
class not_enough_room : public exception {
public:
virtual const char* what() const throw() { return "Not enough space"; }
};
/// The library API - functions intended to be called by the users
template <typename octet_iterator>
octet_iterator append(uint32_t cp, octet_iterator result)
{
if (!utf8::internal::is_code_point_valid(cp))
throw invalid_code_point(cp);
if (cp < 0x80) // one octet
*(result++) = static_cast<uint8_t>(cp);
else if (cp < 0x800) { // two octets
*(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0);
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
}
else if (cp < 0x10000) { // three octets
*(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0);
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
}
else { // four octets
*(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0);
*(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f) | 0x80);
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
}
return result;
}
template <typename octet_iterator, typename output_iterator>
output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement)
{
while (start != end) {
octet_iterator sequence_start = start;
internal::utf_error err_code = utf8::internal::validate_next(start, end);
switch (err_code) {
case internal::UTF8_OK :
for (octet_iterator it = sequence_start; it != start; ++it)
*out++ = *it;
break;
case internal::NOT_ENOUGH_ROOM:
throw not_enough_room();
case internal::INVALID_LEAD:
out = utf8::append (replacement, out);
++start;
break;
case internal::INCOMPLETE_SEQUENCE:
case internal::OVERLONG_SEQUENCE:
case internal::INVALID_CODE_POINT:
out = utf8::append (replacement, out);
++start;
// just one replacement mark for the sequence
while (start != end && utf8::internal::is_trail(*start))
++start;
break;
}
}
return out;
}
template <typename octet_iterator, typename output_iterator>
inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out)
{
static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd);
return utf8::replace_invalid(start, end, out, replacement_marker);
}
template <typename octet_iterator>
uint32_t next(octet_iterator& it, octet_iterator end)
{
uint32_t cp = 0;
internal::utf_error err_code = utf8::internal::validate_next(it, end, cp);
switch (err_code) {
case internal::UTF8_OK :
break;
case internal::NOT_ENOUGH_ROOM :
throw not_enough_room();
case internal::INVALID_LEAD :
case internal::INCOMPLETE_SEQUENCE :
case internal::OVERLONG_SEQUENCE :
throw invalid_utf8(*it);
case internal::INVALID_CODE_POINT :
throw invalid_code_point(cp);
}
return cp;
}
template <typename octet_iterator>
uint32_t peek_next(octet_iterator it, octet_iterator end)
{
return utf8::next(it, end);
}
template <typename octet_iterator>
uint32_t prior(octet_iterator& it, octet_iterator start)
{
// can't do much if it == start
if (it == start)
throw not_enough_room();
octet_iterator end = it;
// Go back until we hit either a lead octet or start
while (utf8::internal::is_trail(*(--it)))
if (it == start)
throw invalid_utf8(*it); // error - no lead byte in the sequence
return utf8::peek_next(it, end);
}
/// Deprecated in versions that include "prior"
template <typename octet_iterator>
uint32_t previous(octet_iterator& it, octet_iterator pass_start)
{
octet_iterator end = it;
while (utf8::internal::is_trail(*(--it)))
if (it == pass_start)
throw invalid_utf8(*it); // error - no lead byte in the sequence
octet_iterator temp = it;
return utf8::next(temp, end);
}
template <typename octet_iterator, typename distance_type>
void advance (octet_iterator& it, distance_type n, octet_iterator end)
{
for (distance_type i = 0; i < n; ++i)
utf8::next(it, end);
}
template <typename octet_iterator>
typename std::iterator_traits<octet_iterator>::difference_type
distance (octet_iterator first, octet_iterator last)
{
typename std::iterator_traits<octet_iterator>::difference_type dist;
for (dist = 0; first < last; ++dist)
utf8::next(first, last);
return dist;
}
template <typename u16bit_iterator, typename octet_iterator>
octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)
{
while (start != end) {
uint32_t cp = utf8::internal::mask16(*start++);
// Take care of surrogate pairs first
if (utf8::internal::is_lead_surrogate(cp)) {
if (start != end) {
uint32_t trail_surrogate = utf8::internal::mask16(*start++);
if (utf8::internal::is_trail_surrogate(trail_surrogate))
cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;
else
throw invalid_utf16(static_cast<uint16_t>(trail_surrogate));
}
else
throw invalid_utf16(static_cast<uint16_t>(cp));
}
// Lone trail surrogate
else if (utf8::internal::is_trail_surrogate(cp))
throw invalid_utf16(static_cast<uint16_t>(cp));
result = utf8::append(cp, result);
}
return result;
}
template <typename u16bit_iterator, typename octet_iterator>
u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)
{
while (start != end) {
uint32_t cp = utf8::next(start, end);
if (cp > 0xffff) { //make a surrogate pair
*result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET);
*result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);
}
else
*result++ = static_cast<uint16_t>(cp);
}
return result;
}
template <typename octet_iterator, typename u32bit_iterator>
octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result)
{
while (start != end)
result = utf8::append(*(start++), result);
return result;
}
template <typename octet_iterator, typename u32bit_iterator>
u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)
{
while (start != end)
(*result++) = utf8::next(start, end);
return result;
}
// The iterator class
template <typename octet_iterator>
class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> {
octet_iterator it;
octet_iterator range_start;
octet_iterator range_end;
public:
iterator () {}
explicit iterator (const octet_iterator& octet_it,
const octet_iterator& range_start,
const octet_iterator& range_end) :
it(octet_it), range_start(range_start), range_end(range_end)
{
if (it < range_start || it > range_end)
throw std::out_of_range("Invalid utf-8 iterator position");
}
// the default "big three" are OK
octet_iterator base () const { return it; }
uint32_t operator * () const
{
octet_iterator temp = it;
return utf8::next(temp, range_end);
}
bool operator == (const iterator& rhs) const
{
if (range_start != rhs.range_start || range_end != rhs.range_end)
throw std::logic_error("Comparing utf-8 iterators defined with different ranges");
return (it == rhs.it);
}
bool operator != (const iterator& rhs) const
{
return !(operator == (rhs));
}
iterator& operator ++ ()
{
utf8::next(it, range_end);
return *this;
}
iterator operator ++ (int)
{
iterator temp = *this;
utf8::next(it, range_end);
return temp;
}
iterator& operator -- ()
{
utf8::prior(it, range_start);
return *this;
}
iterator operator -- (int)
{
iterator temp = *this;
utf8::prior(it, range_start);
return temp;
}
}; // class iterator
} // namespace utf8
#endif //header guard

329
3rdparty/utf8/source/utf8/core.h vendored Normal file
View File

@@ -0,0 +1,329 @@
// Copyright 2006 Nemanja Trifunovic
/*
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
#define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
#include <iterator>
namespace utf8
{
// The typedefs for 8-bit, 16-bit and 32-bit unsigned integers
// You may need to change them to match your system.
// These typedefs have the same names as ones from cstdint, or boost/cstdint
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
// Helper code - not intended to be directly called by the library users. May be changed at any time
namespace internal
{
// Unicode constants
// Leading (high) surrogates: 0xd800 - 0xdbff
// Trailing (low) surrogates: 0xdc00 - 0xdfff
const uint16_t LEAD_SURROGATE_MIN = 0xd800u;
const uint16_t LEAD_SURROGATE_MAX = 0xdbffu;
const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u;
const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu;
const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10);
const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN;
// Maximum valid value for a Unicode code point
const uint32_t CODE_POINT_MAX = 0x0010ffffu;
template<typename octet_type>
inline uint8_t mask8(octet_type oc)
{
return static_cast<uint8_t>(0xff & oc);
}
template<typename u16_type>
inline uint16_t mask16(u16_type oc)
{
return static_cast<uint16_t>(0xffff & oc);
}
template<typename octet_type>
inline bool is_trail(octet_type oc)
{
return ((utf8::internal::mask8(oc) >> 6) == 0x2);
}
template <typename u16>
inline bool is_lead_surrogate(u16 cp)
{
return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX);
}
template <typename u16>
inline bool is_trail_surrogate(u16 cp)
{
return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);
}
template <typename u16>
inline bool is_surrogate(u16 cp)
{
return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);
}
template <typename u32>
inline bool is_code_point_valid(u32 cp)
{
return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp));
}
template <typename octet_iterator>
inline typename std::iterator_traits<octet_iterator>::difference_type
sequence_length(octet_iterator lead_it)
{
uint8_t lead = utf8::internal::mask8(*lead_it);
if (lead < 0x80)
return 1;
else if ((lead >> 5) == 0x6)
return 2;
else if ((lead >> 4) == 0xe)
return 3;
else if ((lead >> 3) == 0x1e)
return 4;
else
return 0;
}
template <typename octet_difference_type>
inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length)
{
if (cp < 0x80) {
if (length != 1)
return true;
}
else if (cp < 0x800) {
if (length != 2)
return true;
}
else if (cp < 0x10000) {
if (length != 3)
return true;
}
return false;
}
enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT};
/// Helper for get_sequence_x
template <typename octet_iterator>
utf_error increase_safely(octet_iterator& it, octet_iterator end)
{
if (++it == end)
return NOT_ENOUGH_ROOM;
if (!utf8::internal::is_trail(*it))
return INCOMPLETE_SEQUENCE;
return UTF8_OK;
}
#define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;}
/// get_sequence_x functions decode utf-8 sequences of the length x
template <typename octet_iterator>
utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point)
{
if (it == end)
return NOT_ENOUGH_ROOM;
code_point = utf8::internal::mask8(*it);
return UTF8_OK;
}
template <typename octet_iterator>
utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point)
{
if (it == end)
return NOT_ENOUGH_ROOM;
code_point = utf8::internal::mask8(*it);
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f);
return UTF8_OK;
}
template <typename octet_iterator>
utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point)
{
if (it == end)
return NOT_ENOUGH_ROOM;
code_point = utf8::internal::mask8(*it);
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff);
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
code_point += (*it) & 0x3f;
return UTF8_OK;
}
template <typename octet_iterator>
utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point)
{
if (it == end)
return NOT_ENOUGH_ROOM;
code_point = utf8::internal::mask8(*it);
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff);
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
code_point += (utf8::internal::mask8(*it) << 6) & 0xfff;
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
code_point += (*it) & 0x3f;
return UTF8_OK;
}
#undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR
template <typename octet_iterator>
utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point)
{
// Save the original value of it so we can go back in case of failure
// Of course, it does not make much sense with i.e. stream iterators
octet_iterator original_it = it;
uint32_t cp = 0;
// Determine the sequence length based on the lead octet
typedef typename std::iterator_traits<octet_iterator>::difference_type octet_difference_type;
const octet_difference_type length = utf8::internal::sequence_length(it);
// Get trail octets and calculate the code point
utf_error err = UTF8_OK;
switch (length) {
case 0:
return INVALID_LEAD;
case 1:
err = utf8::internal::get_sequence_1(it, end, cp);
break;
case 2:
err = utf8::internal::get_sequence_2(it, end, cp);
break;
case 3:
err = utf8::internal::get_sequence_3(it, end, cp);
break;
case 4:
err = utf8::internal::get_sequence_4(it, end, cp);
break;
}
if (err == UTF8_OK) {
// Decoding succeeded. Now, security checks...
if (utf8::internal::is_code_point_valid(cp)) {
if (!utf8::internal::is_overlong_sequence(cp, length)){
// Passed! Return here.
code_point = cp;
++it;
return UTF8_OK;
}
else
err = OVERLONG_SEQUENCE;
}
else
err = INVALID_CODE_POINT;
}
// Failure branch - restore the original value of the iterator
it = original_it;
return err;
}
template <typename octet_iterator>
inline utf_error validate_next(octet_iterator& it, octet_iterator end) {
uint32_t ignored;
return utf8::internal::validate_next(it, end, ignored);
}
} // namespace internal
/// The library API - functions intended to be called by the users
// Byte order mark
const uint8_t bom[] = {0xef, 0xbb, 0xbf};
template <typename octet_iterator>
octet_iterator find_invalid(octet_iterator start, octet_iterator end)
{
octet_iterator result = start;
while (result != end) {
utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end);
if (err_code != internal::UTF8_OK)
return result;
}
return result;
}
template <typename octet_iterator>
inline bool is_valid(octet_iterator start, octet_iterator end)
{
return (utf8::find_invalid(start, end) == end);
}
template <typename octet_iterator>
inline bool starts_with_bom (octet_iterator it, octet_iterator end)
{
return (
((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) &&
((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) &&
((it != end) && (utf8::internal::mask8(*it)) == bom[2])
);
}
//Deprecated in release 2.3
template <typename octet_iterator>
inline bool is_bom (octet_iterator it)
{
return (
(utf8::internal::mask8(*it++)) == bom[0] &&
(utf8::internal::mask8(*it++)) == bom[1] &&
(utf8::internal::mask8(*it)) == bom[2]
);
}
} // namespace utf8
#endif // header guard

228
3rdparty/utf8/source/utf8/unchecked.h vendored Normal file
View File

@@ -0,0 +1,228 @@
// Copyright 2006 Nemanja Trifunovic
/*
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#ifndef UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
#define UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
#include "core.h"
namespace utf8
{
namespace unchecked
{
template <typename octet_iterator>
octet_iterator append(uint32_t cp, octet_iterator result)
{
if (cp < 0x80) // one octet
*(result++) = static_cast<uint8_t>(cp);
else if (cp < 0x800) { // two octets
*(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0);
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
}
else if (cp < 0x10000) { // three octets
*(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0);
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
}
else { // four octets
*(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0);
*(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f)| 0x80);
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
}
return result;
}
template <typename octet_iterator>
uint32_t next(octet_iterator& it)
{
uint32_t cp = utf8::internal::mask8(*it);
typename std::iterator_traits<octet_iterator>::difference_type length = utf8::internal::sequence_length(it);
switch (length) {
case 1:
break;
case 2:
it++;
cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f);
break;
case 3:
++it;
cp = ((cp << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff);
++it;
cp += (*it) & 0x3f;
break;
case 4:
++it;
cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff);
++it;
cp += (utf8::internal::mask8(*it) << 6) & 0xfff;
++it;
cp += (*it) & 0x3f;
break;
}
++it;
return cp;
}
template <typename octet_iterator>
uint32_t peek_next(octet_iterator it)
{
return utf8::unchecked::next(it);
}
template <typename octet_iterator>
uint32_t prior(octet_iterator& it)
{
while (utf8::internal::is_trail(*(--it))) ;
octet_iterator temp = it;
return utf8::unchecked::next(temp);
}
// Deprecated in versions that include prior, but only for the sake of consistency (see utf8::previous)
template <typename octet_iterator>
inline uint32_t previous(octet_iterator& it)
{
return utf8::unchecked::prior(it);
}
template <typename octet_iterator, typename distance_type>
void advance (octet_iterator& it, distance_type n)
{
for (distance_type i = 0; i < n; ++i)
utf8::unchecked::next(it);
}
template <typename octet_iterator>
typename std::iterator_traits<octet_iterator>::difference_type
distance (octet_iterator first, octet_iterator last)
{
typename std::iterator_traits<octet_iterator>::difference_type dist;
for (dist = 0; first < last; ++dist)
utf8::unchecked::next(first);
return dist;
}
template <typename u16bit_iterator, typename octet_iterator>
octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)
{
while (start != end) {
uint32_t cp = utf8::internal::mask16(*start++);
// Take care of surrogate pairs first
if (utf8::internal::is_lead_surrogate(cp)) {
uint32_t trail_surrogate = utf8::internal::mask16(*start++);
cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;
}
result = utf8::unchecked::append(cp, result);
}
return result;
}
template <typename u16bit_iterator, typename octet_iterator>
u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)
{
while (start < end) {
uint32_t cp = utf8::unchecked::next(start);
if (cp > 0xffff) { //make a surrogate pair
*result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET);
*result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);
}
else
*result++ = static_cast<uint16_t>(cp);
}
return result;
}
template <typename octet_iterator, typename u32bit_iterator>
octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result)
{
while (start != end)
result = utf8::unchecked::append(*(start++), result);
return result;
}
template <typename octet_iterator, typename u32bit_iterator>
u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)
{
while (start < end)
(*result++) = utf8::unchecked::next(start);
return result;
}
// The iterator class
template <typename octet_iterator>
class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> {
octet_iterator it;
public:
iterator () {}
explicit iterator (const octet_iterator& octet_it): it(octet_it) {}
// the default "big three" are OK
octet_iterator base () const { return it; }
uint32_t operator * () const
{
octet_iterator temp = it;
return utf8::unchecked::next(temp);
}
bool operator == (const iterator& rhs) const
{
return (it == rhs.it);
}
bool operator != (const iterator& rhs) const
{
return !(operator == (rhs));
}
iterator& operator ++ ()
{
::std::advance(it, utf8::internal::sequence_length(it));
return *this;
}
iterator operator ++ (int)
{
iterator temp = *this;
::std::advance(it, utf8::internal::sequence_length(it));
return temp;
}
iterator& operator -- ()
{
utf8::unchecked::prior(it);
return *this;
}
iterator operator -- (int)
{
iterator temp = *this;
utf8::unchecked::prior(it);
return temp;
}
}; // class iterator
} // namespace utf8::unchecked
} // namespace utf8
#endif // header guard

View File

@@ -7,9 +7,6 @@ if(COMMAND cmake_policy)
if(POLICY CMP0042)
cmake_policy(SET CMP0042 NEW)
endif()
if(POLICY CMP0067)
cmake_policy(SET CMP0067 NEW)
endif()
endif()
@@ -32,7 +29,6 @@ include (GenerateExportHeader)
# only relevant for building shared libs but let's set it regardless
set(CMAKE_OSX_RPATH 1)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X deployment version")
# let's use & require C++11 - note these are only functional with CMake 3.1
# we do manual fallbacks for CMake 3.0 in the compilers section
@@ -45,10 +41,15 @@ string(STRIP ${versionFile} SIMGEAR_VERSION)
project(SimGear VERSION ${SIMGEAR_VERSION} LANGUAGES C CXX)
# add a dependency on the version file
# using 10.7 because boost requires libc++ and 10.6 doesn't include it
# Cmake documentation says we must set this before calling project(), but
# it only seems to be picked up setting it /after/ the call to project()
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7")
# add a dependency on the versino file
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS version)
set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS TRUE)
set(FIND_LIBRARY_USE_LIB64_PATHS ON)
# use simgear version also as the SO version (if building SOs)
SET(SIMGEAR_SOVERSION ${SIMGEAR_VERSION})
@@ -119,19 +120,12 @@ endif()
option(SIMGEAR_HEADLESS "Set to ON to build SimGear without GUI/graphics support" OFF)
option(ENABLE_RTI "Set to ON to build SimGear with RTI support" OFF)
option(ENABLE_GDAL "Set to ON to build SimGear with GDAL support" OFF)
option(ENABLE_TESTS "Set to OFF to disable building SimGear's test applications" ON)
option(ENABLE_SOUND "Set to OFF to disable building SimGear's sound support" ON)
option(USE_AEONWAVE "Set to ON to use AeonWave instead of OpenAL" ON)
option(USE_AEONWAVE "Set to ON to use AeonWave instead of OpenAL" OFF)
option(ENABLE_PKGUTIL "Set to ON to build the sg_pkgutil application (default)" ON)
option(ENABLE_DNS "Set to ON to use udns library and DNS service resolver" ON)
option(ENABLE_SIMD "Enable SSE/SSE2 support for compilers" ON)
option(ENABLE_SIMD_CODE "Enable SSE/SSE2 support code for compilers" OFF)
option(ENABLE_OPENMP "Enable OpenMP compiler support" OFF)
if (NOT ENABLE_SIMD AND ENABLE_SIMD_CODE)
set(ENABLE_SIMD_CODE OFF)
endif()
option(ENABLE_SIMD "Enable SSE/SSE2 support for x86 compilers" ON)
include (DetectArch)
@@ -139,16 +133,6 @@ 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)
@@ -168,22 +152,14 @@ endif (MSVC)
if (MSVC AND MSVC_3RDPARTY_ROOT)
message(STATUS "3rdparty files located in ${MSVC_3RDPARTY_ROOT}")
string(SUBSTRING ${MSVC_VERSION} 0 2 MSVC_VERSION_MAJOR)
string(SUBSTRING ${MSVC_VERSION} 2 2 MSVC_VERSION_MINOR)
set( OSG_MSVC "msvc" )
if (${MSVC_VERSION_MAJOR} EQUAL "19")
if (${MSVC_VERSION_MINOR} EQUAL "00")
if (${MSVC_VERSION} EQUAL 1900)
set( OSG_MSVC ${OSG_MSVC}140 )
else ()
set( OSG_MSVC ${OSG_MSVC}141 )
endif ()
elseif (${MSVC_VERSION_MAJOR} EQUAL "18")
set( OSG_MSVC ${OSG_MSVC}120 )
elseif (${MSVC_VERSION} EQUAL 1800)
set( OSG_MSVC ${OSG_MSVC}120 )
else ()
message(FATAL_ERROR "Visual Studio 2013/15/17 is required")
message(FATAL_ERROR "Visual Studio 2013/2015 is required now")
endif ()
if (CMAKE_CL_64)
set( OSG_MSVC ${OSG_MSVC}-64 )
set( MSVC_3RDPARTY_DIR 3rdParty.x64 )
@@ -203,8 +179,11 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
message(STATUS "BOOST_INCLUDEDIR is ${BOOST_INCLUDEDIR}")
endif()
set (OPENAL_INCLUDE_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include)
set (OPENAL_LIBRARY_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib)
if (NOT USE_AEONWAVE)
set (OPENAL_INCLUDE_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include)
set (OPENAL_LIBRARY_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib)
message(STATUS "OPENAL_INCLUDE_DIR is ${OPENAL_INCLUDE_DIR}")
endif()
endif (MSVC AND MSVC_3RDPARTY_ROOT)
if(APPLE)
@@ -228,19 +207,12 @@ else()
if (ENABLE_SOUND)
if (USE_AEONWAVE)
find_package(AAX)
endif()
if(NOT AAX_FOUND)
set(USE_AEONWAVE FALSE)
find_package(AAX COMPONENTS aax REQUIRED)
else()
find_package(OpenAL REQUIRED)
endif()
if(AAX_FOUND)
message(STATUS "Sound support: AeonWave")
else()
message(STATUS "Sound support: OpenAL")
endif()
message(STATUS "Sound support: ENABLED")
endif(ENABLE_SOUND)
find_package(OpenSceneGraph 3.2.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgViewer osgUtil)
@@ -275,8 +247,8 @@ else()
# declaring symbols as declspec(import)
add_definitions(-DHAVE_EXPAT_CONFIG_H -DXML_STATIC)
set(EXPAT_INCLUDE_DIRS
${PROJECT_SOURCE_DIR}/3rdparty/expat
${PROJECT_BINARY_DIR}/3rdparty/expat)
${PROJECT_SOURCE_DIR}/3rdparty/expat
${PROJECT_BINARY_DIR}/3rdparty/expat)
endif(SYSTEM_EXPAT)
check_include_file(inttypes.h HAVE_INTTYPES_H)
@@ -290,28 +262,13 @@ if(HAVE_INTTYPES_H)
endif()
if(ENABLE_RTI)
find_package(PkgConfig)
if(PKG_CONFIG_FOUND)
SET(ENV{PKG_CONFIG_PATH} "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}")
pkg_check_modules(RTI hla-rti13)
endif(PKG_CONFIG_FOUND)
if(RTI_FOUND)
SET(RTI_INCLUDE_DIR "${RTI_INCLUDE_DIRS}")
message(STATUS "RTI: ENABLED")
else()
message(STATUS "RTI: DISABLED")
endif(RTI_FOUND)
# See if we have any rti library variant installed
message(STATUS "RTI: ENABLED")
find_package(RTI)
else()
message(STATUS "RTI: DISABLED")
endif(ENABLE_RTI)
if(ENABLE_GDAL)
find_package(GDAL 2.0.0 REQUIRED)
if (GDAL_FOUND)
include_directories(${GDAL_INCLUDE_DIR})
endif(GDAL_FOUND)
endif(ENABLE_GDAL)
check_function_exists(gettimeofday HAVE_GETTIMEOFDAY)
check_function_exists(rint HAVE_RINT)
check_function_exists(mkdtemp HAVE_MKDTEMP)
@@ -369,7 +326,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 <cmath>
"#include <cstdlib>
int main() { return std::isnan(0.0);} "
HAVE_STD_ISNAN)
@@ -377,48 +334,22 @@ 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")
set(WARNING_FLAGS_CXX "-Wall")
set(WARNING_FLAGS_C "-Wall")
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()
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_C_FLAGS
"${CMAKE_C_FLAGS} -O0 -fno-omit-frame-pointer -fno-inline")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -O0 -fno-omit-frame-pointer -fno-inline")
elseif (ENABLE_SIMD)
if(ENABLE_SIMD)
if (X86 OR X86_64)
set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse -ftree-vectorize -ftree-slp-vectorize")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse -ftree-vectorize -ftree-slp-vectorize")
set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse")
endif()
endif()
@@ -432,38 +363,24 @@ endif(CMAKE_COMPILER_IS_GNUCXX)
if (CLANG)
# Boost redeclares class members
set(WARNING_FLAGS_CXX "-Wall -fPIC -Wno-overloaded-virtual -Wno-redeclared-class-member")
set(WARNING_FLAGS_C "-Wall -fPIC")
set(WARNING_FLAGS_CXX "-Wall -Wno-overloaded-virtual -Wno-redeclared-class-member")
set(WARNING_FLAGS_C "-Wall")
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
# fix Boost compilation :(
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_C_FLAGS
"${CMAKE_C_FLAGS} -O0 -fno-omit-frame-pointer -fno-inline-functions")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -O0 -fno-omit-frame-pointer -fno-inline-functions")
elseif (ENABLE_SIMD)
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 -ftree-vectorize -ftree-slp-vectorize")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse -ftree-vectorize -ftree-slp-vectorize")
set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse")
endif()
endif()
endif()
if (ENABLE_OPENMP)
find_package(OpenMP)
if(OPENMP_FOUND)
message(STATUS "OpenMP: ENABLED")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
else()
message(STATUS "OpenMP: NOT FOUND")
endif()
else()
message(STATUS "OpenMP: DISABLED")
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# boost goes haywire wrt static asserts
check_cxx_compiler_flag(-Wno-unused-local-typedefs HAS_NOWARN_UNUSED_TYPEDEFS)
@@ -515,8 +432,6 @@ 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)
@@ -549,8 +464,7 @@ set(TEST_LIBS_INTERNAL_CORE
${RT_LIBRARY}
${DL_LIBRARY}
${COCOA_LIBRARY}
${CURL_LIBRARIES}
${GDAL_LIBRARY})
${CURL_LIBRARIES})
set(TEST_LIBS SimGearCore ${TEST_LIBS_INTERNAL_CORE})
if(NOT SIMGEAR_HEADLESS)

View File

@@ -1,30 +0,0 @@
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
)

View File

@@ -1,74 +1,48 @@
# Try to find AAX (AeonWave)
# Locate AAX
# This module defines
#
# AAX_FOUND - if false, do not try to link to AAX
# AAX_INCLUDE_DIR - where to find the headers
# AAX_LIBRARIES - Link these to use AAX
#
# Copyright (C) 2016-2018 by Erik Hofman.
# Copyright (C) 2016-2018 by Adalin B.V.
# AAX_LIBRARIES
# AAX_FOUND, if false, do not try to link to AAX
# AAX_INCLUDE_DIR, where to find the headers
#
# $AAXDIR is an environment variable that would
# correspond to the ./configure --prefix=$AAXDIR
# used in building AAX.
#
# This file is Public Domain (www.unlicense.org)
# This is free and unencumbered software released into the public domain.
# Created by Erik Hofman.
if (AAX_LIBRARY AND AAX_INCLUDE_DIR)
# in cache already
set(AAX_FOUND TRUE)
else()
find_path(AAX_INCLUDE_DIR aax/aax.h
HINTS
$ENV{AAXDIR}
$ENV{ProgramFiles}/aax
$ENV{ProgramFiles}/AeonWave
$ENV{ProgramFiles}/Adalin/AeonWave
${CMAKE_SOURCE_DIR}/aax
PATH_SUFFIXES include
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/opt
)
FIND_PATH(AAX_INCLUDE_DIR aax/aeonwave.hpp
HINTS
$ENV{AAXDIR}
$ENV{ProgramFiles}/aax
$ENV{ProgramFiles}/AeonWave
$ENV{ProgramFiles}/Adalin/AeonWave
PATH_SUFFIXES include
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/opt
)
find_library(AAX_LIBRARY
NAMES AAX aax libAAX
HINTS
$ENV{AAXDIR}
$ENV{ProgramFiles}/AAX
$ENV{ProgramFiles}/AeonWave
$ENV{ProgramFiles}/Adalin/AeonWave
${CMAKE_BUILD_DIR}/aax
PATH_SUFFIXES lib64 lib lib/${CMAKE_LIBRARY_ARCHITECTURE} libs64 libs libs/Win32 libs/Win64 bin
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/opt
)
FIND_LIBRARY(AAX_LIBRARY
NAMES AAX aax AAX32 libAAX32
HINTS
$ENV{AAXDIR}
$ENV{ProgramFiles}/AAX
$ENV{ProgramFiles}/AeonWave
$ENV{ProgramFiles}/Adalin/AeonWave
PATH_SUFFIXES bin lib lib/${CMAKE_LIBRARY_ARCHITECTURE} lib64 libs64 libs libs/Win32 libs/Win64
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr
/opt
/usr/local
)
set(AAX_DEFINITIONS "")
if (AAX_LIBRARY AND AAX_INCLUDE_DIR)
set(AAX_FOUND TRUE)
endif()
if (AAX_FOUND)
if (NOT Udns_FIND_QUIETLY)
message(STATUS "Found AeonWave: ${AAX_LIBRARIES}")
endif ()
else ()
if (Udns_FIND_REQUIRED)
message(FATAL_ERROR "Could not find AeonWave")
endif ()
endif ()
# show the AAX_INCLUDE_DIRS and AAX_LIBRARIES variables only in the advanced view
mark_as_advanced(AAX_INCLUDE_DIRS AAX_LIBRARIES)
endif()
SET(AAX_FOUND "NO")
IF(AAX_LIBRARY AND AAX_INCLUDE_DIR)
SET(AAX_FOUND "YES")
ENDIF(AAX_LIBRARY AND AAX_INCLUDE_DIR)

View File

@@ -12,11 +12,6 @@ macro(simgear_component_common name includePath sourcesList sources headers)
set_property(GLOBAL
APPEND PROPERTY PUBLIC_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/${h}")
set(fh${sourcesList} "${fh${sourcesList}}#${CMAKE_CURRENT_SOURCE_DIR}/${h}")
# also append headers to the sources list, so that IDEs find the files
# correctly (otherwise they are not in the project)
set_property(GLOBAL
APPEND PROPERTY ${sourcesList} "${CMAKE_CURRENT_SOURCE_DIR}/${h}")
endforeach()
set_property(GLOBAL APPEND PROPERTY FG_GROUPS_${sourcesList}_C "${fc${sourcesList}}@")

View File

@@ -2,10 +2,6 @@
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.

20
README.OpenAL Normal file
View File

@@ -0,0 +1,20 @@
[This file is mirrored in both the FlightGear and SimGear packages.]
You *must* have the development components of OpenAL installed on your system
to build FlightGear!" You can get a copy here:
http://connect.creativelabs.com/openal/default.aspx
Build notes:
You can download a versioned release of the openal library from
http://www.openal.org/downloads.html. Download the openal source,
release 0.0.8 (dated February 11, 2006) and run:
tar xjvf openal-soft-1.5.304.tar.bz2
cd openal-soft-1.5.304/
ccmake .
[ While running ccmake: press 'c' to configure, press 'c' once more, and
then press 'g' to generate and exit ]

View File

@@ -1,39 +0,0 @@
[This file is mirrored in both the FlightGear and SimGear packages.]
For Sound support FlightGear requires one of the two following packages:
- OpenAL
- AeonWave
== OpenAL ===
You *must* have the development components of OpenAL installed on your system
to build FlightGear!" You can get a copy here:
http://connect.creativelabs.com/openal/default.aspx
Build notes:
You can download a versioned release of the openal library from
http://www.openal.org/downloads.html. Download the openal source,
release 0.0.8 (dated February 11, 2006) and run:
tar xjvf openal-soft-1.5.304.tar.bz2
cd openal-soft-1.5.304/
ccmake .
[ While running ccmake: press 'c' to configure, press 'c' once more, and
then press 'g' to generate and exit ]
== AeonWave ===
For FlightGear AeonWave has a number of advantages over OpenAL:
* Correct Doppler effect behavior
* Default distance attenuation frequency filtering
* Native support for 29 types of audio formats.
* Native support for wav, mp3, vorbis and raw file formats.
The source code of AeonWave can be found on GitHub:
https://github.com/adalinbv
Optimized binary packages are available at:
http://www.adalin.com/

76
SimGear.spec.in Normal file
View File

@@ -0,0 +1,76 @@
%define ver @VERSION@
%define rel 1
%define prefix /usr
Summary: Simulator Construction Gear.
Name: @PACKAGE@
Version: %ver
Release: %rel
Copyright: LGPL
Group: Libraries/Graphics
Source: %{name}-%{version}.tar.gz
#URL:
BuildRoot: /tmp/%{name}-%{version}-%{rel}-root
Packager: Fill In As You Wish
Docdir: %{prefix}/doc
%description
This package contains a tools and libraries useful for constructing
simulation and visualization applications such as FlightGear or TerraGear.
Authors:
N/A
%prep
%setup -n %{name}-%{version}
%build
# Needed for snapshot releases.
if [ ! -f configure ]; then
CFLAGS="$RPM_OPT_FLAGS" ./autogen.sh --prefix=%prefix
else
CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=%prefix
fi
if [ "$SMP" != "" ]; then
JSMP = '"MAKE=make -k -j $SMP"'
fi
make ${JSMP};
%install
[ -d ${RPM_BUILD_ROOT} ] && rm -rf ${RPM_BUILD_ROOT}
make prefix=${RPM_BUILD_ROOT}%{prefix} install
#
# Generating file lists and store them in file-lists
# Starting with the directory listings
#
find ${RPM_BUILD_ROOT}%{prefix}/{bin,include,lib} -type d | sed "s#^${RPM_BUILD_ROOT}#\%attr (-\,root\,root) \%dir #" > file-lists
%{?ETCDR:find ${RPM_BUILD_ROOT}%{!?SYSCF:%{prefix}}/etc -type d | sed "s#^${RPM_BUILD_ROOT}#\%attr (-\,root\,root) \%dir #" >> file-lists}
#
# Then, the file listings
#
echo "%defattr (-, root, root)" >> file-lists
%{?ETCDR:find ${RPM_BUILD_ROOT}%{!?SYSCF:%{prefix}}/etc/%{name}.conf -type f | sed -e "s#^${RPM_BUILD_ROOT}#%config #g" >> file-lists}
find ${RPM_BUILD_ROOT}%{prefix} -type f | sed -e "s#^${RPM_BUILD_ROOT}##g" >> file-lists
%clean
(cd ..; rm -rf %{name}-%{version} ${RPM_BUILD_ROOT})
%files -f file-lists
%defattr (-, root, root)
%doc AUTHORS
%doc COPYING
%doc ChangeLog
%doc INSTALL
%doc NEWS
%doc README
%doc %{name}.spec.in

View File

@@ -7,7 +7,6 @@ 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)
@@ -18,9 +17,4 @@ set(USE_AEONWAVE @USE_AEONWAVE@)
set(ENABLE_SIMD @ENABLE_SIMD@)
# Alternative terrain engine based on pagedLOD
set(ENABLE_GDAL @ENABLE_GDAL@)
set(ENABLE_OPENMP @ENABLE_OPENMP@)
include("${CMAKE_CURRENT_LIST_DIR}/SimGearTargets.cmake")

13
package/RedHat/README Normal file
View File

@@ -0,0 +1,13 @@
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.)

23
package/openSUSE/README Normal file
View File

@@ -0,0 +1,23 @@
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!

View File

@@ -0,0 +1,63 @@
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

View File

@@ -5,7 +5,6 @@ foreach( mylibfolder
bucket
bvh
debug
embedded_resources
ephemeris
io
magvar
@@ -15,7 +14,6 @@ foreach( mylibfolder
nasal/cppbind
props
serial
std
structure
threads
timing
@@ -174,11 +172,6 @@ if(NOT SIMGEAR_HEADLESS)
${OPENGL_LIBRARY}
${JPEG_LIBRARY})
if(ENABLE_GDAL)
target_link_libraries(SimGearScene
${GDAL_LIBRARIES})
endif()
# only actually needed by canvas/KeyboardEvent.cxx
target_include_directories(SimGearScene PRIVATE ${PROJECT_SOURCE_DIR}/3rdparty/utf8/source)
endif()

View File

@@ -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,47 +87,19 @@ void testBasic()
SG_CHECK_EQUAL(atAntiMeridian2.get_chunk_lon(), -180);
SG_CHECK_EQUAL(atAntiMeridian2.get_x(), 0);
// check comparison operator overload
// check comparisom 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()
@@ -138,17 +110,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);
@@ -159,11 +131,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);
@@ -171,16 +143,17 @@ 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)
@@ -194,7 +167,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);
}
@@ -208,7 +181,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);
@@ -217,7 +190,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.
@@ -226,7 +199,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));
}
@@ -237,40 +210,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());
}
@@ -283,80 +256,29 @@ 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
}

View File

@@ -15,8 +15,6 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include "BVHMaterial.hxx"
namespace simgear {

View File

@@ -15,8 +15,6 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include "BVHPageNode.hxx"
#include "BVHPager.hxx"

View File

@@ -15,8 +15,6 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include "BVHPageRequest.hxx"
namespace simgear {

View File

@@ -15,8 +15,6 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include "BVHPager.hxx"
#include <list>

View File

@@ -15,8 +15,6 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include "BVHStaticNode.hxx"
namespace simgear {

View File

@@ -15,7 +15,6 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include <iostream>
#include <simgear/structure/SGSharedPtr.hxx>

View File

@@ -1,5 +1,4 @@
///@file
/// The canvas for rendering with the 2d API
// The canvas for rendering with the 2d API
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -17,16 +16,12 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "Canvas.hxx"
#include "CanvasEventManager.hxx"
#include "CanvasEventVisitor.hxx"
#include "CanvasPlacement.hxx"
#include <simgear/canvas/events/KeyboardEvent.hxx>
#include <simgear/canvas/events/MouseEvent.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/scene/util/parse_color.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
@@ -35,79 +30,13 @@
#include <osgText/Text>
#include <osgViewer/Viewer>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/foreach.hpp>
namespace simgear
{
namespace canvas
{
static int globalinstanceid = 1;
/**
* Camera Callback for moving completed canvas images to subscribed listener.
*/
class CanvasImageCallback : public osg::Camera::DrawCallback {
public:
osg::Image *_rawImage;
CanvasImageCallback(osg::Image *rawImage)
: _min_delta_tick(1.0 / 8.0) {
_previousFrameTick = osg::Timer::instance()->tick();
_rawImage = rawImage;
SG_LOG(SG_GENERAL,SG_INFO,"CanvasImageCallback created. instance is " << instanceid);
}
virtual void operator()(osg::RenderInfo& renderInfo) const {
osg::Timer_t n = osg::Timer::instance()->tick();
double dt = osg::Timer::instance()->delta_s(_previousFrameTick, n);
if (dt < _min_delta_tick)
return;
_previousFrameTick = n;
SG_LOG(SG_GENERAL,SG_DEBUG,"CanvasImageCallback " << instanceid << ": image available for " << _subscribers.size() << " subscribers. camera is " << renderInfo.getCurrentCamera());
bool hasSubscribers = false;
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
hasSubscribers = !_subscribers.empty();
}
if (hasSubscribers) {
//Make sure image can be overwritten by next frame while it is still returned to the client
osg::Image* image = new osg::Image(*_rawImage, osg::CopyOp::DEEP_COPY_ALL);
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
while (!_subscribers.empty()) {
try {
CanvasImageReadyListener *subs = _subscribers.back();
if (subs){
subs->imageReady(image);
}else{
SG_LOG(SG_GENERAL,SG_WARN,"CanvasImageCallback subscriber null");
}
} catch (...) { }
_subscribers.pop_back();
}
}
}
}
void subscribe(CanvasImageReadyListener * subscriber) {
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
_subscribers.push_back(subscriber);
}
void unsubscribe(CanvasImageReadyListener * subscriber) {
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
_subscribers.remove(subscriber);
}
int getSubscriberCount() {
return _subscribers.size();
}
private:
mutable list<CanvasImageReadyListener*> _subscribers;
mutable OpenThreads::Mutex _lock;
mutable double _previousFrameTick;
double _min_delta_tick;
int instanceid = globalinstanceid++;
};
//----------------------------------------------------------------------------
Canvas::CullCallback::CullCallback(const CanvasWeakPtr& canvas):
@@ -133,9 +62,18 @@ 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")
_status_msg(node, "status-msg"),
_sampling_dirty(false),
_render_dirty(true),
_visible(true),
_render_always(false)
{
_status = 0;
setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
@@ -296,10 +234,6 @@ 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() )
@@ -317,21 +251,6 @@ namespace canvas
osg::Camera* camera = _texture.getCamera();
string canvasname = _node->getStringValue("name");
int renderToImage = _node->getBoolValue("render-to-image");
if (renderToImage){
CanvasImageCallback *_screenshotCallback = dynamic_cast<CanvasImageCallback*> (camera->getFinalDrawCallback());
if (!_screenshotCallback) {
// no draw callback yet
osg::Image* shot = new osg::Image();
shot->allocateImage(getSizeX(), getSizeY(), 24, GL_RGB, GL_UNSIGNED_BYTE);
camera->attach(osg::Camera::COLOR_BUFFER, shot);
camera->setFinalDrawCallback(new CanvasImageCallback(shot));
SG_LOG(SG_GENERAL,SG_INFO,"CanvasImage: attached image and draw callback to camera " << camera << " for canvas " << canvasname << ". Ready for subscriber now.");
}
}
// TODO Allow custom render order? For now just keep in order with
// property tree.
camera->setRenderOrder(osg::Camera::PRE_RENDER, _node->getIndex());
@@ -340,7 +259,7 @@ namespace canvas
parseColor(_node->getStringValue("background"), clear_color);
camera->setClearColor(clear_color);
camera->addChild(root_scene_group);
camera->addChild(_root_group->getMatrixTransform());
if( _texture.serviceable() )
{
@@ -360,7 +279,7 @@ namespace canvas
if( _visible || _render_always )
{
for(auto& canvas_weak: _child_canvases)
BOOST_FOREACH(CanvasWeakPtr canvas_weak, _child_canvases)
{
// TODO should we check if the image the child canvas is displayed
// within is really visible?
@@ -372,7 +291,7 @@ namespace canvas
if( _render_dirty )
{
// Also mark all canvases this canvas is displayed within as dirty
for(auto& canvas_weak: _parent_canvases)
BOOST_FOREACH(CanvasWeakPtr canvas_weak, _parent_canvases)
{
CanvasPtr canvas = canvas_weak.lock();
if( canvas )
@@ -431,41 +350,6 @@ namespace canvas
}
}
int Canvas::subscribe(CanvasImageReadyListener * subscriber) {
osg::Camera* camera = _texture.getCamera();
const string canvasname = _node->getStringValue("name");
SG_LOG(SG_GENERAL,SG_DEBUG,"CanvasImage: subscribe to canvas " << canvasname.c_str() << ", camera ="<< camera);
if (!_node->getBoolValue("render-to-image")) {
SG_LOG(SG_GENERAL,SG_INFO,"CanvasImage: Setting render-to-image");
_node->addChild("render-to-image", 0)->setBoolValue(1);
setStatusFlags(STATUS_DIRTY, true);
}
CanvasImageCallback *_screenshotCallback = dynamic_cast<CanvasImageCallback*> (camera->getFinalDrawCallback());
if (_screenshotCallback) {
// Camera ready for subscriber. Otherwise, draw callback is created by canvas thread later.
SG_LOG(SG_GENERAL,SG_DEBUG,"CanvasImage: adding subscriber to camera draw callback");
_screenshotCallback->subscribe(subscriber);
// TODO: check: Is this the correct way to ensure the canvas will be available?
enableRendering(true);
return 1;
}
return 0;
}
int Canvas::unsubscribe(CanvasImageReadyListener * subscriber) {
osg::Camera* camera = _texture.getCamera();
SG_LOG(SG_GENERAL,SG_DEBUG,"CanvasImage: unsubscribe");
CanvasImageCallback *cb = dynamic_cast<CanvasImageCallback*> (camera->getFinalDrawCallback());
if (cb) {
SG_LOG(SG_GENERAL,SG_DEBUG,"CanvasImage: unsubscribe from camera " << camera);
cb->unsubscribe(subscriber);
}
return 0;
}
//----------------------------------------------------------------------------
bool Canvas::addEventListener( const std::string& type,
const EventListener& cb )
@@ -636,8 +520,8 @@ namespace canvas
{
const std::string& name = node->getNameString();
if( strutils::starts_with(name, "status")
|| strutils::starts_with(name, "data-") )
if( boost::starts_with(name, "status")
|| boost::starts_with(name, "data-") )
return;
_render_dirty = true;
@@ -652,7 +536,7 @@ namespace canvas
if( !placements.empty() )
{
bool placement_dirty = false;
for(auto& placement: placements)
BOOST_FOREACH(PlacementPtr& placement, placements)
{
// check if change can be directly handled by placement
if( placement->getProps() == node->getParent()

View File

@@ -33,7 +33,7 @@
#include <osg/NodeCallback>
#include <osg/observer_ptr>
#include <memory>
#include <boost/scoped_ptr.hpp>
#include <string>
namespace simgear
@@ -44,17 +44,6 @@ namespace canvas
class CanvasMgr;
class MouseEvent;
/**
* A listener interested in completed canvas drawing.
*/
class CanvasImageReadyListener {
public:
virtual void imageReady(osg::ref_ptr<osg::Image>) = 0;
virtual ~CanvasImageReadyListener()
{
}
};
/**
* Canvas to draw onto (to an off-screen render target).
*/
@@ -82,18 +71,18 @@ namespace canvas
public osg::NodeCallback
{
public:
explicit CullCallback(const CanvasWeakPtr& canvas);
CullCallback(const CanvasWeakPtr& canvas);
private:
CanvasWeakPtr _canvas;
void operator()(osg::Node* node, osg::NodeVisitor* nv) override;
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
};
typedef osg::ref_ptr<CullCallback> CullCallbackPtr;
explicit Canvas(SGPropertyNode* node);
Canvas(SGPropertyNode* node);
virtual ~Canvas();
void onDestroy() override;
virtual void onDestroy();
void setCanvasMgr(CanvasMgr* canvas_mgr);
CanvasMgr* getCanvasMgr() const;
@@ -171,12 +160,7 @@ namespace canvas
*/
void enableRendering(bool force = false);
void update(double delta_time_sec) override;
osg::Camera* getCamera();
int subscribe(CanvasImageReadyListener * subscriber);
int unsubscribe(CanvasImageReadyListener * subscriber);
int getSubscriberCount();
void update(double delta_time_sec);
bool addEventListener(const std::string& type, const EventListener& cb);
bool dispatchEvent(const EventPtr& event);
@@ -200,9 +184,11 @@ namespace canvas
bool propagateEvent( EventPtr const& event,
EventPropagationPath const& path );
void childAdded(SGPropertyNode* parent, SGPropertyNode* child) override;
void childRemoved(SGPropertyNode* parent, SGPropertyNode* child) override;
void valueChanged(SGPropertyNode * node) override;
virtual void childAdded( SGPropertyNode * parent,
SGPropertyNode * child );
virtual void childRemoved( SGPropertyNode * parent,
SGPropertyNode * child );
virtual void valueChanged (SGPropertyNode * node);
osg::Texture2D* getTexture() const;
@@ -225,21 +211,21 @@ namespace canvas
protected:
CanvasMgr *_canvas_mgr {nullptr};
CanvasMgr *_canvas_mgr;
std::unique_ptr<EventManager> _event_manager;
boost::scoped_ptr<EventManager> _event_manager;
int _size_x {-1},
_size_y {-1},
_view_width {-1},
_view_height {-1};
int _size_x,
_size_y,
_view_width,
_view_height;
PropertyObject<int> _status;
PropertyObject<std::string> _status_msg;
bool _sampling_dirty {false},
_render_dirty {true},
_visible {true};
bool _sampling_dirty,
_render_dirty,
_visible;
ODGauge _texture;
@@ -249,9 +235,7 @@ namespace canvas
ElementWeakPtr _focus_element;
CullCallbackPtr _cull_callback;
/** Used to disable automatic lazy rendering (culling) */
bool _render_always {false};
bool _render_always; //!< Used to disable automatic lazy rendering (culling)
std::vector<SGPropertyNode*> _dirty_placements;
std::vector<Placements> _placements;
@@ -268,8 +252,8 @@ namespace canvas
static SystemAdapterPtr _system_adapter;
Canvas(const Canvas&) = delete;
Canvas& operator=(const Canvas&) = delete;
Canvas(const Canvas&); // = delete;
Canvas& operator=(const Canvas&); // = delete;
};
} // namespace canvas

View File

@@ -1,5 +1,4 @@
///@file
/// Canvas Event for event model similar to DOM Level 3 Event Model
// Canvas Event for event model similar to DOM Level 3 Event Model
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -17,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasEvent.hxx"
namespace simgear
@@ -126,10 +123,10 @@ namespace canvas
//----------------------------------------------------------------------------
std::string Event::typeToStr(int type)
{
auto const& map_by_id = getTypeMap().by<id>();
TypeMap const& type_map = getTypeMap();
auto it = map_by_id.find(type);
if( it == map_by_id.end() )
TypeMap::map_by<id>::const_iterator it = type_map.by<id>().find(type);
if( it == type_map.by<id>().end() )
return "unknown";
return it->second;
}

View File

@@ -65,11 +65,6 @@ namespace canvas
// of the actual event instances.
virtual ~Event();
/**
* Clone event and set to the given type (Same type if not specified)
*/
virtual Event* clone(int type = 0) const = 0;
/**
* Get whether this events support bubbling
*/
@@ -115,14 +110,7 @@ namespace canvas
*/
bool defaultPrevented() const;
/**
* Register a new type string or get the id of an existing type string
*
* @param type Type string
* @return Id of the given @a type
*/
static int getOrRegisterType(const std::string& type);
static int strToType(const std::string& type);
static std::string typeToStr(int type);

View File

@@ -1,5 +1,4 @@
///@file
/// Manage event handling inside a Canvas similar to the DOM Level 3 Event Model
// Manage event handling inside a Canvas similar to the DOM Level 3 Event Model
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -17,12 +16,9 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasEventManager.hxx"
#include "elements/CanvasElement.hxx"
#include "events/MouseEvent.hxx"
#include <simgear/canvas/events/MouseEvent.hxx>
#include <simgear/canvas/elements/CanvasElement.hxx>
#include <cmath>
namespace simgear
@@ -117,8 +113,6 @@ namespace canvas
return handled;
}
case Event::DRAG:
case Event::DRAG_START:
case Event::DRAG_END:
if( !_last_mouse_down.valid() )
return false;
else

View File

@@ -1,5 +1,4 @@
///@file
/// Manage event handling inside a Canvas similar to the DOM Level 3 Event Model
// Manage event handling inside a Canvas similar to the DOM Level 3 Event Model
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//

View File

@@ -1,5 +1,4 @@
///@file
/// Mapping between canvas gui Event types and their names
// Mapping between canvas gui Event types and their names
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -26,8 +25,6 @@ ENUM_MAPPING(MOUSE_UP, "mouseup", MouseEvent)
ENUM_MAPPING(CLICK, "click", MouseEvent)
ENUM_MAPPING(DBL_CLICK, "dblclick", MouseEvent)
ENUM_MAPPING(DRAG, "drag", MouseEvent)
ENUM_MAPPING(DRAG_START, "dragstart", MouseEvent)
ENUM_MAPPING(DRAG_END, "dragend", MouseEvent)
ENUM_MAPPING(WHEEL, "wheel", MouseEvent)
ENUM_MAPPING(MOUSE_MOVE, "mousemove", MouseEvent)
ENUM_MAPPING(MOUSE_OVER, "mouseover", MouseEvent)

View File

@@ -1,6 +1,5 @@
///@file
/// Visitor for traversing a canvas element hierarchy similar to the traversal
/// of the DOM Level 3 Event Model
// Visitor for traversing a canvas element hierarchy similar to the traversal
// of the DOM Level 3 Event Model
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -18,11 +17,9 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasEvent.hxx"
#include "CanvasEventVisitor.hxx"
#include "elements/CanvasElement.hxx"
#include <simgear/canvas/elements/CanvasElement.hxx>
namespace simgear
{

View File

@@ -1,6 +1,5 @@
///@file
/// Visitor for traversing a canvas element hierarchy similar to the traversal
/// of the DOM Level 3 Event Model
// Visitor for traversing a canvas element hierarchy similar to the traversal
// of the DOM Level 3 Event Model
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//

View File

@@ -1,5 +1,4 @@
///@file
/// Canvas with 2D rendering API
// Canvas with 2D rendering API
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -17,12 +16,12 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasMgr.hxx"
#include "Canvas.hxx"
#include "CanvasEventManager.hxx"
#include <boost/bind.hpp>
namespace simgear
{
namespace canvas

View File

@@ -1,5 +1,4 @@
///@file
/// Canvas with 2D rendering API
// Canvas with 2D rendering API
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -62,7 +61,7 @@ namespace canvas
protected:
void elementCreated(PropertyBasedElementPtr element) override;
virtual void elementCreated(PropertyBasedElementPtr element);
};
} // namespace canvas

View File

@@ -1,5 +1,4 @@
///@file
/// Canvas placement for placing a canvas texture onto osg objects
// Canvas placement for placing a canvas texture onto osg objects.
//
// It also provides a SGPickCallback for passing mouse events to the canvas and
// manages emissive lighting of the placed canvas.
@@ -20,8 +19,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "Canvas.hxx"
#include "CanvasObjectPlacement.hxx"
#include <simgear/canvas/events/MouseEvent.hxx>

View File

@@ -1,5 +1,5 @@
///@file
/// Placement for putting a canvas texture onto OpenSceneGraph objects
/// Placement for putting a canvas texture onto OpenSceneGraph objects.
///
/// It also provides a SGPickCallback for passing mouse events to the canvas and
/// manages emissive lighting of the placed canvas.
@@ -60,7 +60,7 @@ namespace canvas
*/
void setCaptureEvents(bool enable);
bool childChanged(SGPropertyNode* child) override;
virtual bool childChanged(SGPropertyNode* child);
protected:
typedef SGSharedPtr<SGPickCallback> PickCallbackPtr;

View File

@@ -1,5 +1,4 @@
///@file
/// Base class for canvas placements
// Base class for canvas placements
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -17,7 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasPlacement.hxx"
#include <simgear/props/props.hxx>

View File

@@ -1,5 +1,4 @@
///@file
/// Base class for canvas placements
// Base class for canvas placements
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -41,8 +40,9 @@ namespace canvas
protected:
SGPropertyNode_ptr _node;
Placement(const Placement&) = delete;
Placement& operator=(const Placement&) = delete;
private:
Placement(const Placement&) /* = delete */;
Placement& operator=(const Placement&) /* = delete */;
};
} // namespace canvas

View File

@@ -1,5 +1,4 @@
///@file
/// Adapter for using the canvas with different applications
// Adapter for using the canvas with different applications
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -30,10 +29,6 @@ namespace HTTP { class Client; }
namespace canvas
{
/**
* Provides access to different required systems of the application to the
* Canvas
*/
class SystemAdapter
{
public:

View File

@@ -1,5 +1,4 @@
///@file
/// Window for placing a Canvas onto it (for dialogs, menus, etc.)
// Window for placing a Canvas onto it (for dialogs, menus, etc.)
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -17,17 +16,18 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasMgr.hxx"
#include "CanvasSystemAdapter.hxx"
#include "CanvasWindow.hxx"
#include <simgear/canvas/Canvas.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/scene/util/OsgMath.hxx>
#include <osgGA/GUIEventHandler>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/foreach.hpp>
namespace simgear
{
namespace canvas
@@ -42,6 +42,9 @@ 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"),
@@ -88,7 +91,7 @@ namespace canvas
_capture_events = node->getBoolValue();
else if( name == "decoration-border" )
parseDecorationBorder(node->getStringValue());
else if( strutils::starts_with(name, "shadow-")
else if( boost::starts_with(name, "shadow-")
|| name == "content-size" )
_attributes_dirty |= DECORATION;
else
@@ -99,10 +102,16 @@ namespace canvas
Image::valueChanged(node);
}
//----------------------------------------------------------------------------
osg::Group* Window::getGroup()
{
return getMatrixTransform();
}
//----------------------------------------------------------------------------
const SGVec2<float> Window::getPosition() const
{
auto const& m = getMatrix();
const osg::Matrix& m = getMatrixTransform()->getMatrix();
return SGVec2<float>( m(3, 0), m(3, 1) );
}
@@ -213,19 +222,6 @@ namespace canvas
_resize_left = getRegion().l() + offset.x();
}
//----------------------------------------------------------------------------
bool Window::handleEvent(const EventPtr& event)
{
if( auto mouse_event = dynamic_cast<MouseEvent*>(event.get()) )
{
mouse_event->local_pos =
mouse_event->client_pos =
mouse_event->screen_pos - toOsg(getPosition());
}
return Image::handleEvent(event);
}
//----------------------------------------------------------------------------
void Window::parseDecorationBorder(const std::string& str)
{

View File

@@ -1,5 +1,4 @@
///@file
/// Window for placing a Canvas onto it (for dialogs, menus, etc.)
// Window for placing a Canvas onto it (for dialogs, menus, etc.)
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -71,6 +70,7 @@ 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;
@@ -97,8 +97,6 @@ namespace canvas
void handleResize( uint8_t mode,
const osg::Vec2f& offset = osg::Vec2f() );
bool handleEvent(const EventPtr& event) override;
protected:
enum Attributes
@@ -106,7 +104,7 @@ namespace canvas
DECORATION = 1
};
uint32_t _attributes_dirty {0};
uint32_t _attributes_dirty;
CanvasPtr _canvas_decoration;
CanvasWeakPtr _canvas_content;
@@ -115,8 +113,8 @@ namespace canvas
ImagePtr _image_content,
_image_shadow;
bool _resizable {false},
_capture_events {true};
bool _resizable,
_capture_events;
PropertyObject<int> _resize_top,
_resize_right,

View File

@@ -1,5 +1,4 @@
///@file
/// Owner Drawn Gauge helper class
// Owner Drawn Gauge helper class
//
// Written by Harald JOHNSEN, started May 2005.
//
@@ -7,9 +6,9 @@
//
// Ported to OSG by Tim Moore - Jun 2007
//
// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April
// 2012 Supports now multisampling/mipmapping, usage of the stencil buffer and
// placing the texture in the scene by certain filter criteria.
// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April 2012
// Supports now multisampling/mipmapping, usage of the stencil buffer and placing
// the texture in the scene by certain filter criteria
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public

View File

@@ -1,5 +1,4 @@
///@file
/// Owner Drawn Gauge helper class
// Owner Drawn Gauge helper class
//
// Written by Harald JOHNSEN, started May 2005.
//
@@ -7,9 +6,9 @@
//
// Ported to OSG by Tim Moore - Jun 2007
//
// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April
// 2012 Supports now multisampling/mipmapping, usage of the stencil buffer and
// placing the texture in the scene by certain filter criteria.
// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April 2012
// Supports now multisampling/mipmapping, usage of the stencil buffer and placing
// the texture in the scene by certain filter criteria
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public

View File

@@ -35,7 +35,7 @@ void SHVector2_dtor(SHVector2 *v) {
}
void SHVector3_ctor(SHVector3 *v) {
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
v->vec = _mm_setzero_ps();
#else
v->x=0.0f; v->y=0.0f; v->z=0.0f;
@@ -46,7 +46,7 @@ void SHVector3_dtor(SHVector3 *v) {
}
void SHVector4_ctor(SHVector4 *v) {
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
v->vec = _mm_setzero_ps();
#else
v->x=0.0f; v->y=0.0f; v->z=0.0f; v->w=0.0f;
@@ -57,7 +57,7 @@ void SHVector4_dtor(SHVector4 *v) {
}
void SHRectangle_ctor(SHRectangle *r) {
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
r->vec = _mm_setzero_ps();
#else
r->x=0.0f; r->y=0.0f; r->w=0.0f; r->h=0.0f;
@@ -148,7 +148,7 @@ int shLineLineXsection(SHVector2 *o1, SHVector2 *v1,
return 1;
}
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# ifdef __SSE3__
# include <pmmintrin.h>
inline float hsum_ps_sse(__m128 v) {

View File

@@ -21,17 +21,7 @@
#ifndef __SHVECTORS_H
#define __SHVECTORS_H
#ifdef HAVE_CONFIG_H
# include <simgear/simgear_config.h>
#endif
#ifdef ENABLE_SIMD
# ifdef __SSE__
// # define SHIVA_USE_SIMD
# endif
# endif
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# include <xmmintrin.h>
float hsum_ps_sse(__m128 v);
#endif
@@ -51,7 +41,7 @@ void SHVector2_dtor(SHVector2 *v);
typedef struct
{
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
union ALIGN16 {
__m128 vec;
struct { SHfloat x,y,z,w; };
@@ -66,7 +56,7 @@ void SHVector3_dtor(SHVector3 *v);
typedef struct
{
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
union ALIGN16 {
__m128 vec;
struct { SHfloat x,y,z,w; };
@@ -81,7 +71,7 @@ void SHVector4_dtor(SHVector4 *v);
typedef struct
{
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
union ALIGN16 {
__m128 vec;
struct { SHfloat x,y,w,h; };
@@ -98,7 +88,7 @@ void shRectangleSet(SHRectangle *r, SHfloat x,
typedef struct
{
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
union ALIGN16 {
__m128 mtx[4];
SHfloat m[4][4];
@@ -127,7 +117,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
*--------------------------------------------------------- */
#define SET2(v,xs,ys) { v.x=xs; v.y=ys; }
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define SET3(v,xs,ys,zs,ws) { v.vec=_mm_set_ps(0,zs,ys,xs); }
# define SET4(v,xs,ys,zs,ws) { v.vec=_mm_set_ps(ws,zs,ys,xs); }
#else
@@ -136,7 +126,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
#endif
#define SET2V(v1,v2) { v1.x=v2.x; v1.y=v2.y; }
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define SET3V(v1,v2) { v1.vec=v2.vec; }
# define SET4V(v1,v2) { v1.vec=v2.vec; }
#else
@@ -157,7 +147,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
#define EQ4V(v1,v2) ( v1.x==v2.x && v1.y==v2.y && v1.z==v2.z && v1.w==v2.w )
#define ADD2(v,xx,yy) { v.x+=xx; v.y+=yy; }
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define ADD3(v,xx,yy,zz,ww) { v.vec=_mm_add_ps(v.vec,_mm_set_ps(0,zz,yy,xx)); }
# define ADD4(v,xx,yy,zz,ww) { v.vec=_mm_add_ps(v.vec,_mm_set_ps(ww,zz,yy,xx)); }
#else
@@ -166,7 +156,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
#endif
#define ADD2V(v1,v2) { v1.x+=v2.x; v1.y+=v2.y; }
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define ADD3V(v1,v2) { v1.vec=_mm_add_ps(v1.vec,v2.vec); }
# define ADD4V(v1,v2) { v1.vec=_mm_add_ps(v1.vec,v2.vec); }
#else
@@ -175,7 +165,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
#endif
#define SUB2(v,xx,yy) { v.x-=xx; v.y-=yy; }
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define SUB3(v,xx,yy,zz,ww) { v.vec=_mm_sub_ps(v.vec,_mm_set_ps(0,zz,yy,xx)); }
# define SUB4(v,xx,yy,zz,ww) { v.vec=_mm_sub_ps(v.vec,_mm_set_ps(ww,zz,yy,xx)); }
#else
@@ -184,7 +174,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
#endif
#define SUB2V(v1,v2) { v1.x-=v2.x; v1.y-=v2.y; }
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define SUB3V(v1,v2) { v1.vec=_mm_sub_ps(v1.vec,v2.vec); }
# define SUB4V(v1,v2) { v1.vec=_mm_sub_ps(v1.vec,v2.vec); }
#else
@@ -193,7 +183,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
#endif
#define MUL2(v,f) { v.x*=f; v.y*=f; }
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define MUL3(v,f) { v.vec=_mm_mul_ps(v.vec,_mm_set1_ps(f)); }
# define MUL4(v,f) { v.vec=_mm_mul_ps(v.vec,_mm_set1_ps(f)); }
#else
@@ -202,7 +192,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
#endif
#define DIV2(v,f) { v.x/=f; v.y/=f; }
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define DIV3(v,f) { v.vec=_mm_div_ps(v.vec,_mm_set1_ps(f)); }
# define DIV4(v,f) { v.vec=_mm_div_ps(v.vec,_mm_set1_ps(f)); }
#else
@@ -211,7 +201,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
#endif
#define ABS2(v) { v.x=SH_ABS(v.x); v.y=SH_ABS(v.y); }
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define ABS_MASK _mm_set1_ps(-0.f)
# define ABS3(v) { v.vec=_mm_andnot_ps(ABS_MASK, v.vec); }
# define ABS4(v) { v.vec=_mm_andnot_ps(ABS_MASK, v.vec); }
@@ -233,7 +223,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
#define NORMALIZE4(v) { SHfloat n=NORM4(v); DIV4(v,n); }
#define DOT2(v1,v2) (v1.x*v2.x + v1.y*v2.y)
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define DOT4(v1,v2) hsum_ps_sse(_mm_mul_ps(v1.vec,v2.vec))
# define DOT4(v1,v2) hsum_ps_sse(_mm_mul_ps(v1.vec,v2.vec))
#else
@@ -247,7 +237,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
#define ANGLE2N(v1,v2) (SH_ACOS( DOT2(v1,v2) ))
#define OFFSET2V(v, o, s) { v.x += o.x*s; v.y += o.y*s; }
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define OFFSET4V(v, o, s) { v.vec=_mm_add_ps(v.vec,_mm_mul_ps(o.vec,_mm_set1_ps(s))); }
# define OFFSET4V(v, o, s) { v.vec=_mm_add_ps(v.vec,_mm_mul_ps(o.vec,_mm_set1_ps(s))); }
#else
@@ -259,7 +249,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
* Macros for matrix operations
*-----------------------------------------------------*/
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define SETMAT(mat, m00, m01, m02, m10, m11, m12, m20, m21, m22) { \
mat.mtx[0] = _mm_set_ps(0,m02,m01,m00); \
mat.mtx[1] = _mm_set_ps(0,m12,m11,m10); \
@@ -272,7 +262,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
mat.m[2][0] = m20; mat.m[2][1] = m21; mat.m[2][2] = m22; }
#endif
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define SETMATMAT(m1, m2) { \
m1.mtx[0] = m2.mtx[0]; \
m1.mtx[1] = m2.mtx[1]; \
@@ -285,7 +275,7 @@ int i,j; \
m1.m[i][j] = m2.m[i][j]; }
#endif
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define MULMATS(mat, s) { \
mat.mtx[0] = _mm_mul_ps(mat.mtx[0],_mm_set1_ps(s)); \
mat.mtx[1] = _mm_mul_ps(mat.mtx[1],_mm_set1_ps(s)); \
@@ -298,7 +288,7 @@ int i,j; \
mat.m[i][j] *= s; }
#endif
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define DIVMATS(mat, s) { \
mat.mtx[0] = _mm_mul_ps(mat.mtx[0],_mm_set1_ps(1/s)); \
mat.mtx[1] = _mm_mul_ps(mat.mtx[1],_mm_set1_ps(1/s)); \
@@ -311,7 +301,7 @@ int i,j; \
mat.m[i][j] /= s; }
#endif
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define MULMATMAT(m2, m1, mout) { \
int i,j; \
for (i=0;i<4;i++) { \

View File

@@ -1,5 +1,4 @@
///@file
/// osg::Operation to initialize the OpenVG context used for path rendering
// osg::Operation to initialize the OpenVG context used for path rendering
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//

View File

@@ -1,5 +1,4 @@
///@file
/// osg::Operation to initialize the OpenVG context used for path rendering
// osg::Operation to initialize the OpenVG context used for path rendering
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//

View File

@@ -1,5 +1,4 @@
///@file
/// Canvas forward declarations
// Canvas forward declarations
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//

View File

@@ -9,6 +9,10 @@ set(HEADERS
CanvasText.hxx
)
set(DETAIL_HEADERS
detail/add_segment_variadic.hxx
)
set(SOURCES
CanvasElement.cxx
CanvasGroup.cxx
@@ -19,6 +23,7 @@ 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

View File

@@ -1,5 +1,4 @@
///@file
/// Interface for 2D Canvas element
// Interface for 2D Canvas element
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -17,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasElement.hxx"
#include <simgear/canvas/Canvas.hxx>
#include <simgear/canvas/CanvasEventVisitor.hxx>
@@ -32,6 +29,10 @@
#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>
@@ -211,22 +212,22 @@ namespace canvas
//----------------------------------------------------------------------------
void Element::onDestroy()
{
if( !_scene_group.valid() )
if( !_transform.valid() )
return;
// The transform node keeps a reference on this element, so ensure it is
// deleted.
for(osg::Group* parent: _scene_group->getParents())
BOOST_FOREACH(osg::Group* parent, _transform->getParents())
{
parent->removeChild(_scene_group.get());
parent->removeChild(_transform.get());
}
// Hide in case someone still holds a reference
setVisible(false);
removeListener();
_parent = nullptr;
_scene_group = nullptr;
_parent = 0;
_transform = 0;
}
//----------------------------------------------------------------------------
@@ -244,8 +245,29 @@ namespace canvas
//----------------------------------------------------------------------------
void Element::update(double dt)
{
if( isVisible() )
updateImpl(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;
}
}
//----------------------------------------------------------------------------
@@ -272,7 +294,8 @@ namespace canvas
//----------------------------------------------------------------------------
void Element::setFocus()
{
if( auto canvas = _canvas.lock() )
CanvasPtr canvas = _canvas.lock();
if( canvas )
canvas->setFocusElement(this);
}
@@ -316,8 +339,7 @@ namespace canvas
if( listeners == _listener.end() )
return false;
for(auto const& listener: listeners->second)
{
BOOST_FOREACH(EventListener const& listener, listeners->second)
try
{
listener(event);
@@ -330,7 +352,6 @@ namespace canvas
"canvas::Element: event handler error: '" << ex.what() << "'"
);
}
}
return true;
}
@@ -372,9 +393,9 @@ namespace canvas
getBoundingBox()
#endif
.contains(osg::Vec3f(local_pos, 0));
else if( _scene_group.valid() )
else if( _transform.valid() )
// ... for other elements, i.e. groups only a bounding sphere is available
return _scene_group->getBound().contains(osg::Vec3f(parent_pos, 0));
return _transform->getBound().contains(osg::Vec3f(parent_pos, 0));
else
return false;
}
@@ -383,32 +404,34 @@ namespace canvas
//----------------------------------------------------------------------------
void Element::setVisible(bool visible)
{
if( _scene_group.valid() )
if( _transform.valid() )
// TODO check if we need another nodemask
_scene_group->setNodeMask(visible ? 0xffffffff : 0);
_transform->setNodeMask(visible ? 0xffffffff : 0);
}
//----------------------------------------------------------------------------
bool Element::isVisible() const
{
return _scene_group.valid() && _scene_group->getNodeMask() != 0;
return _transform.valid() && _transform->getNodeMask() != 0;
}
//----------------------------------------------------------------------------
osg::MatrixTransform* Element::getSceneGroup() const
osg::MatrixTransform* Element::getMatrixTransform()
{
return _scene_group.get();
return _transform.get();
}
//----------------------------------------------------------------------------
osg::MatrixTransform const* Element::getMatrixTransform() const
{
return _transform.get();
}
//----------------------------------------------------------------------------
osg::Vec2f Element::posToLocal(const osg::Vec2f& pos) const
{
if( !_scene_group )
// TODO log warning?
return pos;
updateMatrix();
const osg::Matrix& m = _scene_group->getInverseMatrix();
getMatrix();
const osg::Matrix& m = _transform->getInverseMatrix();
return osg::Vec2f
(
m(0, 0) * pos[0] + m(1, 0) * pos[1] + m(3, 0),
@@ -461,6 +484,9 @@ namespace canvas
{
if( child->getNameString() == NAME_TRANSFORM )
{
if( !_transform.valid() )
return;
if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
{
SG_LOG
@@ -497,7 +523,7 @@ namespace canvas
if( parent == _node )
{
const std::string& name = child->getNameString();
if( strutils::starts_with(name, "data-") )
if( boost::starts_with(name, "data-") )
return;
else if( StyleInfo const* style_info = getStyleInfo(name) )
{
@@ -512,7 +538,7 @@ namespace canvas
}
else if( name == "update" )
return update(0);
else if( strutils::starts_with(name, "blend-") )
else if( boost::starts_with(name, "blend-") )
return (void)(_attributes_dirty |= BLEND_FUNC);
}
else if( parent
@@ -536,10 +562,6 @@ namespace canvas
//----------------------------------------------------------------------------
void Element::setClip(const std::string& clip)
{
if( !_scene_group )
// TODO warn?
return;
osg::StateSet* ss = getOrCreateStateSet();
if( !ss )
return;
@@ -553,8 +575,8 @@ namespace canvas
// TODO generalize CSS property parsing
const std::string RECT("rect(");
if( !strutils::ends_with(clip, ")")
|| !strutils::starts_with(clip, RECT) )
if( !boost::ends_with(clip, ")")
|| !boost::starts_with(clip, RECT) )
{
SG_LOG(SG_GENERAL, SG_WARN, "Canvas: invalid clip: " << clip);
return;
@@ -594,7 +616,7 @@ namespace canvas
}
if( !_scissor )
_scissor = new RelativeScissor(_scene_group.get());
_scissor = new RelativeScissor(_transform.get());
// <top>, <right>, <bottom>, <left>
_scissor->x() = values[3];
@@ -618,26 +640,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
@@ -651,8 +673,8 @@ namespace canvas
osg::BoundingBox bb;
if( _scene_group.valid() )
bb.expandBy( _scene_group->getBound() );
if( _transform.valid() )
bb.expandBy(_transform->getBound());
return bb;
}
@@ -686,11 +708,73 @@ namespace canvas
//----------------------------------------------------------------------------
osg::Matrix Element::getMatrix() const
{
if( !_scene_group )
if( !_transform )
return osg::Matrix::identity();
updateMatrix();
return _scene_group->getMatrix();
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;
}
//----------------------------------------------------------------------------
@@ -704,8 +788,11 @@ namespace canvas
PropertyBasedElement(node),
_canvas( canvas ),
_parent( parent ),
_scene_group( new osg::MatrixTransform ),
_style( parent_style )
_attributes_dirty( 0 ),
_transform( new osg::MatrixTransform ),
_style( parent_style ),
_scissor( 0 ),
_drawable( 0 )
{
staticInit();
@@ -717,15 +804,15 @@ namespace canvas
);
// Ensure elements are drawn in order they appear in the element tree
_scene_group
->getOrCreateStateSet()
->setRenderBinDetails(
0,
"PreOrderBin",
osg::StateSet::OVERRIDE_RENDERBIN_DETAILS
);
_transform->getOrCreateStateSet()
->setRenderBinDetails
(
0,
"PreOrderBin",
osg::StateSet::OVERRIDE_RENDERBIN_DETAILS
);
_scene_group->setUserData( new OSGUserData(this) );
_transform->setUserData( new OSGUserData(this) );
}
//----------------------------------------------------------------------------
@@ -813,21 +900,11 @@ namespace canvas
void Element::setDrawable( osg::Drawable* drawable )
{
_drawable = drawable;
assert( _drawable );
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;
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
geode->addDrawable(_drawable);
_scene_group->addChild(geode);
_transform->addChild(geode);
}
//----------------------------------------------------------------------------
@@ -835,109 +912,18 @@ namespace canvas
{
if( _drawable.valid() )
return _drawable->getOrCreateStateSet();
else if( _scene_group.valid() )
return _scene_group->getOrCreateStateSet();
else
return nullptr;
if( _transform.valid() )
return _transform->getOrCreateStateSet();
return 0;
}
//----------------------------------------------------------------------------
void Element::setupStyle()
{
for(auto const& style: _style)
BOOST_FOREACH( Style::value_type 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

View File

@@ -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,7 +49,6 @@ namespace canvas
public PropertyBasedElement
{
public:
using SceneGroupWeakPtr = osg::observer_ptr<osg::MatrixTransform>;
/**
* Store pointer to window as user data
@@ -95,7 +94,7 @@ namespace canvas
*
*/
virtual ~Element() = 0;
void onDestroy() override;
virtual void onDestroy();
ElementPtr getParent() const;
CanvasWeakPtr getCanvas() const;
@@ -105,7 +104,7 @@ namespace canvas
*
* @param dt Frame time in seconds
*/
void update(double dt) override;
virtual void update(double dt);
bool addEventListener(const std::string& type, const EventListener& cb);
virtual void clearEventListener();
@@ -143,20 +142,19 @@ namespace canvas
*/
virtual bool isVisible() const;
/**
* Get the according group in the OSG scene graph
*/
// TODO ref_ptr
osg::MatrixTransform* getSceneGroup() const;
osg::MatrixTransform* getMatrixTransform();
osg::MatrixTransform const* getMatrixTransform() const;
/**
* Transform position to local coordinages.
*/
osg::Vec2f posToLocal(const osg::Vec2f& pos) const;
void childAdded(SGPropertyNode* parent, SGPropertyNode* child) override;
void childRemoved(SGPropertyNode* parent, SGPropertyNode* child) override;
void valueChanged(SGPropertyNode* child) override;
virtual void childAdded( SGPropertyNode * parent,
SGPropertyNode * child );
virtual void childRemoved( SGPropertyNode * parent,
SGPropertyNode * child );
virtual void valueChanged(SGPropertyNode * child);
virtual bool setStyle( const SGPropertyNode* child,
const StyleInfo* style_info = 0 );
@@ -219,14 +217,13 @@ namespace canvas
*/
template<typename Derived>
static
std::enable_if_t<
std::is_base_of<Element, Derived>::value,
typename boost::enable_if<
boost::is_base_of<Element, Derived>,
ElementPtr
>
create( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& style = Style(),
Element* parent = NULL )
>::type create( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& style = Style(),
Element* parent = NULL )
{
return ElementPtr( new Derived(canvas, node, style, parent) );
}
@@ -254,13 +251,13 @@ namespace canvas
CanvasWeakPtr _canvas;
ElementWeakPtr _parent;
mutable uint32_t _attributes_dirty = 0;
mutable uint32_t _attributes_dirty;
SceneGroupWeakPtr _scene_group;
std::vector<TransformType> _transform_types;
osg::observer_ptr<osg::MatrixTransform> _transform;
std::vector<TransformType> _transform_types;
Style _style;
RelativeScissor *_scissor = nullptr;
RelativeScissor *_scissor;
typedef std::vector<EventListener> Listener;
typedef std::map<int, Listener> ListenerMap;
@@ -587,15 +584,11 @@ namespace canvas
void setupStyle();
void updateMatrix() const;
virtual void updateImpl(double dt);
private:
osg::ref_ptr<osg::Drawable> _drawable;
Element(const Element&) = delete;
Element(const Element&);// = delete
template<class Derived>
static Derived& derived_cast(Element& el)

View File

@@ -1,5 +1,4 @@
///@file
/// A group of 2D Canvas elements
// A group of 2D Canvas elements
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -17,17 +16,18 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasGroup.hxx"
#include "CanvasImage.hxx"
#include "CanvasMap.hxx"
#include "CanvasPath.hxx"
#include "CanvasText.hxx"
#include <simgear/canvas/CanvasEventVisitor.hxx>
#include <simgear/canvas/events/MouseEvent.hxx>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/lambda/core.hpp>
namespace simgear
{
namespace canvas
@@ -46,7 +46,7 @@ namespace canvas
ElementFactories Group::_child_factories;
const std::string Group::TYPE_NAME = "group";
void warnSceneGroupExpired(const char* member_name)
void warnTransformExpired(const char* member_name)
{
SG_LOG( SG_GENERAL,
SG_WARN,
@@ -133,58 +133,63 @@ namespace canvas
//----------------------------------------------------------------------------
ElementPtr Group::getElementById(const std::string& id)
{
if( !_scene_group.valid() )
if( !_transform.valid() )
{
warnSceneGroupExpired("getElementById");
return {};
warnTransformExpired("getElementById");
return ElementPtr();
}
// 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)
std::vector<GroupPtr> groups;
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
{
const ElementPtr& el = getChildByIndex(i);
if( el->get<std::string>("id") == id )
return el;
if( Group* child_group = dynamic_cast<Group*>(el.get()) )
child_groups.push_back(child_group);
Group* group = dynamic_cast<Group*>(el.get());
if( group )
groups.push_back(group);
}
for(auto group: child_groups)
BOOST_FOREACH( GroupPtr group, groups )
{
if( ElementPtr el = group->getElementById(id) )
ElementPtr el = group->getElementById(id);
if( el )
return el;
}
return {};
return ElementPtr();
}
//----------------------------------------------------------------------------
void Group::clearEventListener()
{
Element::clearEventListener();
if( !_transform.valid() )
return warnTransformExpired("clearEventListener");
if( !_scene_group.valid() )
return warnSceneGroupExpired("clearEventListener");
// TODO should this be recursive?
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
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);
Element::update(dt);
}
//----------------------------------------------------------------------------
bool Group::traverse(EventVisitor& visitor)
{
if( _scene_group.valid() )
// Iterate in reverse order as last child is displayed on top
for(size_t i = _transform->getNumChildren(); i --> 0;)
{
// 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;
}
if( getChildByIndex(i)->accept(visitor) )
return true;
}
return false;
}
@@ -199,13 +204,13 @@ namespace canvas
bool handled = setStyleImpl(style, style_info);
if( style_info->inheritable )
{
if( !_scene_group.valid() )
if( !_transform.valid() )
{
warnSceneGroupExpired("setStyle");
warnTransformExpired("setStyle");
return false;
}
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
handled |= getChildByIndex(i)->setStyle(style, style_info);
}
@@ -215,20 +220,26 @@ namespace canvas
//----------------------------------------------------------------------------
osg::BoundingBox Group::getTransformedBounds(const osg::Matrix& m) const
{
if( !_scene_group.valid() )
osg::BoundingBox bb;
if( !_transform.valid() )
{
warnSceneGroupExpired("getTransformedBounds");
return {};
warnTransformExpired("getTransformedBounds");
return bb;
}
osg::BoundingBox bb;
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
{
auto child = getChildByIndex(i);
if( !child || !child->isVisible() )
const ElementPtr& child = getChildByIndex(i);
if( !child->getMatrixTransform()->getNodeMask() )
continue;
bb.expandBy( child->getTransformedBounds(child->getMatrix() * m) );
bb.expandBy
(
child->getTransformedBounds
(
child->getMatrixTransform()->getMatrix() * m
)
);
}
return bb;
@@ -244,15 +255,6 @@ 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)
{
@@ -262,13 +264,13 @@ namespace canvas
ElementFactory child_factory = getChildFactory( child->getNameString() );
if( child_factory )
{
if( !_scene_group.valid() )
return warnSceneGroupExpired("childAdded");
if( !_transform.valid() )
return warnTransformExpired("childAdded");
ElementPtr element = child_factory(_canvas, child, _style, this);
// Add to osg scene graph...
_scene_group->addChild(element->getSceneGroup());
_transform->addChild( element->getMatrixTransform() );
// ...and ensure correct ordering
handleZIndexChanged(element);
@@ -289,7 +291,7 @@ namespace canvas
if( getChildFactory(node->getNameString()) )
{
if( !_scene_group.valid() )
if( !_transform.valid() )
// If transform is destroyed also all children are destroyed, so we can
// not do anything here.
return;
@@ -319,7 +321,7 @@ namespace canvas
void Group::childChanged(SGPropertyNode* node)
{
SGPropertyNode* parent = node->getParent();
SGPropertyNode* grand_parent = parent ? parent->getParent() : nullptr;
SGPropertyNode* grand_parent = parent ? parent->getParent() : NULL;
if( grand_parent == _node
&& node->getNameString() == "z-index" )
@@ -329,18 +331,16 @@ namespace canvas
//----------------------------------------------------------------------------
void Group::handleZIndexChanged(ElementPtr child, int z_index)
{
if( !child || !_scene_group.valid() )
if( !child || !_transform.valid() )
return;
// 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),
osg::ref_ptr<osg::MatrixTransform> tf = child->getMatrixTransform();
size_t index = _transform->getChildIndex(tf),
index_new = index;
for(;; ++index_new)
{
if( index_new + 1 == _scene_group->getNumChildren() )
if( index_new + 1 == _transform->getNumChildren() )
break;
// Move to end of block with same index (= move upwards until the next
@@ -367,8 +367,8 @@ namespace canvas
return;
}
_scene_group->removeChild(index);
_scene_group->insertChild(index_new, tf);
_transform->removeChild(index);
_transform->insertChild(index_new, tf);
SG_LOG
(
@@ -381,27 +381,24 @@ namespace canvas
//----------------------------------------------------------------------------
ElementPtr Group::getChildByIndex(size_t index) const
{
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();
assert(_transform.valid());
OSGUserData* ud =
static_cast<OSGUserData*>(_transform->getChild(index)->getUserData());
assert(ud);
return ud->element;
}
//----------------------------------------------------------------------------
ElementPtr Group::findChild( const SGPropertyNode* node,
const std::string& id ) const
{
if( !_scene_group.valid() )
if( !_transform.valid() )
{
warnSceneGroupExpired("findChild");
return {};
warnTransformExpired("findChild");
return ElementPtr();
}
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
{
ElementPtr el = getChildByIndex(i);
@@ -417,7 +414,7 @@ namespace canvas
}
}
return {};
return ElementPtr();
}
} // namespace canvas

View File

@@ -1,5 +1,4 @@
///@file
/// A group of 2D Canvas elements
// A group of 2D Canvas elements
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -87,15 +86,16 @@ namespace canvas
*/
ElementPtr getElementById(const std::string& id);
void clearEventListener() override;
virtual void clearEventListener();
bool traverse(EventVisitor& visitor) override;
virtual void update(double dt);
bool setStyle( const SGPropertyNode* child,
const StyleInfo* style_info = 0 ) override;
virtual bool traverse(EventVisitor& visitor);
osg::BoundingBox
getTransformedBounds(const osg::Matrix& m) const override;
virtual bool setStyle( const SGPropertyNode* child,
const StyleInfo* style_info = 0 );
virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
protected:
@@ -107,11 +107,9 @@ namespace canvas
*/
virtual ElementFactory getChildFactory(const std::string& type) const;
void updateImpl(double dt) override;
void childAdded(SGPropertyNode * child) override;
void childRemoved(SGPropertyNode * child) override;
void childChanged(SGPropertyNode * child) override;
virtual void childAdded(SGPropertyNode * child);
virtual void childRemoved(SGPropertyNode * child);
virtual void childChanged(SGPropertyNode * child);
void handleZIndexChanged(ElementPtr child, int z_index = 0);

View File

@@ -1,5 +1,4 @@
///@file
/// An image on the Canvas
// An image on the Canvas
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -17,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasImage.hxx"
#include <simgear/canvas/Canvas.hxx>
@@ -118,7 +115,9 @@ namespace canvas
ElementWeakPtr parent ):
Element(canvas, node, parent_style, parent),
_texture(new osg::Texture2D),
_node_src_rect( node->getNode("source", 0, true) )
_node_src_rect( node->getNode("source", 0, true) ),
_src_rect(0,0),
_region(0,0)
{
staticInit();
@@ -156,207 +155,22 @@ namespace canvas
//----------------------------------------------------------------------------
Image::~Image()
{
if( _http_request )
{
Canvas::getSystemAdapter()
->getHTTPClient()
->cancelRequest(_http_request, "image destroyed");
}
}
//----------------------------------------------------------------------------
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);
if( _http_request ) {
Canvas::getSystemAdapter()->getHTTPClient()->cancelRequest(_http_request, "image destroyed");
}
}
}
//----------------------------------------------------------------------------
CanvasWeakPtr Image::getSrcCanvas() const
void Image::update(double dt)
{
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);
Element::update(dt);
osg::Texture2D* texture = dynamic_cast<osg::Texture2D*>
(
_geom->getOrCreateStateSet()
->getTextureAttribute(0, osg::StateAttribute::TEXTURE)
);
auto canvas = _src_canvas.lock();
simgear::canvas::CanvasPtr canvas = _src_canvas.lock();
if( (_attributes_dirty & SRC_CANVAS)
// check if texture has changed (eg. due to resizing)
@@ -586,6 +400,188 @@ 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)
{
@@ -636,9 +632,7 @@ 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();
}

View File

@@ -1,5 +1,4 @@
///@file
/// An image on the Canvas
// An image on the Canvas
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -54,7 +53,8 @@ namespace canvas
ElementWeakPtr parent = 0 );
virtual ~Image();
void valueChanged(SGPropertyNode* child) override;
virtual void update(double dt);
virtual void valueChanged(SGPropertyNode* child);
void setSrcCanvas(CanvasPtr canvas);
CanvasWeakPtr getSrcCanvas() const;
@@ -94,14 +94,14 @@ namespace canvas
const SGRect<float>& getRegion() const;
bool handleEvent(const EventPtr& event) override;
bool handleEvent(const EventPtr& event);
/**
*
*/
void setSourceRect(const SGRect<float>& sourceRect);
protected:
enum ImageAttributes
{
SRC_RECT = LAST_ATTRIBUTE << 1, // Source image rectangle
@@ -109,9 +109,7 @@ namespace canvas
SRC_CANVAS = DEST_SIZE << 1
};
void updateImpl(double dt) override;
void childChanged(SGPropertyNode * child) override;
virtual void childChanged(SGPropertyNode * child);
void setupDefaultDimensions();
SGRect<int> getTextureDimensions() const;
@@ -136,9 +134,9 @@ namespace canvas
osg::ref_ptr<osg::Vec2Array> _texCoords;
osg::ref_ptr<osg::Vec4Array> _colors;
SGPropertyNode *_node_src_rect = nullptr;
SGRect<float> _src_rect {0, 0},
_region {0, 0};
SGPropertyNode *_node_src_rect;
SGRect<float> _src_rect,
_region;
SVGpreserveAspectRatio _preserve_aspect_ratio;

View File

@@ -1,6 +1,5 @@
///@file
/// A group of 2D Canvas elements which get automatically transformed according
/// to the map parameters.
// A group of 2D Canvas elements which get automatically transformed according
// to the map parameters.
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -18,16 +17,14 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasMap.hxx"
#include "map/geo_node_pair.hxx"
#include "map/projection.hxx"
#include <simgear/misc/strutils.hxx>
#include <cmath>
#include <boost/algorithm/string/predicate.hpp>
#define LOG_GEO_RET(msg) \
{\
SG_LOG\
@@ -49,12 +46,6 @@ 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()
@@ -72,11 +63,12 @@ namespace canvas
const SGPropertyNode_ptr& node,
const Style& parent_style,
ElementWeakPtr parent ):
Group(canvas, node, parent_style, parent)
Group(canvas, node, parent_style, parent),
// TODO make projection configurable
_projection(new SansonFlamsteedProjection),
_projection_dirty(true)
{
staticInit();
projectionNodeChanged(node->getChild(PROJECTION));
}
//----------------------------------------------------------------------------
@@ -86,49 +78,42 @@ namespace canvas
}
//----------------------------------------------------------------------------
void Map::updateImpl(double dt)
void Map::update(double dt)
{
Group::updateImpl(dt);
for(auto& it: _geo_nodes)
for( GeoNodes::iterator it = _geo_nodes.begin();
it != _geo_nodes.end();
++it )
{
GeoNodePair* geo_node = it.second.get();
GeoNodePair* geo_node = it->second.get();
if( !geo_node->isComplete()
|| (!geo_node->isDirty() && !_projection_dirty) )
continue;
double latD = -9999.0, lonD = -9999.0;
if (geo_node->isDirty()) {
GeoCoord lat = parseGeoCoord(geo_node->getLat());
if( lat.type != GeoCoord::LATITUDE )
continue;
GeoCoord lon = parseGeoCoord(geo_node->getLon());
if( lon.type != GeoCoord::LONGITUDE )
continue;
// save the parsed values so we can re-use them if only projection
// is changed (very common case for moving vehicle)
latD = lat.value;
lonD = lon.value;
geo_node->setCachedLatLon(std::make_pair(latD, lonD));
} else {
std::tie(latD, lonD) = geo_node->getCachedLatLon();
}
Projection::ScreenPosition pos = _projection->worldToScreen(latD, lonD);
GeoCoord lat = parseGeoCoord(geo_node->getLat());
if( lat.type != GeoCoord::LATITUDE )
continue;
GeoCoord lon = parseGeoCoord(geo_node->getLon());
if( lon.type != GeoCoord::LONGITUDE )
continue;
Projection::ScreenPosition pos =
_projection->worldToScreen(lat.value, lon.value);
geo_node->setScreenPos(pos.x, pos.y);
// geo_node->print();
geo_node->setDirty(false);
}
_projection_dirty = false;
Group::update(dt);
}
//----------------------------------------------------------------------------
void Map::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
{
if( strutils::ends_with(child->getNameString(), GEO) )
if( boost::ends_with(child->getNameString(), GEO) )
_geo_nodes[child].reset(new GeoNodePair());
else if( parent != _node && child->getNameString() == HDG )
_hdg_nodes.insert(child);
@@ -139,7 +124,7 @@ namespace canvas
//----------------------------------------------------------------------------
void Map::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
{
if( strutils::ends_with(child->getNameString(), GEO) )
if( boost::ends_with(child->getNameString(), GEO) )
// TODO remove from other node
_geo_nodes.erase(child);
else if( parent != _node && child->getName() == HDG )
@@ -161,7 +146,7 @@ namespace canvas
{
const std::string& name = child->getNameString();
if( strutils::ends_with(name, GEO) )
if( boost::ends_with(name, GEO) )
return geoNodeChanged(child);
else if( name == HDG )
return hdgNodeChanged(child);
@@ -176,10 +161,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());
@@ -188,43 +173,16 @@ 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));
// Only set existing properties to prevent using 0 instead of default values
if( auto heading = _node->getChild(HDG) )
_projection->setOrientation(heading->getFloatValue());
if( auto screen_range = _node->getChild(SCREEN_RANGE) )
_projection->setScreenRange(screen_range->getDoubleValue());
if( auto range = _node->getChild(RANGE) )
_projection->setRange(range->getDoubleValue());
_projection_dirty = true;
}
//----------------------------------------------------------------------------
void Map::geoNodeChanged(SGPropertyNode* child)
{

View File

@@ -1,6 +1,5 @@
///@file
/// A group of 2D Canvas elements which get automatically transformed according
/// to the map parameters.
// A group of 2D Canvas elements which get automatically transformed according
// to the map parameters.
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -23,9 +22,9 @@
#include "CanvasGroup.hxx"
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <boost/shared_ptr.hpp>
#include <boost/unordered_map.hpp>
#include <boost/unordered_set.hpp>
namespace simgear
{
@@ -46,39 +45,45 @@ 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:
void updateImpl(double dt) override;
void updateProjection(SGPropertyNode* type_node);
virtual void childChanged(SGPropertyNode * child);
void childAdded(SGPropertyNode* parent, SGPropertyNode* child) override;
void childRemoved(SGPropertyNode* parent, SGPropertyNode* child) override;
void valueChanged(SGPropertyNode* child) override;
void childChanged(SGPropertyNode* child) override;
using GeoNodes =
std::unordered_map<SGPropertyNode*, std::shared_ptr<GeoNodePair>>;
using NodeSet = std::unordered_set<SGPropertyNode*>;
typedef boost::unordered_map< SGPropertyNode*,
boost::shared_ptr<GeoNodePair>
> GeoNodes;
typedef boost::unordered_set<SGPropertyNode*> NodeSet;
GeoNodes _geo_nodes;
NodeSet _hdg_nodes;
std::shared_ptr<HorizontalProjection> _projection;
bool _projection_dirty = false;
boost::shared_ptr<HorizontalProjection> _projection;
bool _projection_dirty;
struct GeoCoord
{
GeoCoord():
type(INVALID),
value(0)
{}
enum
{
INVALID,
LATITUDE,
LONGITUDE
} type = INVALID;
double value = 0;
} type;
double value;
};
void projectionNodeChanged(SGPropertyNode* child);
void geoNodeChanged(SGPropertyNode* child);
void hdgNodeChanged(SGPropertyNode* child);
void geoNodeChanged(SGPropertyNode * child);
void hdgNodeChanged(SGPropertyNode * child);
GeoCoord parseGeoCoord(const std::string& val) const;
};

View File

@@ -1,5 +1,4 @@
///@file
/// An OpenVG path on the Canvas
// An OpenVG path on the Canvas
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -17,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasPath.hxx"
#include <simgear/scene/util/parse_color.hxx>
#include <simgear/misc/strutils.hxx>
@@ -207,13 +204,23 @@ namespace canvas
return SGVec2f(-1.0f, -1.0f);
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
class Path::PathDrawable:
public osg::Drawable
{
public:
PathDrawable(Path* path):
_path_element(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)
{
setSupportsDisplayList(false);
setDataVariance(Object::DYNAMIC);
@@ -231,12 +238,9 @@ namespace canvas
vgDestroyPaint(_paint_fill);
}
const char* className() const override
{ return "PathDrawable"; }
osg::Object* cloneType() const override
{ return new PathDrawable(_path_element); }
osg::Object* clone(const osg::CopyOp&) const override
{ return new PathDrawable(_path_element); }
virtual const char* className() const { return "PathDrawable"; }
virtual osg::Object* cloneType() const { return new PathDrawable(_path_element); }
virtual osg::Object* clone(const osg::CopyOp&) const { return new PathDrawable(_path_element); }
/**
* Replace the current path segments with the new ones
@@ -277,16 +281,6 @@ 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")
*
@@ -314,7 +308,7 @@ namespace canvas
else if( parseColor(stroke, _stroke_color) )
{
_mode |= VG_STROKE_PATH;
_attributes_dirty |= STROKE_COLOR;
_attributes_dirty |= STROKE_COLOR;
}
else
{
@@ -327,16 +321,6 @@ 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
*/
@@ -387,7 +371,7 @@ namespace canvas
/**
* Draw callback
*/
void drawImplementation(osg::RenderInfo& renderInfo) const override
virtual void drawImplementation(osg::RenderInfo& renderInfo) const
{
if( _attributes_dirty & PATH )
return;
@@ -404,22 +388,31 @@ 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);
@@ -431,19 +424,6 @@ 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);
@@ -559,13 +539,13 @@ namespace canvas
/**
* Compute the bounding box
*/
osg::BoundingBox
virtual osg::BoundingBox
#if OSG_VERSION_LESS_THAN(3,3,2)
computeBound()
#else
computeBoundingBox()
#endif
const override
const
{
if( _path == VG_INVALID_HANDLE || (_attributes_dirty & PATH) )
return osg::BoundingBox();
@@ -597,24 +577,22 @@ namespace canvas
Path *_path_element;
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};
mutable VGPath _path;
mutable VGPaint _paint;
mutable VGPaint _paint_fill;
mutable uint32_t _attributes_dirty;
CmdList _cmds;
CoordList _coords;
VGbitfield _mode {0};
VGbitfield _mode;
osg::Vec4f _fill_color;
uint8_t _fill_opacity {255};
VGFillRule _fill_rule {VG_EVEN_ODD};
VGFillRule _fill_rule;
osg::Vec4f _stroke_color;
uint8_t _stroke_opacity {255};
VGfloat _stroke_width {1};
VGfloat _stroke_width;
std::vector<VGfloat> _stroke_dash;
VGCapStyle _stroke_linecap {VG_CAP_BUTT};
VGJoinStyle _stroke_linejoin {VG_JOIN_MITER};
VGCapStyle _stroke_linecap;
VGJoinStyle _stroke_linejoin;
osg::Vec3f transformPoint( const osg::Matrix& m,
osg::Vec2f pos ) const
@@ -671,7 +649,7 @@ namespace canvas
struct PathUpdateCallback:
public osg::Drawable::UpdateCallback
{
void update(osg::NodeVisitor*, osg::Drawable* drawable) override
virtual void update(osg::NodeVisitor*, osg::Drawable* drawable)
{
static_cast<PathDrawable*>(drawable)->update();
}
@@ -690,10 +668,8 @@ 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);
@@ -722,69 +698,92 @@ 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);
}
//----------------------------------------------------------------------------
@@ -827,83 +826,47 @@ 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();
if (simgear::strutils::starts_with(name, "border-"))
{
_attributes_dirty |= RECT;
return;
}
if (prName == "rect") {
_hasRect = true;
if (name == "left") {
_rect.setLeft(child->getDoubleValue());
} else if (name == "top") {
_rect.setTop(child->getDoubleValue());
} else if (name == "right") {
_rect.setRight(child->getDoubleValue());
} else if (name == "bottom") {
_rect.setBottom(child->getDoubleValue());
} else if (name == "width") {
_rect.setWidth(child->getDoubleValue());
} else if (name == "height") {
_rect.setHeight(child->getDoubleValue());
}
_attributes_dirty |= RECT;
return;
}
if( child->getParent() != _node )
return;
const std::string& name = child->getNameString();
const std::string &prName = child->getParent()->getNameString();
if( strutils::starts_with(name, "border-") )
{
_attributes_dirty |= RECT;
return;
}
if (prName == "rect")
{
_hasRect = true;
if (name == "left") {
_rect.setLeft(child->getDoubleValue());
} else if (name == "top") {
_rect.setTop(child->getDoubleValue());
} else if (name == "right") {
_rect.setRight(child->getDoubleValue());
} else if (name == "bottom") {
_rect.setBottom(child->getDoubleValue());
} else if (name == "width") {
_rect.setWidth(child->getDoubleValue());
} else if (name == "height") {
_rect.setHeight(child->getDoubleValue());
}
_attributes_dirty |= RECT;
return;
}
if( name == "cmd" )
_attributes_dirty |= CMDS;
else if( name == "coord" )
_attributes_dirty |= COORDS;
else if ( name == "svg")
{
_hasSVG = true;
_attributes_dirty |= SVG;
}
}
//----------------------------------------------------------------------------
@@ -928,68 +891,67 @@ namespace canvas
return values;
}
//----------------------------------------------------------------------------
void operator+=(CoordList& base, const std::initializer_list<VGfloat>& other)
{
base.insert(base.end(), other.begin(), other.end());
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
void Path::parseRectToVGPath()
{
CmdList commands;
CoordList coords;
commands.reserve(4);
coords.reserve(8);
bool haveCorner = false;
SGVec2f topLeft = parseRectCornerRadius(_node, "left", "top", haveCorner);
if (haveCorner) {
commands.push_back(VG_MOVE_TO_ABS);
coords += {_rect.l(), _rect.t() + topLeft.y()};
commands.push_back(VG_SCCWARC_TO_REL);
coords += {topLeft.x(), topLeft.y(), 0.0, topLeft.x(), -topLeft.y()};
} else {
commands.push_back(VG_MOVE_TO_ABS);
coords += {_rect.l(), _rect.t()};
void operator+=(CoordList& base, const std::initializer_list<VGfloat>& other)
{
base.insert(base.end(), other.begin(), other.end());
}
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()};
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 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

View File

@@ -1,5 +1,4 @@
///@file
/// An OpenVG path on the Canvas
// An OpenVG path on the Canvas
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -21,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
{
@@ -41,11 +40,14 @@ namespace canvas
ElementWeakPtr parent = 0 );
virtual ~Path();
osg::BoundingBox
getTransformedBounds(const osg::Matrix& m) const override;
virtual void update(double dt);
/** Add a segment with the given command and coordinates */
Path& addSegment(uint8_t cmd, std::initializer_list<float> coords = {});
virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
#define BOOST_PP_ITERATION_LIMITS (0, 6)
#define BOOST_PP_FILENAME_1 \
<simgear/canvas/elements/detail/add_segment_variadic.hxx>
#include BOOST_PP_ITERATE()
/** Move path cursor */
Path& moveTo(float x_abs, float y_abs);
@@ -68,10 +70,10 @@ namespace canvas
void setSVGPath(const std::string& svgPath);
void setRect(const SGRectf& r);
void setRoundRect(const SGRectf& r, float radiusX, float radiusY = -1.0);
void setRect(const SGRect<float>& r);
void setRoundRect(const SGRect<float>& r, float radiusX, float radiusY = -1.0);
protected:
enum PathAttributes
{
CMDS = LAST_ATTRIBUTE << 1,
@@ -86,14 +88,12 @@ namespace canvas
bool _hasSVG : 1;
bool _hasRect : 1;
SGRectf _rect;
void updateImpl(double dt) override;
void childRemoved(SGPropertyNode * child) override;
void childChanged(SGPropertyNode * child) override;
SGRect<float> _rect;
void parseRectToVGPath();
virtual void childRemoved(SGPropertyNode * child);
virtual void childChanged(SGPropertyNode * child);
};
} // namespace canvas

View File

@@ -1,5 +1,4 @@
///@file
/// A text on the Canvas
// A text on the Canvas
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -17,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasText.hxx"
#include <simgear/canvas/Canvas.hxx>
#include <simgear/canvas/CanvasSystemAdapter.hxx>
@@ -58,20 +55,21 @@ namespace canvas
SGVec2i sizeForWidth(int w) const;
osg::BoundingBox
virtual osg::BoundingBox
#if OSG_VERSION_LESS_THAN(3,3,2)
computeBound()
#else
computeBoundingBox()
#endif
const override;
const;
protected:
friend class TextLine;
canvas::Text *_text_element;
void computePositions(unsigned int contextID) const override;
virtual void computePositions(unsigned int contextID) const;
};
class TextLine
@@ -498,31 +496,18 @@ namespace canvas
{
case LEFT_TO_RIGHT:
{
#if OSG_VERSION_LESS_THAN(3,5,2)
osg::Vec2 delta(activefont->getKerning(previous_charcode,
charcode,
_kerningType));
#else
osg::Vec2 delta(activefont->getKerning(_fontSize,
previous_charcode,
charcode,
_kerningType));
#endif
osg::Vec2 delta( activefont->getKerning( previous_charcode,
charcode,
_kerningType ) );
cursor.x() += delta.x() * wr;
cursor.y() += delta.y() * hr;
break;
}
case RIGHT_TO_LEFT:
{
#if OSG_VERSION_LESS_THAN(3,5,2)
osg::Vec2 delta(activefont->getKerning(charcode,
previous_charcode,
_kerningType));
#else
osg::Vec2 delta(activefont->getKerning(_fontSize, charcode,
previous_charcode,
_kerningType));
#endif
osg::Vec2 delta( activefont->getKerning( charcode,
previous_charcode,
_kerningType ) );
cursor.x() -= delta.x() * wr;
cursor.y() -= delta.y() * hr;
break;
@@ -866,12 +851,12 @@ namespace canvas
//----------------------------------------------------------------------------
osg::StateSet* Text::getOrCreateStateSet()
{
if( !_scene_group.valid() )
return nullptr;
if( !_transform.valid() )
return 0;
// Only check for StateSet on Transform, as the text stateset is shared
// between all text instances using the same font (texture).
return _scene_group->getOrCreateStateSet();
return _transform->getOrCreateStateSet();
}
} // namespace canvas

View File

@@ -1,5 +1,4 @@
///@file
/// A text on the Canvas
// A text on the Canvas
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//

View File

@@ -0,0 +1,21 @@
#ifndef CANVAS_PATH_HXX_
# error Canvas - do not include this file!
#endif
#define n BOOST_PP_ITERATION()
Path& addSegment( uint8_t cmd
BOOST_PP_COMMA_IF(n)
BOOST_PP_ENUM_PARAMS(n, float coord) )
{
_node->addChild("cmd")->setIntValue(cmd);
#define SG_CANVAS_PATH_SET_COORD(z, n, dummy)\
_node->addChild("coord")->setFloatValue(coord##n);
BOOST_PP_REPEAT(n, SG_CANVAS_PATH_SET_COORD, 0)
#undef SG_CANVAS_PATH_SET_COORD
return *this;
}
#undef n

View File

@@ -67,8 +67,7 @@ namespace canvas
{
_node_lat = node;
_status &= ~LAT_MISSING;
_xNode.reset();
if( node == _node_lon )
{
_node_lon = 0;
@@ -80,8 +79,7 @@ namespace canvas
{
_node_lon = node;
_status &= ~LON_MISSING;
_yNode.reset();
if( node == _node_lat )
{
_node_lat = 0;
@@ -99,34 +97,19 @@ namespace canvas
return _node_lon ? _node_lon->getStringValue() : "";
}
void setCachedLatLon(const std::pair<double, double>& latLon)
{ _cachedLatLon = latLon; }
std::pair<double, double> getCachedLatLon()
{ return _cachedLatLon; }
void setTargetName(const std::string& name)
{
_target_name = name;
_xNode.reset();
_yNode.reset();
}
void setScreenPos(float x, float y)
{
assert( isComplete() );
if (!_xNode) {
SGPropertyNode *parent = _node_lat->getParent();
_xNode = parent->getChild(_target_name, _node_lat->getIndex(), true);
}
if (!_yNode) {
SGPropertyNode *parent = _node_lat->getParent();
_yNode = parent->getChild(_target_name, _node_lon->getIndex(), true);
}
_xNode->setDoubleValue(x);
_yNode->setDoubleValue(y);
SGPropertyNode *parent = _node_lat->getParent();
parent->getChild(_target_name, _node_lat->getIndex(), true)
->setDoubleValue(x);
parent->getChild(_target_name, _node_lon->getIndex(), true)
->setDoubleValue(y);
}
void print()
@@ -142,9 +125,6 @@ namespace canvas
SGPropertyNode *_node_lat,
*_node_lon;
std::string _target_name;
SGPropertyNode_ptr _xNode,
_yNode;
std::pair<double, double> _cachedLatLon;
};

View File

@@ -1,5 +1,4 @@
///@file
/// Geographic projections for Canvas map element
// Geographic projections for Canvas map element
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -191,32 +190,6 @@ 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

View File

@@ -1,5 +1,4 @@
///@file
/// Canvas user defined event
// Canvas user defined event
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
@@ -17,7 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CustomEvent.hxx"
namespace simgear
@@ -47,14 +45,6 @@ namespace canvas
// assert( type_map.find(type_id) != type_map.end() );
}
//----------------------------------------------------------------------------
CustomEvent* CustomEvent::clone(int type) const
{
auto event = new CustomEvent(*this);
event->type = type;
return event;
}
//----------------------------------------------------------------------------
void CustomEvent::setDetail(StringMap const& data)
{

View File

@@ -38,7 +38,6 @@ namespace canvas
public:
/**
* @brief Construct a user defined event from a type string
*
* @param type_str Event type name (if name does not exist yet it will
* be registered as new event type)
@@ -50,10 +49,6 @@ namespace canvas
StringMap const& data = StringMap() );
/**
* @brief Construct a user defined event from a (previously registered)
* type id
*
* @see getOrRegisterType()
*
* @param type_id Event type id
* @param bubbles If this event should take part in the bubbling phase
@@ -63,8 +58,6 @@ namespace canvas
bool bubbles = false,
StringMap const& data = StringMap() );
CustomEvent* clone(int type = 0) const override;
/**
* Set user data
*/
@@ -81,7 +74,7 @@ namespace canvas
* @see #bubbles
* @see CustomEvent()
*/
bool canBubble() const override { return bubbles; }
virtual bool canBubble() const { return bubbles; }
StringMap detail; //!< User data map
bool bubbles; //!< Whether the event supports bubbling

View File

@@ -1,5 +1,4 @@
///@file
/// Input device event
// Input device event
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
@@ -17,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "DeviceEvent.hxx"
#include <osgGA/GUIEventAdapter>

View File

@@ -1,5 +1,4 @@
///@file
/// Keyboard event
// Keyboard event
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
@@ -17,9 +16,8 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "KeyboardEvent.hxx"
#include "utf8.h"
#include <osgGA/GUIEventAdapter>
@@ -42,7 +40,6 @@ namespace canvas
// TODO check Win/Mac keycode for altgr/ISO Level3 Shift
const uint32_t KEY_AltGraph = 0xfe03;
//----------------------------------------------------------------------------
KeyboardEvent::KeyboardEvent():
@@ -70,14 +67,6 @@ namespace canvas
// // TODO what to do with wrong event type?
}
//----------------------------------------------------------------------------
KeyboardEvent* KeyboardEvent::clone(int type) const
{
auto event = new KeyboardEvent(*this);
event->type = type;
return event;
}
//----------------------------------------------------------------------------
void KeyboardEvent::setKey(uint32_t key)
{
@@ -278,24 +267,10 @@ namespace canvas
// Empty or no mapping -> convert UTF-32 key value to UTF-8
if( _name.empty() )
{
if (( _key >= 0xd800u && _key <= 0xdfffu ) || _key > 0x10ffffu )
if( !utf8::internal::is_code_point_valid(_key) )
_name = "Unidentified";
else
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));
}
utf8::unchecked::append(_key, std::back_inserter(_name));
}
// Keys on the numpad with NumLock enabled are reported just like their
@@ -330,30 +305,11 @@ namespace canvas
if( key_name.empty() )
return false;
// 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;
}
std::string::const_iterator it = key_name.begin();
uint32_t cp = utf8::next(it, key_name.end());
// Check if _name contains exactly one (UTF-8 encoded) character.
if (key_name.length() > len)
if( it != key_name.end() )
return false;
// C0 and C1 control characters are not printable.

View File

@@ -45,7 +45,6 @@ namespace canvas
KeyboardEvent();
KeyboardEvent(const osgGA::GUIEventAdapter& ea);
KeyboardEvent* clone(int type = 0) const override;
void setKey(uint32_t key);
void setUnmodifiedKey(uint32_t key);

View File

@@ -1,5 +1,4 @@
///@file
/// Mouse event
// Mouse event
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
@@ -17,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "MouseEvent.hxx"
#include <osgGA/GUIEventAdapter>
@@ -49,14 +46,6 @@ namespace canvas
button += 1;
}
//----------------------------------------------------------------------------
MouseEvent* MouseEvent::clone(int type) const
{
auto event = new MouseEvent(*this);
event->type = type;
return event;
}
//----------------------------------------------------------------------------
bool MouseEvent::canBubble() const
{

View File

@@ -36,9 +36,8 @@ namespace canvas
public:
MouseEvent();
MouseEvent(const osgGA::GUIEventAdapter& ea);
MouseEvent* clone(int type = 0) const override;
bool canBubble() const override;
virtual bool canBubble() const;
osg::Vec2f getScreenPos() const { return screen_pos; }
osg::Vec2f getClientPos() const { return client_pos; }

View File

@@ -1,5 +1,4 @@
///@file
/// Keyboard event demo. Press some keys and get some info...
// Keyboard event demo. Press some keys and get some info...
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
@@ -17,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "KeyboardEvent.hxx"
#include <osgViewer/Viewer>

View File

@@ -16,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "BoxLayout.hxx"
#include "SpacerItem.hxx"
#include <simgear/canvas/Canvas.hxx>

View File

@@ -16,7 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "Layout.hxx"
#include <simgear/debug/logstream.hxx>

View File

@@ -16,7 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "LayoutItem.hxx"
#include <simgear/canvas/Canvas.hxx>

View File

@@ -16,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "NasalWidget.hxx"
#include <simgear/canvas/Canvas.hxx>
@@ -45,7 +43,7 @@ namespace canvas
//----------------------------------------------------------------------------
NasalWidget::~NasalWidget()
{
onRemove();
}
//----------------------------------------------------------------------------
@@ -189,10 +187,10 @@ namespace canvas
if( hfw.empty() )
return -1;
naContext c = naNewContext();
try
{
nasal::Context ctx;
return hfw(ctx.to_me(const_cast<NasalWidget*>(this)), w);
return hfw(nasal::to_nasal(c, const_cast<NasalWidget*>(this)), w);
}
catch( std::exception const& ex )
{
@@ -202,6 +200,7 @@ namespace canvas
"NasalWidget.heightForWidth: callback error: '" << ex.what() << "'"
);
}
naFreeContext(c);
return -1;
}
@@ -261,8 +260,8 @@ namespace canvas
try
{
nasal::Context ctx;
_set_geometry(ctx.to_me(this), rect);
nasal::Context c;
_set_geometry(nasal::to_nasal(c, this), rect);
_flags &= ~LAYOUT_DIRTY;
}
catch( std::exception const& ex )

View File

@@ -16,7 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "SpacerItem.hxx"
namespace simgear

View File

@@ -109,13 +109,10 @@
# define SG_UNIX
#endif
#ifdef __GNUC__
#define SG_DEPRECATED(func) func __attribute__ ((deprecated))
#elif defined(_MSC_VER)
#define SG_DEPRECATED(func) __declspec(deprecated) func
#if defined( __GNUC__ )
# define DEPRECATED __attribute__ ((deprecated))
#else
#pragma message("WARNING: You need to implement SG_DEPRECATED for this compiler")
#define SG_DEPRECATED(func) func
# define DEPRECATED
#endif
#if defined(__clang__)

View File

@@ -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 equatorial earth radius from LaRCsim (meter) */
/** Value of earth radius from LaRCsim (meter) */
#define SG_EQUATORIAL_RADIUS_M 6378138.12
/** Radius squared (ft) */
@@ -104,8 +104,6 @@ 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

View File

@@ -18,8 +18,7 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include <simgear/debug/BufferedLogCallback.hxx>
#include <boost/foreach.hpp>

View File

@@ -1,7 +1,7 @@
include (SimGearComponent)
set(HEADERS debug_types.h logstream.hxx BufferedLogCallback.hxx OsgIoCapture.hxx)
set(HEADERS debug_types.h logstream.hxx BufferedLogCallback.hxx)
set(SOURCES logstream.cxx BufferedLogCallback.cxx)
simgear_component(debug debug "${SOURCES}" "${HEADERS}")
simgear_component(debug debug "${SOURCES}" "${HEADERS}")

View File

@@ -1,45 +0,0 @@
#include <cstring>
#include <osg/Notify>
using namespace osg;
/**
* merge OSG output into our logging system, so it gets recorded to file,
* and so we can display a GUI console with renderer issues, especially
* shader compilation warnings and errors.
*/
class NotifyLogger : public osg::NotifyHandler
{
public:
// note this callback will be invoked by OSG from multiple threads.
// fortunately our Simgear logging implementation already handles
// that internally, so we simply pass the message on.
virtual void notify(osg::NotifySeverity severity, const char* message) {
// Detect whether a osg::Reference derived object is deleted with a non-zero
// reference count. In this case trigger a segfault to get a stack trace.
if (strstr(message, "the final reference count was")) {
// as this is going to segfault ignore the translation of severity and always output the message.
SG_LOG(SG_GL, SG_ALERT, message);
int* trigger_segfault = 0;
*trigger_segfault = 0;
return;
}
SG_LOG(SG_GL, translateSeverity(severity), message);
}
private:
sgDebugPriority translateSeverity(osg::NotifySeverity severity) {
switch (severity) {
case osg::ALWAYS:
case osg::FATAL: return SG_ALERT;
case osg::WARN: return SG_WARN;
case osg::NOTICE:
case osg::INFO: return SG_INFO;
case osg::DEBUG_FP:
case osg::DEBUG_INFO: return SG_DEBUG;
default: return SG_ALERT;
}
}
};

View File

@@ -34,8 +34,7 @@ typedef enum {
SG_GUI = 0x00800000,
SG_TERRASYNC = 0x01000000,
SG_PARTICLES = 0x02000000,
SG_HEADLESS = 0x04000000,
SG_UNDEFD = 0x08000000, // For range checking
SG_UNDEFD = 0x04000000, // For range checking
SG_ALL = 0xFFFFFFFF
} sgDebugClass;

View File

@@ -46,7 +46,39 @@
#include <io.h>
#endif
const char* debugClassToString(sgDebugClass c)
{
switch (c) {
case SG_NONE: return "none";
case SG_TERRAIN: return "terrain";
case SG_ASTRO: return "astro";
case SG_FLIGHT: return "flight";
case SG_INPUT: return "input";
case SG_GL: return "opengl";
case SG_VIEW: return "view";
case SG_COCKPIT: return "cockpit";
case SG_GENERAL: return "general";
case SG_MATH: return "math";
case SG_EVENT: return "event";
case SG_AIRCRAFT: return "aircraft";
case SG_AUTOPILOT: return "autopilot";
case SG_IO: return "io";
case SG_CLIPPER: return "clipper";
case SG_NETWORK: return "network";
case SG_ATC: return "atc";
case SG_NASAL: return "nasal";
case SG_INSTR: return "instruments";
case SG_SYSTEMS: return "systems";
case SG_AI: return "ai";
case SG_ENVIRONMENT:return "environment";
case SG_SOUND: return "sound";
case SG_NAVAID: return "navaid";
case SG_GUI: return "gui";
case SG_TERRASYNC: return "terrasync";
case SG_PARTICLES: return "particles";
default: return "unknown";
}
}
//////////////////////////////////////////////////////////////////////////////
@@ -70,41 +102,6 @@ void LogCallback::setLogLevels( sgDebugClass c, sgDebugPriority p )
m_class = c;
}
const char* LogCallback::debugClassToString(sgDebugClass c)
{
switch (c) {
case SG_NONE: return "none";
case SG_TERRAIN: return "terrain";
case SG_ASTRO: return "astro";
case SG_FLIGHT: return "flight";
case SG_INPUT: return "input";
case SG_GL: return "opengl";
case SG_VIEW: return "view";
case SG_COCKPIT: return "cockpit";
case SG_GENERAL: return "general";
case SG_MATH: return "math";
case SG_EVENT: return "event";
case SG_AIRCRAFT: return "aircraft";
case SG_AUTOPILOT: return "autopilot";
case SG_IO: return "io";
case SG_CLIPPER: return "clipper";
case SG_NETWORK: return "network";
case SG_ATC: return "atc";
case SG_NASAL: return "nasal";
case SG_INSTR: return "instruments";
case SG_SYSTEMS: return "systems";
case SG_AI: return "ai";
case SG_ENVIRONMENT:return "environment";
case SG_SOUND: return "sound";
case SG_NAVAID: return "navaid";
case SG_GUI: return "gui";
case SG_TERRASYNC: return "terrasync";
case SG_PARTICLES: return "particles";
case SG_HEADLESS: return "headless";
default: return "unknown";
}
}
} // of namespace simgear
//////////////////////////////////////////////////////////////////////////////
@@ -180,7 +177,7 @@ public:
#endif
class logstream::LogStreamPrivate : public SGThread
class LogStreamPrivate : public SGThread
{
private:
/**
@@ -336,7 +333,9 @@ public:
~LogStreamPrivate()
{
removeCallbacks();
for (simgear::LogCallback* cb : m_callbacks) {
delete cb;
}
}
SGMutex m_lock;
@@ -362,9 +361,6 @@ public:
#endif
bool m_developerMode = false;
// test suite mode.
bool m_testMode = false;
void startLog()
{
SGGuard<SGMutex> g(m_lock);
@@ -445,16 +441,6 @@ public:
}
}
void removeCallbacks()
{
PauseThread pause(this);
for (simgear::LogCallback* cb : m_callbacks) {
delete cb;
}
m_callbacks.clear();
m_consoleCallbacks.clear();
}
void setLogLevels( sgDebugClass c, sgDebugPriority p )
{
PauseThread pause(this);
@@ -467,9 +453,6 @@ public:
bool would_log( sgDebugClass c, sgDebugPriority p ) const
{
// Testing mode, so always log.
if (m_testMode) return true;
p = translatePriority(p);
if (p >= SG_INFO) return true;
return ((c & m_logClass) != 0 && p >= m_logPriority);
@@ -499,105 +482,52 @@ public:
/////////////////////////////////////////////////////////////////////////////
static std::unique_ptr<logstream> global_logstream;
static logstream* global_logstream = NULL;
static LogStreamPrivate* global_privateLogstream = NULL;
static SGMutex global_logStreamLock;
logstream::logstream()
{
d.reset(new LogStreamPrivate);
d->startLog();
global_privateLogstream = new LogStreamPrivate;
global_privateLogstream->startLog();
}
logstream::~logstream()
{
popup_msgs.clear();
d->stop();
global_privateLogstream->stop();
delete global_privateLogstream;
}
void
logstream::setLogLevels( sgDebugClass c, sgDebugPriority p )
{
d->setLogLevels(c, p);
global_privateLogstream->setLogLevels(c, p);
}
void logstream::setDeveloperMode(bool devMode)
{
d->m_developerMode = devMode;
global_privateLogstream->m_developerMode = devMode;
}
void
logstream::addCallback(simgear::LogCallback* cb)
{
d->addCallback(cb);
global_privateLogstream->addCallback(cb);
}
void
logstream::removeCallback(simgear::LogCallback* cb)
{
d->removeCallback(cb);
global_privateLogstream->removeCallback(cb);
}
void
logstream::log( sgDebugClass c, sgDebugPriority p,
const char* fileName, int line, const std::string& msg)
{
d->log(c, p, fileName, line, msg);
}
void logstream::hexdump(sgDebugClass c, sgDebugPriority p, const char* fileName, int line, const void *mem, unsigned int len, unsigned int columns)
{
unsigned int i, j;
char temp[3000], temp1[3000];
*temp = 0;
for (i = 0; i < len + ((len % columns) ? (columns - len % columns) : 0); i++)
{
if (strlen(temp) > 500) return;
/* print offset */
if (i % columns == 0)
{
sprintf(temp1, "0x%06x: ", i);
strcat(temp, temp1);
}
/* print hex data */
if (i < len)
{
sprintf(temp1, "%02x ", 0xFF & ((char*)mem)[i]);
strcat(temp, temp1);
}
else /* end of block, just aligning for ASCII dump */
{
strcat(temp, " ");
}
/* print ASCII dump */
if (i % columns == (columns - 1))
{
for (j = i - (columns - 1); j <= i; j++)
{
if (j >= len) /* end of block, not really printing */
{
strcat(temp, " ");
}
else if (((((char*)mem)[j]) & (char)0x7f) > 32) /* printable char */
{
char t2[2];
t2[0] = 0xFF & ((char*)mem)[j];
t2[1] = 0;
strcat(temp, t2);
}
else /* other char */
{
strcat(temp, ".");
}
}
log(c, p, fileName, line, temp );
*temp = 0;
}
}
global_privateLogstream->log(c, p, fileName, line, msg);
}
void
@@ -627,31 +557,31 @@ logstream::has_popup()
bool
logstream::would_log( sgDebugClass c, sgDebugPriority p ) const
{
return d->would_log(c,p);
return global_privateLogstream->would_log(c,p);
}
sgDebugClass
logstream::get_log_classes() const
{
return d->m_logClass;
return global_privateLogstream->m_logClass;
}
sgDebugPriority
logstream::get_log_priority() const
{
return d->m_logPriority;
return global_privateLogstream->m_logPriority;
}
void
logstream::set_log_priority( sgDebugPriority p)
{
d->setLogLevels(d->m_logClass, p);
global_privateLogstream->setLogLevels(global_privateLogstream->m_logClass, p);
}
void
logstream::set_log_classes( sgDebugClass c)
{
d->setLogLevels(c, d->m_logPriority);
global_privateLogstream->setLogLevels(c, global_privateLogstream->m_logPriority);
}
@@ -667,54 +597,54 @@ sglog()
SGGuard<SGMutex> g(global_logStreamLock);
if( !global_logstream )
global_logstream.reset(new logstream);
return *(global_logstream.get());
global_logstream = new logstream();
return *global_logstream;
}
void
logstream::logToFile( const SGPath& aPath, sgDebugClass c, sgDebugPriority p )
{
d->addCallback(new FileLogCallback(aPath, c, p));
global_privateLogstream->addCallback(new FileLogCallback(aPath, c, p));
}
void logstream::setStartupLoggingEnabled(bool enabled)
{
d->setStartupLoggingEnabled(enabled);
global_privateLogstream->setStartupLoggingEnabled(enabled);
}
void logstream::requestConsole()
namespace simgear
{
void requestConsole()
{
#if defined (SG_WINDOWS)
const bool stderrAlreadyRedirected = d->m_stderr_isRedirectedAlready;
const bool stdoutAlreadyRedirected = d->m_stdout_isRedirectedAlready;
/*
* 2016-09-20(RJH) - Reworked console handling
* This is part of the reworked console handling for Win32. This is for building as a Win32 GUI Subsystem where no
* console is allocated on launch. If building as a console app then the startup will ensure that a console is created - but
* we don't need to handle that.
* The new handling is quite simple:
* 1. The constructor will ensure that these streams exists. It will attach to the
* parent command prompt if started from the command prompt, otherwise the
* stdout/stderr will be bound to the NUL device.
* 2. with --console a window will always appear regardless of where the process was
* started from. Any non redirected streams will be redirected
* 3. You cannot use --console and either redirected stream.
*
* This is called after the Private Log Stream constructor so we need to undo any console that it has attached to.
*/
/*
* 2016-09-20(RJH) - Reworked console handling
* This is part of the reworked console handling for Win32. This is for building as a Win32 GUI Subsystem where no
* console is allocated on launch. If building as a console app then the startup will ensure that a console is created - but
* we don't need to handle that.
* The new handling is quite simple:
* 1. The constructor will ensure that these streams exists. It will attach to the
* parent command prompt if started from the command prompt, otherwise the
* stdout/stderr will be bound to the NUL device.
* 2. with --console a window will always appear regardless of where the process was
* started from. Any non redirected streams will be redirected
* 3. You cannot use --console and either redirected stream.
*
* This is called after the Private Log Stream constructor so we need to undo any console that it has attached to.
*/
if (!stderrAlreadyRedirected && !stdoutAlreadyRedirected) {
if (!global_privateLogstream->m_stderr_isRedirectedAlready && !global_privateLogstream->m_stdout_isRedirectedAlready) {
FreeConsole();
if (AllocConsole()) {
if (!stdoutAlreadyRedirected)
if (!global_privateLogstream->m_stdout_isRedirectedAlready)
freopen("conout$", "w", stdout);
if (!stderrAlreadyRedirected)
if (!global_privateLogstream->m_stderr_isRedirectedAlready)
freopen("conout$", "w", stderr);
//http://stackoverflow.com/a/25927081
//Clear the error state for each of the C++ standard stream objects.
//Clear the error state for each of the C++ standard stream objects.
std::wcout.clear();
std::cout.clear();
std::wcerr.clear();
@@ -726,27 +656,12 @@ void logstream::requestConsole()
#endif
}
void
logstream::setTestingMode( bool testMode )
{
d->m_testMode = testMode;
if (testMode) d->removeCallbacks();
}
namespace simgear
{
void requestConsole()
{
sglog().requestConsole();
}
void shutdownLogging()
{
SGGuard<SGMutex> g(global_logStreamLock);
global_logstream.reset();
delete global_logstream;
global_logstream = 0;
}
} // of namespace simgear

View File

@@ -30,8 +30,7 @@
#include <sstream>
#include <vector>
#include <memory>
// forward decls
class SGPath;
@@ -50,8 +49,6 @@ protected:
LogCallback(sgDebugClass c, sgDebugPriority p);
bool shouldLog(sgDebugClass c, sgDebugPriority p) const;
static const char* debugClassToString(sgDebugClass c);
private:
sgDebugClass m_class;
sgDebugPriority m_priority;
@@ -77,14 +74,6 @@ public:
~logstream();
static void initGlobalLogstream();
/**
* Helper force a console on platforms where it might optional, when
* we need to show a console. This basically means Windows at the
* moment - on other plaforms it's a no-op
*/
void requestConsole();
/**
* Set the global log class and priority level.
* @param c debug class
@@ -117,12 +106,6 @@ public:
void log( sgDebugClass c, sgDebugPriority p,
const char* fileName, int line, const std::string& msg);
/**
* output formatted hex dump of memory block
*/
void hexdump(sgDebugClass c, sgDebugPriority p, const char* fileName, int line, const void *mem, unsigned int len, unsigned int columns = 16);
/**
* support for the SG_POPUP logging class
* set the content of the popup message
@@ -158,31 +141,17 @@ public:
void removeCallback(simgear::LogCallback* cb);
void removeCallbacks();
/**
* optionally record all entries and submit them to new log callbacks that
* are added. This allows simplified logging configuration, but still including
* early startup information in all logs.
*/
void setStartupLoggingEnabled(bool enabled);
/**
* Set up the logstream for running in test mode. For example the callbacks
* will be unregistered and the behaviour of the would_log() function
* sanitized.
*/
void setTestingMode(bool testMode);
private:
// constructor
logstream();
std::vector<std::string> popup_msgs;
class LogStreamPrivate;
std::unique_ptr<LogStreamPrivate> d;
};
logstream& sglog();
@@ -199,14 +168,12 @@ logstream& sglog();
do { if(sglog().would_log(C,P)) { \
std::ostringstream os; os << M; \
sglog().log(C, P, __FILE__, __LINE__, os.str()); \
if ((P) == SG_POPUP) sglog().popup(os.str()); \
if (P == SG_POPUP) sglog().popup(os.str()); \
} } while(0)
#ifdef FG_NDEBUG
# define SG_LOG(C,P,M) do { if((P) == SG_POPUP) SG_LOGX(C,P,M) } while(0)
# define SG_HEXDUMP(C,P,MEM,LEN)
# define SG_LOG(C,P,M) do { if(P == SG_POPUP) SG_LOGX(C,P,M) } while(0)
#else
# define SG_LOG(C,P,M) SG_LOGX(C,P,M)
# define SG_LOG_HEXDUMP(C,P,MEM,LEN) if(sglog().would_log(C,P)) sglog().hexdump(C, P, __FILE__, __LINE__, MEM, LEN)
#endif
#define SG_ORIGIN __FILE__ ":" SG_STRINGIZE(__LINE__)

View File

@@ -1,18 +0,0 @@
include (SimGearComponent)
set(HEADERS EmbeddedResource.hxx
EmbeddedResourceManager.hxx
EmbeddedResourceProxy.hxx)
set(SOURCES EmbeddedResource.cxx
EmbeddedResourceManager.cxx
EmbeddedResourceProxy.cxx)
simgear_component(embedded_resources embedded_resources
"${SOURCES}" "${HEADERS}")
if(ENABLE_TESTS)
add_executable(test_embedded_resources embedded_resources_test.cxx)
target_link_libraries(test_embedded_resources ${TEST_LIBS})
add_test(embedded_resources
${EXECUTABLE_OUTPUT_PATH}/test_embedded_resources)
endif(ENABLE_TESTS)

View File

@@ -1,265 +0,0 @@
// -*- coding: utf-8 -*-
//
// EmbeddedResource.cxx --- Class for pointing to/accessing an embedded resource
// Copyright (C) 2017 Florent Rougon
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA.
#include <simgear_config.h>
#include <string>
#include <iosfwd>
#include <ios> // std::streamsize
#include <ostream>
#include <memory> // std::unique_ptr
#include <utility> // std::move()
#include <algorithm> // std::min()
#include <limits> // std::numeric_limits
#include <cstddef> // std::size_t, std::ptrdiff_t
#include <simgear/structure/exception.hxx>
#include <simgear/io/iostreams/CharArrayStream.hxx>
#include <simgear/io/iostreams/zlibstream.hxx>
#include "EmbeddedResource.hxx"
using std::string;
using std::unique_ptr;
// Inspired by <http://stackoverflow.com/a/21174979/4756009>
template<typename Derived, typename Base>
static unique_ptr<Derived> static_unique_ptr_cast(unique_ptr<Base> p)
{
auto d = static_cast<Derived *>(p.release());
return unique_ptr<Derived>(d);
}
namespace simgear
{
// ***************************************************************************
// * AbstractEmbeddedResource class *
// ***************************************************************************
AbstractEmbeddedResource::AbstractEmbeddedResource(const char *data,
std::size_t size)
: _data(data),
_size(size)
{ }
const char *AbstractEmbeddedResource::rawPtr() const
{
return _data;
}
std::size_t AbstractEmbeddedResource::rawSize() const
{
return _size;
}
string AbstractEmbeddedResource::str() const
{
if (_size > std::numeric_limits<string::size_type>::max()) {
throw sg_range_exception(
"Resource too large to fit in an std::string (size: " +
std::to_string(_size) + " bytes)");
}
return string(_data, _size);
}
// ***************************************************************************
// * RawEmbeddedResource class *
// ***************************************************************************
RawEmbeddedResource::RawEmbeddedResource(const char *data, std::size_t size)
: AbstractEmbeddedResource(data, size)
{ }
AbstractEmbeddedResource::CompressionType
RawEmbeddedResource::compressionType() const
{
return AbstractEmbeddedResource::CompressionType::NONE;
}
string RawEmbeddedResource::compressionDescr() const
{
return string("none");
}
unique_ptr<std::streambuf> RawEmbeddedResource::streambuf() const
{
// This is a read-only variant of CharArrayStreambuf
return unique_ptr<std::streambuf>(
new ROCharArrayStreambuf(rawPtr(), rawSize()));
}
unique_ptr<std::istream> RawEmbeddedResource::istream() const
{
return unique_ptr<std::istream>(new CharArrayIStream(rawPtr(), rawSize()));
}
// ***************************************************************************
// * ZlibEmbeddedResource class *
// ***************************************************************************
ZlibEmbeddedResource::ZlibEmbeddedResource(const char *data,
std::size_t compressedSize,
std::size_t uncompressedSize)
: AbstractEmbeddedResource(data, compressedSize),
_uncompressedSize(uncompressedSize),
_inBuf(nullptr),
_inBufSize(262144), // adjusted below in the constructor body
_outBuf(nullptr),
_outBufSize(262144),
_putbackSize(0) // default for best performance
{
static_assert(262144 <= std::numeric_limits<std::size_t>::max(),
"The std::size_t type is unexpectedly small.");
// No need to use an input buffer (where compressed data chunks are put for
// zlib to read and decompress) larger than the whole compressed resource!
_inBufSize = std::min(rawSize(), _inBufSize);
}
AbstractEmbeddedResource::CompressionType
ZlibEmbeddedResource::compressionType() const
{ return AbstractEmbeddedResource::CompressionType::ZLIB; }
string ZlibEmbeddedResource::compressionDescr() const
{ return string("zlib"); }
std::size_t ZlibEmbeddedResource::uncompressedSize() const
{ return _uncompressedSize; }
char* ZlibEmbeddedResource::getInputBufferStart()
{ return _inBuf; }
void ZlibEmbeddedResource::setInputBufferStart(char* inBuf)
{ _inBuf = inBuf; }
std::size_t ZlibEmbeddedResource::getInputBufferSize()
{ return _inBufSize; }
void ZlibEmbeddedResource::setInputBufferSize(std::size_t size)
{ _inBufSize = size; }
char* ZlibEmbeddedResource::getOutputBufferStart()
{ return _outBuf; }
void ZlibEmbeddedResource::setOutputBufferStart(char* outBuf)
{ _outBuf = outBuf; }
std::size_t ZlibEmbeddedResource::getOutputBufferSize()
{ return _outBufSize; }
void ZlibEmbeddedResource::setOutputBufferSize(std::size_t size)
{ _outBufSize = size; }
std::size_t ZlibEmbeddedResource::getPutbackSize()
{ return _putbackSize; }
void ZlibEmbeddedResource::setPutbackSize(std::size_t size)
{ _putbackSize = size; }
unique_ptr<std::streambuf> ZlibEmbeddedResource::streambuf() const
{
unique_ptr<CharArrayIStream> rawReaderIStream(
new CharArrayIStream(rawPtr(), rawSize()));
return unique_ptr<std::streambuf>(
new ZlibDecompressorIStreambuf(
std::move(rawReaderIStream),
SGPath(), // rawReaderIStream isn't bound to a file
ZLibCompressionFormat::ZLIB,
_inBuf, _inBufSize, _outBuf, _outBufSize, _putbackSize));
}
unique_ptr<std::istream> ZlibEmbeddedResource::istream() const
{
unique_ptr<CharArrayIStream> rawReaderIStream(
new CharArrayIStream(rawPtr(), rawSize()));
return unique_ptr<std::istream>(
new ZlibDecompressorIStream(
std::move(rawReaderIStream),
SGPath(), // rawReaderIStream isn't bound to a file
ZLibCompressionFormat::ZLIB,
_inBuf, _inBufSize, _outBuf, _outBufSize, _putbackSize));
}
std::string ZlibEmbeddedResource::str() const
{
static constexpr std::size_t bufSize = 65536;
static_assert(bufSize <= std::numeric_limits<std::streamsize>::max(),
"Type std::streamsize is unexpectedly small");
static_assert(bufSize <= std::numeric_limits<string::size_type>::max(),
"Type std::string::size_type is unexpectedly small");
unique_ptr<char[]> buf(new char[bufSize]);
auto decompressor =
static_unique_ptr_cast<ZlibDecompressorIStream>(istream());
std::streamsize nbCharsRead;
string result;
if (_uncompressedSize > std::numeric_limits<string::size_type>::max()) {
throw sg_range_exception(
"Resource too large to fit in an std::string (uncompressed size: "
+ std::to_string(_uncompressedSize) + " bytes)");
} else {
result.reserve(static_cast<string::size_type>(_uncompressedSize));
}
do {
decompressor->read(buf.get(), bufSize);
nbCharsRead = decompressor->gcount();
if (nbCharsRead > 0) {
result.append(buf.get(), nbCharsRead);
}
} while (*decompressor);
// decompressor->fail() would *not* indicate an error, due to the semantics
// of std::istream::read().
if (decompressor->bad()) {
throw sg_io_exception("Error while extracting a compressed resource");
}
return result;
}
// ***************************************************************************
// * Stream insertion operators *
// ***************************************************************************
std::ostream& operator<<(std::ostream& os,
const RawEmbeddedResource& resource)
{ // This won't escape double quotes, backslashes, etc. in resource.str().
return os << "RawEmbeddedResource:\n"
" compressionType = \"" << resource.compressionDescr() << "\"\n"
" rawPtr = " << (void*) resource.rawPtr() << "\n"
" rawSize = " << resource.rawSize();
}
std::ostream& operator<<(std::ostream& os,
const ZlibEmbeddedResource& resource)
{ // This won't escape double quotes, backslashes, etc. in resource.str().
return os << "ZlibEmbeddedResource:\n"
" compressionType = \"" << resource.compressionDescr() << "\"\n"
" rawPtr = " << (void*) resource.rawPtr() << "\n"
" rawSize = " << resource.rawSize() << "\n"
" uncompressedSize = " << resource.uncompressedSize();
}
} // of namespace simgear

View File

@@ -1,163 +0,0 @@
// -*- coding: utf-8 -*-
//
// EmbeddedResource.hxx --- Class for pointing to/accessing an embedded resource
// Copyright (C) 2017 Florent Rougon
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA.
#ifndef FG_EMBEDDEDRESOURCE_HXX
#define FG_EMBEDDEDRESOURCE_HXX
#include <iosfwd>
#include <string>
#include <ostream>
#include <memory> // std::unique_ptr
#include <cstddef> // std::size_t, std::ptrdiff_t
#include <simgear/io/iostreams/zlibstream.hxx>
namespace simgear
{
// Abstract base class for embedded resources
class AbstractEmbeddedResource
{
public:
enum class CompressionType {
NONE = 0,
ZLIB
};
// Constructor.
//
// 'data' and 'size' indicate the resource contents. There is no requirement
// of null-termination, including for text data (given how
// EmbeddedResourceManager::getString() works, including a null terminator
// for text contents is actually counter-productive). The data may be of
// arbitrary type and size: binary, text, whatever. The constructed object
// (for derived classes since this one is abstract) does *not* hold a copy
// of the data, it just keeps a pointer to it and provides methods to access
// it. The data must therefore remain available as long as the object is in
// use---this class was designed for use with data stored in static
// variables.
explicit AbstractEmbeddedResource(const char *data, std::size_t size);
AbstractEmbeddedResource(const AbstractEmbeddedResource&) = default;
AbstractEmbeddedResource(AbstractEmbeddedResource&&) = default;
AbstractEmbeddedResource& operator=(const AbstractEmbeddedResource&) = default;
AbstractEmbeddedResource& operator=(AbstractEmbeddedResource&&) = default;
virtual ~AbstractEmbeddedResource() = default;
// Return the pointer to beginning-of-resource contents---the same that was
// passed to the constructor.
const char *rawPtr() const;
// Return the resource size, as passed to the constructor. For a compressed
// resource, this is the compressed size; such resources provide an
// additional uncompressedSize() method.
std::size_t rawSize() const;
// Return an std::string object containing a copy of the resource contents.
// For a compressed resource, this is the data obtained after decompression.
virtual std::string str() const;
// Return an std::streambuf instance providing read-only access to the
// resource contents (in uncompressed form for compressed resources). This
// allows memory-friendly access to large resources by enabling incremental
// processing with transparent decompression for compressed resources.
virtual std::unique_ptr<std::streambuf> streambuf() const = 0;
// Return an std::istream instance providing read-only access to the
// resource contents (in uncompressed form for compressed resources).
//
// The same remark as for streambuf() applies. std::istream is simply a
// higher-level interface than std::streambuf, otherwise both allow the same
// kind of processing.
virtual std::unique_ptr<std::istream> istream() const = 0;
// Return the resource compression type.
virtual CompressionType compressionType() const = 0;
// Return a string description of the resource compression type. Examples:
// "none", "zlib".
virtual std::string compressionDescr() const = 0;
private:
// Pointer to the start of resource contents
const char *_data;
// Size of resource contents, in bytes
std::size_t _size;
};
// Class to describe an uncompressed resource. See AbstractEmbeddedResource.
class RawEmbeddedResource : public AbstractEmbeddedResource
{
public:
explicit RawEmbeddedResource(const char *data, std::size_t size);
AbstractEmbeddedResource::CompressionType compressionType() const override;
std::string compressionDescr() const override;
// The str() method is inherited from AbstractEmbeddedResource
std::unique_ptr<std::streambuf> streambuf() const override;
std::unique_ptr<std::istream> istream() const override;
};
// Class to describe a zlib-compressed resource.
//
// Instances of this class point to resource contents stored in the stream
// format documented in RFC 1950.
class ZlibEmbeddedResource : public AbstractEmbeddedResource
{
public:
explicit ZlibEmbeddedResource(const char *data, std::size_t compressedSize,
std::size_t uncompressedSize);
AbstractEmbeddedResource::CompressionType compressionType() const override;
std::string compressionDescr() const override;
// Return the resource uncompressed size, in bytes.
std::size_t uncompressedSize() const;
std::string str() const override;
std::unique_ptr<std::streambuf> streambuf() const override;
std::unique_ptr<std::istream> istream() const override;
// Getters and setters for parameters used in streambuf() and istream().
// Calling any of the setters affects the subsequent streambuf() and
// istream() calls.
char* getInputBufferStart();
void setInputBufferStart(char* inBuf);
std::size_t getInputBufferSize();
void setInputBufferSize(std::size_t size);
char* getOutputBufferStart();
void setOutputBufferStart(char* outBuf);
std::size_t getOutputBufferSize();
void setOutputBufferSize(std::size_t size);
std::size_t getPutbackSize();
void setPutbackSize(std::size_t size);
private:
std::size_t _uncompressedSize;
char* _inBuf;
std::size_t _inBufSize;
char* _outBuf;
std::size_t _outBufSize;
std::size_t _putbackSize;
};
// These functions are essentially intended for troubleshooting purposes.
std::ostream& operator<<(std::ostream&, const RawEmbeddedResource&);
std::ostream& operator<<(std::ostream&, const ZlibEmbeddedResource&);
} // of namespace simgear
#endif // of FG_EMBEDDEDRESOURCE_HXX

View File

@@ -1,235 +0,0 @@
// -*- coding: utf-8 -*-
//
// EmbeddedResourceManager.cxx --- Manager class for resources embedded in an
// executable
// Copyright (C) 2017 Florent Rougon
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA.
#include <simgear_config.h>
#include <memory>
#include <utility> // std::move()
#include <string>
#include <vector>
#include <cstdlib>
#include <cassert>
#include <simgear/structure/exception.hxx>
#include "EmbeddedResource.hxx"
#include "EmbeddedResourceManager.hxx"
#include "EmbeddedResourceManager_private.hxx"
using std::string;
using std::shared_ptr;
using std::unique_ptr;
namespace simgear
{
static unique_ptr<EmbeddedResourceManager> staticInstance;
// ***************************************************************************
// * EmbeddedResourceManager::Impl *
// ***************************************************************************
EmbeddedResourceManager::Impl::Impl()
: dirty(true)
{ }
void
EmbeddedResourceManager::Impl::rehash()
{
// Update the list of resource pools to search when looking up a resource.
// This allows to optimize resource lookup: no need to parse, split and hash
// the same locale string every time to find the corresponding resource
// pools.
poolSearchList = listOfResourcePoolsToSearch(selectedLocale);
dirty = false;
}
string
EmbeddedResourceManager::Impl::getLocale() const
{
return selectedLocale;
}
string
EmbeddedResourceManager::Impl::selectLocale(const std::string& locale)
{
string previousLocale = std::move(selectedLocale);
selectedLocale = locale;
dirty = true;
return previousLocale;
}
// Static method
std::vector<string>
EmbeddedResourceManager::Impl::localesSearchList(const string& locale)
{
std::vector<string> result;
if (locale.empty()) {
result.push_back(string()); // only the default locale
} else {
std::size_t sepIdx = locale.find_first_of('_');
if (sepIdx == string::npos) {
// Try the given “locale” first (e.g., fr), then the default locale
result = std::vector<string>({locale, string()});
} else {
string langCode = locale.substr(0, sepIdx);
// Try the given “locale” first (e.g., fr_FR), then the language code
// (e.g., fr) and finally the default locale
result = std::vector<string>({locale, langCode, string()});
}
}
return result;
}
auto
EmbeddedResourceManager::Impl::listOfResourcePoolsToSearch(
const string& locale) const
-> std::vector< shared_ptr<ResourcePool> >
{
std::vector<string> searchedLocales = localesSearchList(locale);
std::vector< shared_ptr<ResourcePool> > result;
for (const string& loc: searchedLocales) {
auto poolPtrIt = localeToResourcePoolMap.find(loc);
// Don't store pointers to empty resource pools in 'result'. This
// optimizes resource fetching a little bit, but requires that all
// resources are added before this method is called.
if (poolPtrIt != localeToResourcePoolMap.end()) {
// Copy a shared_ptr<ResourcePool>
result.push_back(poolPtrIt->second);
}
}
return result;
}
// Static method
shared_ptr<const AbstractEmbeddedResource>
EmbeddedResourceManager::Impl::lookupResourceInPools(
const string& virtualPath,
const std::vector< shared_ptr<ResourcePool> >& aPoolSearchList)
{
// Search the provided resource pools in proper order. For instance, the one
// for 'fr_FR', then the one for 'fr' and finally the one for the default
// locale. Return the first resource found in one of these pools.
for (const shared_ptr<ResourcePool>& poolPtr: aPoolSearchList) {
auto resourcePtrIt = poolPtr->find(virtualPath);
if (resourcePtrIt != poolPtr->end()) {
// Copy a shared_ptr<const AbstractEmbeddedResource>
return resourcePtrIt->second;
}
}
return shared_ptr<const AbstractEmbeddedResource>(); // null shared_ptr object
}
void
EmbeddedResourceManager::Impl::addResource(
const string& virtualPath,
unique_ptr<const AbstractEmbeddedResource> resourcePtr,
const string& locale)
{
// Find the resource pool corresponding to the specified locale
shared_ptr<ResourcePool>& resPoolPtr = localeToResourcePoolMap[locale];
if (!resPoolPtr) {
resPoolPtr.reset(new ResourcePool());
}
auto emplaceRetval = resPoolPtr->emplace(virtualPath, std::move(resourcePtr));
if (!emplaceRetval.second) {
const string localeDescr =
(locale.empty()) ? "the default locale" : "locale '" + locale + "'";
throw sg_error(
"Virtual path already in use for " + localeDescr +
" in the EmbeddedResourceManager: '" + virtualPath + "'");
}
dirty = true;
}
// ***************************************************************************
// * EmbeddedResourceManager *
// ***************************************************************************
EmbeddedResourceManager::EmbeddedResourceManager()
: p(unique_ptr<Impl>(new Impl))
{ }
const unique_ptr<EmbeddedResourceManager>&
EmbeddedResourceManager::createInstance()
{
staticInstance.reset(new EmbeddedResourceManager);
return staticInstance;
}
const unique_ptr<EmbeddedResourceManager>&
EmbeddedResourceManager::instance()
{
return staticInstance;
}
string
EmbeddedResourceManager::getLocale() const
{
return p->getLocale();
}
string
EmbeddedResourceManager::selectLocale(const std::string& locale)
{
return p->selectLocale(locale);
}
void
EmbeddedResourceManager::addResource(
const string& virtualPath,
unique_ptr<const AbstractEmbeddedResource> resourcePtr,
const string& locale)
{
p->addResource(virtualPath, std::move(resourcePtr), locale);
}
shared_ptr<const AbstractEmbeddedResource>
EmbeddedResourceManager::getResourceOrNullPtr(const string& virtualPath) const
{
if (p->dirty) {
p->rehash(); // update p->poolSearchList
}
// Use the selected locale
return p->lookupResourceInPools(virtualPath, p->poolSearchList);
}
shared_ptr<const AbstractEmbeddedResource>
EmbeddedResourceManager::getResourceOrNullPtr(const string& virtualPath,
const string& locale) const
{
// In this overload, we don't use the cached list of pools
// (p->poolSearchList), therefore there is no need to check the 'dirty' flag
// or to rehash().
return p->lookupResourceInPools(virtualPath,
p->listOfResourcePoolsToSearch(locale));
}
} // of namespace simgear

View File

@@ -1,204 +0,0 @@
// -*- coding: utf-8 -*-
//
// EmbeddedResourceManager.hxx --- Manager class for resources embedded in an
// executable
// Copyright (C) 2017 Florent Rougon
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA.
#ifndef FG_EMBEDDEDRESOURCEMANAGER_HXX
#define FG_EMBEDDEDRESOURCEMANAGER_HXX
#include <string>
#include <memory> // std::unique_ptr, std::shared_ptr
#include <unordered_map>
#include <vector>
#include <utility> // std::forward()
#include <cstddef> // std::size_t
#include <simgear/structure/exception.hxx>
#include "EmbeddedResource.hxx"
namespace simgear
{
class EmbeddedResourceManager
{
public:
EmbeddedResourceManager(const EmbeddedResourceManager&) = delete;
EmbeddedResourceManager& operator=(const EmbeddedResourceManager&) = delete;
EmbeddedResourceManager(EmbeddedResourceManager&&) = delete;
EmbeddedResourceManager& operator=(EmbeddedResourceManager&&) = delete;
// The instance is created by createInstance() -> private constructor
// but it should be deleted by its owning std::unique_ptr -> public destructor
~EmbeddedResourceManager() = default;
// Static creator
static const std::unique_ptr<EmbeddedResourceManager>& createInstance();
// Singleton accessor
static const std::unique_ptr<EmbeddedResourceManager>& instance();
// Return the currently-selected “locale”[*] for resource fetching.
//
// [*] For instance: std::string("") for the default locale
// std::string("fr") for French
// std::string("fr_FR") for French from France
std::string getLocale() const;
// Select the locale for which resources will be returned in the future, for
// the getResourceOrNullPtr(), getResource(), getString(), getStreambuf()
// and getIStream() overloads that don't have a 'locale' parameter.
// May be called several times. Return the previously-selected locale.
//
// If you just want to fetch one or two resources in a particular “locale”
// (language), it is simpler to use an overload of one of the
// getResourceOrNullPtr(), getResource(), ..., getIStream() methods that has
// a 'locale' parameter.
std::string selectLocale(const std::string& locale);
// Add a resource for the specified locale to the embedded resource manager.
// This method acts as a sink for its second argument (the std::unique_ptr
// typically has to be std::move()d). If 'locale' is empty, the resource is
// added for the default locale.
void addResource(const std::string& virtualPath,
std::unique_ptr<const AbstractEmbeddedResource> resourcePtr,
const std::string& locale = std::string());
// Get access to a resource.
//
// Fetch the resource for the selected locale (cf. selectLocale()), with
// fallback behavior[1]. If no resource is found for the given
// 'virtualPath', return a null
// std::shared_ptr<const AbstractEmbeddedResource>.
//
// [1] This means that for instance, if the selected locale is 'es_ES', the
// resource is first looked up for the 'es_ES' “locale”; then, if not
// found, for 'es'; and finally, if still not found, for the default
// locale ''.
std::shared_ptr<const AbstractEmbeddedResource> getResourceOrNullPtr(
const std::string& virtualPath) const;
// Same as the previous overload, except the resource is fetched for the
// specified locale (with fallback behavior) instead of for the selected
// locale. Use an empty 'locale' parameter to fetch the resource for the
// default locale.
std::shared_ptr<const AbstractEmbeddedResource> getResourceOrNullPtr(
const std::string& virtualPath,
const std::string& locale) const;
// Same overloads as for getResourceOrNullPtr(), except that if the resource
// isn't found, then an sg_exception is raised. These methods never return
// a null or empty std::shared_ptr<const AbstractEmbeddedResource>.
template <typename ...Args>
std::shared_ptr<const AbstractEmbeddedResource> getResource(
const std::string& virtualPath, Args&& ...args) const
{
const auto resPtr = getResourceOrNullPtr(virtualPath,
std::forward<Args>(args)...);
if (!resPtr) {
throw sg_exception("No embedded resource found at virtual path '" +
virtualPath + "'");
}
return resPtr;
}
// Get a resource contents in the form of an std::string. Raise an
// sg_exception if no resource is found for the specified 'virtualPath'.
//
// The returned std::string is a copy of the resource contents (possibly
// transparently decompressed, cf. simgear::ZlibEmbeddedResource).
template <typename ...Args>
std::string getString(const std::string& virtualPath, Args&& ...args) const
{
return getResource(virtualPath, std::forward<Args>(args)...)->str();
}
// Get access to a resource via an std::streambuf instance. Raise an
// sg_exception if no resource is found for the specified 'virtualPath'.
//
// This allows one to incrementally process the resource contents without
// ever making a copy of it (including incremental, transparent
// decompression if the resource happens to be compressed---cf.
// simgear::ZlibEmbeddedResource).
template <typename ...Args>
std::unique_ptr<std::streambuf> getStreambuf(const std::string& virtualPath,
Args&& ...args) const
{
return getResource(virtualPath, std::forward<Args>(args)...)->streambuf();
}
// Get access to a resource via an std::istream instance. Raise an
// sg_exception if no resource is found for the specified 'virtualPath'.
//
// The same remarks made for getStreambuf() apply here too.
template <typename ...Args>
std::unique_ptr<std::istream> getIStream(const std::string& virtualPath,
Args&& ...args) const
{
return getResource(virtualPath, std::forward<Args>(args)...)->istream();
}
private:
// Constructor called from createInstance() only
explicit EmbeddedResourceManager();
class Impl;
const std::unique_ptr<Impl> p; // Pimpl idiom
};
// Explicit template instantiations
template
std::shared_ptr<const AbstractEmbeddedResource>
EmbeddedResourceManager::getResource(const std::string& virtualPath) const;
template
std::string
EmbeddedResourceManager::getString(const std::string& virtualPath) const;
template
std::unique_ptr<std::streambuf>
EmbeddedResourceManager::getStreambuf(const std::string& virtualPath) const;
template
std::unique_ptr<std::istream>
EmbeddedResourceManager::getIStream(const std::string& virtualPath) const;
// MSVC doesn't recognize these as template instantiations of what we defined
// above (this seems to be with “Visual Studio 14 2015”), therefore only
// include them with other compilers.
#ifndef _MSC_VER
template
std::shared_ptr<const AbstractEmbeddedResource>
EmbeddedResourceManager::getResource(const std::string& virtualPath,
const std::string& locale) const;
template
std::string
EmbeddedResourceManager::getString(const std::string& virtualPath,
const std::string& locale) const;
template
std::unique_ptr<std::streambuf>
EmbeddedResourceManager::getStreambuf(const std::string& virtualPath,
const std::string& locale) const;
template
std::unique_ptr<std::istream>
EmbeddedResourceManager::getIStream(const std::string& virtualPath,
const std::string& locale) const;
#endif // #ifndef _MSC_VER
} // of namespace simgear
#endif // of FG_EMBEDDEDRESOURCEMANAGER_HXX

View File

@@ -1,102 +0,0 @@
// -*- coding: utf-8 -*-
//
// EmbeddedResourceManager_private.hxx --- Private implementation class for
// SimGear's EmbeddedResourceManager
// Copyright (C) 2017 Florent Rougon
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA.
#ifndef FG_EMBEDDEDRESOURCEMANAGERPRIVATE_HXX
#define FG_EMBEDDEDRESOURCEMANAGERPRIVATE_HXX
#include <string>
#include <memory> // std::unique_ptr, std::shared_ptr
#include <unordered_map>
#include <vector>
#include "EmbeddedResource.hxx"
namespace simgear
{
class EmbeddedResourceManager::Impl
{
public:
explicit Impl();
// Each “locale” for which addResource() has been used has an associated
// resource pool, that is a sort of directory of all resources declared in
// this locale. The resource pool for a given locale (e.g., 'fr' or 'de_DE')
// maps resource virtual paths to the corresponding resource descriptors
// (via std::shared_ptr<const AbstractEmbeddedResource> instances).
//
// Note: for optimal lookup performance, a tree would probably be better,
// since the expected use for each key here is to store a virtual
// path. But such an optimization is likely unneeded in most cases.
typedef std::unordered_map< std::string,
std::shared_ptr<const AbstractEmbeddedResource> >
ResourcePool;
// Return the list of “locales” to scan to implement fallback behavior when
// fetching a resource for the specified locale. This list will be searched
// from left to right. Examples:
//
// "" -> [""]
// "fr" -> ["fr", ""]
// "fr_FR" -> ["fr_FR", "fr", ""]
static std::vector<std::string> localesSearchList(const std::string& locale);
// Same as localesSearchList(), except it returns the resource pools instead
// of the “locale” strings, and only those pools that are not empty.
std::vector< std::shared_ptr<ResourcePool> > listOfResourcePoolsToSearch(
const std::string& locale) const;
// Look up, in each of the pools referred to by 'poolSearchList', the
// resource associated to 'virtualPath'. Return the first match.
static std::shared_ptr<const AbstractEmbeddedResource> lookupResourceInPools(
const std::string& virtualPath,
const std::vector< std::shared_ptr<ResourcePool> >& poolSearchList);
// Recompute p->poolSearchList. This method is automatically called whenever
// needed (lazily), so it doesn't need to be part of the public interface.
void rehash();
// Implement the corresponding EmbeddedResourceManager public methods
std::string getLocale() const;
std::string selectLocale(const std::string& locale);
// Ditto
void addResource(const std::string& virtualPath,
std::unique_ptr<const AbstractEmbeddedResource> resourcePtr,
const std::string& locale);
std::string selectedLocale;
// Each call to rehash() updates this member to contain precisely the
// (ordered) list of pools to search for a resource in the selected
// “locale”. This allows relatively cheap resource lookups, assuming the
// desired “locale” doesn't change all the time.
std::vector< std::shared_ptr<ResourcePool> > poolSearchList;
// Indicate whether 'poolSearchList' must be updated (i.e., resources have
// been added or the selected locale was changed without rehash() being
// called afterwards).
bool dirty;
// Maps each “locale name” to the corresponding resource pool.
std::unordered_map< std::string,
std::shared_ptr<ResourcePool> > localeToResourcePoolMap;
};
} // of namespace simgear
#endif // of FG_EMBEDDEDRESOURCEMANAGERPRIVATE_HXX

View File

@@ -1,257 +0,0 @@
// -*- 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

View File

@@ -1,155 +0,0 @@
// -*- 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

View File

@@ -1,516 +0,0 @@
// -*- coding: utf-8 -*-
//
// embedded_resources_test.cxx --- Automated tests for the embedded resources
// system in SimGear
//
// Copyright (C) 2017 Florent Rougon
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA.
#include <simgear_config.h>
#include <string>
#include <memory>
#include <ios> // std::streamsize
#include <iostream> // std::cout (used for progress info)
#include <limits> // std::numeric_limits
#include <type_traits> // std::make_unsigned()
#include <sstream>
#include <cstdlib> // EXIT_SUCCESS
#include <cstddef> // std::size_t
#include <simgear/misc/test_macros.hxx>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/io/iostreams/CharArrayStream.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/io/iostreams/zlibstream.hxx>
#include "EmbeddedResource.hxx"
#include "EmbeddedResourceManager.hxx"
#include "EmbeddedResourceProxy.hxx"
using std::cout;
using std::cerr;
using std::endl;
using std::string;
using std::unique_ptr;
using std::shared_ptr;
using simgear::AbstractEmbeddedResource;
using simgear::RawEmbeddedResource;
using simgear::ZlibEmbeddedResource;
using simgear::EmbeddedResourceManager;
typedef typename std::make_unsigned<std::streamsize>::type uStreamSize;
// Safely convert a non-negative std::streamsize into an std::size_t. If
// impossible, bail out.
std::size_t streamsizeToSize_t(std::streamsize n)
{
SG_CHECK_GE(n, 0);
SG_CHECK_LE(static_cast<uStreamSize>(n),
std::numeric_limits<std::size_t>::max());
return static_cast<std::size_t>(n);
}
// This array is null-terminated, but we'll declare the resource size as
// sizeof(res1Array) - 1 so that the null char is *not* part of it. This
// way allows one to treat text and binary resources exactly the same way,
// with the conversion to std::string via a simple
// std::string(res1Array, resourceSize) not producing a bizarre std::string
// instance whose last character would be '\0' (followed in memory by the same
// '\0' used as C-style string terminator this time!).
static const char res1Array[] = "This is a simple embedded resource test.";
static const char res1frArray[] = "Ceci est un petit test de ressource "
"embarquée.";
static const char res1fr_FRArray[] = "Ceci est un petit test de ressource "
"embarquée (variante fr_FR).";
static const string lipsum = "\
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque congue ornare\n\
congue. Mauris mollis est et porttitor condimentum. Vivamus laoreet blandit\n\
odio eget consectetur. Etiam quis magna eu enim luctus pretium. In et\n\
tristique nunc, non efficitur metus. Nullam efficitur tristique velit.\n\
Praesent et luctus nunc. Mauris eros eros, rutrum at molestie quis, egestas et\n\
lorem. Ut nulla turpis, eleifend sed mauris ac, faucibus molestie nulla.\n\
Quisque viverra vel turpis nec efficitur. Proin non rutrum velit. Nam sodales\n\
metus felis, eu pharetra velit posuere ut.";
// Should be enough to store the compressed lipsum (320 bytes are required
// with zlib 1.2.8, keeping some room to account for possible future format
// changes in the zlib output...). In any case, there is no risk of buffer
// overflow, because simgear::CharArrayOStream prevents this by design.
static char res2Array[350];
static const char res2frArray[] = "Un lorem ipsum un peu plus court...";
// Read data from a string and write it in compressed form to the specified
// buffer.
std::size_t writeCompressedDataToBuffer(const string& inputString,
char *outBuf,
std::size_t outBufSize)
{
simgear::CharArrayOStream res2Writer(outBuf, outBufSize);
std::istringstream iss(inputString);
simgear::ZlibCompressorIStream compressor(
iss,
SGPath(), /* no associated file */
9 /* highest compression level */);
static constexpr std::size_t bufSize = 1024;
unique_ptr<char[]> buf(new char[bufSize]);
std::size_t res2Size = 0;
do {
compressor.read(buf.get(), bufSize);
std::streamsize nBytes = compressor.gcount();
if (nBytes > 0) { // at least one char could be read
res2Writer.write(buf.get(), nBytes);
res2Size += nBytes;
}
} while (compressor && res2Writer);
SG_VERIFY(compressor.eof()); // all the compressed data has been read
// This would fail (among other causes) if the output buffer were too small
// to hold all of the compressed data.
SG_VERIFY(res2Writer);
return res2Size;
}
void initResources()
{
cout << "Creating the EmbeddedResourceManager instance and adding a few "
"resources to it" << endl;
const auto& resMgr = EmbeddedResourceManager::createInstance();
// The resource will *not* consider the null terminator to be in.
unique_ptr<const RawEmbeddedResource> res1(
new RawEmbeddedResource(res1Array, sizeof(res1Array) - 1));
resMgr->addResource("/path/to/resource1", std::move(res1));
unique_ptr<const RawEmbeddedResource> res1fr(
new RawEmbeddedResource(res1frArray, sizeof(res1frArray) - 1));
resMgr->addResource("/path/to/resource1", std::move(res1fr), "fr");
unique_ptr<const RawEmbeddedResource> res1fr_FR(
new RawEmbeddedResource(res1fr_FRArray, sizeof(res1fr_FRArray) - 1));
resMgr->addResource("/path/to/resource1", std::move(res1fr_FR), "fr_FR");
// Write the contents of 'lipsum' in compressed form to the 'res2Array'
// static buffer.
std::size_t res2Size = writeCompressedDataToBuffer(lipsum, res2Array,
sizeof(res2Array));
// Now we have a compressed resource to work with, plus the corresponding
// uncompressed output -> perfect for tests!
unique_ptr<const ZlibEmbeddedResource> res2(
new ZlibEmbeddedResource(res2Array, res2Size, lipsum.size()));
resMgr->addResource("/path/to/resource2", std::move(res2));
unique_ptr<const RawEmbeddedResource> res2fr(
new RawEmbeddedResource(res2frArray, sizeof(res2frArray) - 1));
resMgr->addResource("/path/to/resource2", std::move(res2fr), "fr");
// Explicitly select the default locale (typically, English). This is for
// clarity, but isn't required.
resMgr->selectLocale("");
}
// Auxiliary function for test_RawEmbeddedResource()
void auxTest_RawEmbeddedResource_streambuf()
{
cout << "Testing EmbeddedResourceManager::getStreambuf()" << endl;
const auto& resMgr = EmbeddedResourceManager::instance();
unique_ptr<std::streambuf> sbuf(resMgr->getStreambuf("/path/to/resource1"));
// Just to show an efficient algorithm. For real applications, use larger
// buffer sizes!
static constexpr std::size_t bufSize = 4;
unique_ptr<char[]> buf(new char[bufSize]); // intermediate buffer
std::streamsize nbCharsRead;
string result;
do {
nbCharsRead = sbuf->sgetn(buf.get(), bufSize);
// The conversion to std::size_t is safe because sbuf->sgetn() returned a
// non-negative value which, in this case, can't exceed bufSize.
result.append(buf.get(), streamsizeToSize_t((nbCharsRead)));
} while (nbCharsRead == bufSize);
SG_CHECK_EQUAL(result, "This is a simple embedded resource test.");
}
// Auxiliary function for test_RawEmbeddedResource()
void auxTest_RawEmbeddedResource_istream()
{
cout << "Testing EmbeddedResourceManager::getIStream()" << endl;
const auto& resMgr = EmbeddedResourceManager::instance();
unique_ptr<std::istream> iStream(resMgr->getIStream("/path/to/resource1"));
// This is convenient, but be aware that still in 2017, some buggy C++
// compilers don't allow the exception to be caught: cf.
// <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66145>.
iStream->exceptions(std::ios_base::badbit);
// Just to show an efficient algorithm. For real applications, use larger
// buffer sizes!
static constexpr std::size_t bufSize = 4;
unique_ptr<char[]> buf(new char[bufSize]); // intermediate buffer
string result;
do {
iStream->read(buf.get(), bufSize);
result.append(buf.get(), iStream->gcount());
} while (*iStream); // iStream *points* to an std::istream
// 1) If set, badbit would have caused an exception to be raised (see above).
// 2) failbit doesn't necessarily indicate an error here: it is set as soon
// as the read() call can't provide the requested number of characters.
SG_VERIFY(iStream->eof() && !iStream->bad());
SG_CHECK_EQUAL(result, "This is a simple embedded resource test.");
}
void test_RawEmbeddedResource()
{
cout << "Testing resource fetching methods of EmbeddedResourceManager with "
"a RawEmbeddedResource" << endl;
const auto& resMgr = EmbeddedResourceManager::instance();
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
string("This is a simple embedded resource test."));
// Get a shared_ptr to a const AbstractEmbeddedResource
const auto res1abs = resMgr->getResource("/path/to/resource1");
// Okay because we know this resource is not a compressed one
const auto res1 =
std::dynamic_pointer_cast<const RawEmbeddedResource>(res1abs);
SG_VERIFY(res1);
// Print a representation of the resource metadata
std::cout << "\n/path/to/resource1 -> " << *res1 << "\n\n";
// The following methods would work the same with res1abs
SG_CHECK_EQUAL_NOSTREAM(res1->compressionType(),
AbstractEmbeddedResource::CompressionType::NONE);
SG_CHECK_EQUAL(res1->compressionDescr(), "none");
SG_CHECK_EQUAL(res1->rawPtr(), res1Array);
SG_CHECK_EQUAL(res1->rawSize(), sizeof(res1Array) - 1); // see above
SG_CHECK_EQUAL(res1->str(),
string("This is a simple embedded resource test."));
auxTest_RawEmbeddedResource_streambuf();
auxTest_RawEmbeddedResource_istream();
// Just reload and recheck the resource, because we can :)
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
string("This is a simple embedded resource test."));
}
void test_ZlibEmbeddedResource()
{
cout << "Testing resource fetching methods of EmbeddedResourceManager with "
"a ZlibEmbeddedResource" << endl;
const auto& resMgr = EmbeddedResourceManager::instance();
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource2"),
lipsum);
// Get a shared_ptr to a const AbstractEmbeddedResource
const auto res2abs = resMgr->getResource("/path/to/resource2");
// Okay because we know this resource is a Zlib-compressed one
const auto res2 =
std::dynamic_pointer_cast<const ZlibEmbeddedResource>(res2abs);
SG_VERIFY(res2);
SG_CHECK_EQUAL(res2->uncompressedSize(), lipsum.size());
// Print a representation of the resource metadata
std::cout << "\n/path/to/resource2 -> " << *res2 << "\n\n";
cout << "Resource 2 compression ratio: " <<
static_cast<float>(res2->uncompressedSize()) /
static_cast<float>(res2->rawSize()) << "\n";
// Just reload and recheck the resource
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource2"), lipsum);
}
void test_getMissingResources()
{
cout << "Testing the behavior of EmbeddedResourceManager when trying to "
"fetch inexistent resources" << endl;
const auto& resMgr = EmbeddedResourceManager::instance();
SG_VERIFY(!resMgr->getResourceOrNullPtr("/inexistant/resource"));
bool gotException = false;
try {
resMgr->getResource("/inexistant/resource");
} catch (const sg_exception&) {
gotException = true;
}
SG_VERIFY(gotException);
gotException = false;
try {
resMgr->getString("/other/inexistant/resource");
} catch (const sg_exception&) {
gotException = true;
}
SG_VERIFY(gotException);
}
void test_addAlreadyExistingResource()
{
cout << "Testing the behavior of EmbeddedResourceManager when trying to "
"add an already existing resource" << endl;
const auto& resMgr = EmbeddedResourceManager::instance();
for (const string& locale: {"", "fr", "fr_FR"}) {
// For these tests, we don't care about the resource contents -> no need
// to substract 1 from the result of sizeof() as we did above.
unique_ptr<const RawEmbeddedResource> someRes(
new RawEmbeddedResource(res1fr_FRArray, sizeof(res1fr_FRArray)));
bool gotException = false;
try {
resMgr->addResource("/path/to/resource1", std::move(someRes), locale);
} catch (const sg_error&) {
gotException = true;
}
SG_VERIFY(gotException);
}
}
void test_localeDependencyOfResourceFetching()
{
cout << "Testing the locale-dependency of resource fetching from "
"EmbeddedResourceManager" << endl;
const auto& resMgr = EmbeddedResourceManager::instance();
resMgr->selectLocale(""); // select the default locale
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
"This is a simple embedded resource test.");
// Switch to the 'fr_FR' locale (French from France)
resMgr->selectLocale("fr_FR");
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
"Ceci est un petit test de ressource embarquée (variante "
"fr_FR).");
// This one is for the 'fr' “locale”, obtained as fallback since there is no
// resource mapped to /path/to/resource2 for the 'fr_FR' “locale”.
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource2"),
"Un lorem ipsum un peu plus court...");
// Explicitly ask for the resource in the default locale
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1", ""),
"This is a simple embedded resource test.");
// Switch to the 'fr' locale (French)
resMgr->selectLocale("fr");
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
"Ceci est un petit test de ressource embarquée.");
// Explicitly ask for the resource in the 'fr_FR' locale
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1", "fr_FR"),
"Ceci est un petit test de ressource embarquée "
"(variante fr_FR).");
// Switch to the default locale
resMgr->selectLocale("");
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
"This is a simple embedded resource test.");
// Explicitly ask for the resource in the 'fr' locale
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1", "fr"),
"Ceci est un petit test de ressource embarquée.");
// Explicitly ask for the resource in the 'fr_FR' locale
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1", "fr_FR"),
"Ceci est un petit test de ressource embarquée "
"(variante fr_FR).");
// Explicitly ask for the resource in the default locale
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1", ""),
"This is a simple embedded resource test.");
}
void test_getLocaleAndSelectLocale()
{
cout << "Testing the getLocale() and selectLocale() methods of "
"EmbeddedResourceManager" << endl;
const auto& resMgr = EmbeddedResourceManager::instance();
for (const string& locale: {"", "fr", "fr_FR", "de_DE"}) {
// The important effects of setLocale() are tested in
// test_localeDependencyOfResourceFetching()
resMgr->selectLocale(locale);
SG_CHECK_EQUAL(resMgr->getLocale(), locale);
}
}
// Auxiliary function for test_EmbeddedResourceProxy()
void auxTest_EmbeddedResourceProxy_getIStream(unique_ptr<std::istream> iStream,
const string& contents)
{
cout << "Testing EmbeddedResourceProxy::getIStream()" << endl;
iStream->exceptions(std::ios_base::badbit);
static constexpr std::size_t bufSize = 65536;
unique_ptr<char[]> buf(new char[bufSize]); // intermediate buffer
string result;
do {
iStream->read(buf.get(), bufSize);
result.append(buf.get(), iStream->gcount());
} while (*iStream); // iStream *points* to an std::istream
// 1) If set, badbit would have caused an exception to be raised (see above).
// 2) failbit doesn't necessarily indicate an error here: it is set as soon
// as the read() call can't provide the requested number of characters.
SG_VERIFY(iStream->eof() && !iStream->bad());
SG_CHECK_EQUAL(result, contents);
}
void test_EmbeddedResourceProxy()
{
cout << "Testing the EmbeddedResourceProxy class" << endl;
// Initialize stuff we need and create two files containing the contents of
// the default-locale version of two embedded resources: those with virtual
// paths '/path/to/resource1' and '/path/to/resource2'.
const auto& resMgr = EmbeddedResourceManager::instance();
simgear::Dir tmpDir = simgear::Dir::tempDir("FlightGear");
tmpDir.setRemoveOnDestroy();
const SGPath path1 = tmpDir.path() / "resource1";
const SGPath path2 = tmpDir.path() / "resource2";
sg_ofstream out1(path1);
sg_ofstream out2(path2);
const string s1 = resMgr->getString("/path/to/resource1", "");
// To make sure in these tests that we can tell whether something came from
// a real file or from an embedded resource.
const string rs1 = s1 + " from real file";
const string rlipsum = lipsum + " from real file";
out1 << rs1;
out1.close();
if (!out1) {
throw sg_io_exception("Error writing to file", sg_location(path1));
}
out2 << rlipsum;
out2.close();
if (!out2) {
throw sg_io_exception("Error writing to file", sg_location(path2));
}
// 'proxy' defaults to using embedded resources
const simgear::EmbeddedResourceProxy proxy(tmpDir.path(), "/path/to",
/* useEmbeddedResourcesByDefault */
true);
simgear::EmbeddedResourceProxy rproxy(tmpDir.path(), "/path/to");
// 'rproxy' defaults to using real files
rproxy.setUseEmbeddedResources(false); // could be done from the ctor too
// Test EmbeddedResourceProxy::getString()
SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix("/resource1"), rs1);
SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix(":/resource1"), s1);
SG_CHECK_EQUAL(proxy.getString("/resource1", false), rs1);
SG_CHECK_EQUAL(proxy.getString("/resource1", true), s1);
SG_CHECK_EQUAL(proxy.getString("/resource1"), s1);
SG_CHECK_EQUAL(rproxy.getString("/resource1"), rs1);
SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix("/resource2"), rlipsum);
SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix(":/resource2"), lipsum);
SG_CHECK_EQUAL(proxy.getString("/resource2", false), rlipsum);
SG_CHECK_EQUAL(proxy.getString("/resource2", true), lipsum);
SG_CHECK_EQUAL(proxy.getString("/resource2"), lipsum);
SG_CHECK_EQUAL(rproxy.getString("/resource2"), rlipsum);
// Test EmbeddedResourceProxy::getIStream()
auxTest_EmbeddedResourceProxy_getIStream(
proxy.getIStreamDecideOnPrefix("/resource1"),
rs1);
auxTest_EmbeddedResourceProxy_getIStream(
proxy.getIStreamDecideOnPrefix(":/resource1"),
s1);
auxTest_EmbeddedResourceProxy_getIStream(proxy.getIStream("/resource1"), s1);
auxTest_EmbeddedResourceProxy_getIStream(rproxy.getIStream("/resource1"), rs1);
auxTest_EmbeddedResourceProxy_getIStream(proxy.getIStream("/resource2", false),
rlipsum);
auxTest_EmbeddedResourceProxy_getIStream(proxy.getIStream("/resource2", true),
lipsum);
auxTest_EmbeddedResourceProxy_getIStream(proxy.getIStream("/resource2"),
lipsum);
auxTest_EmbeddedResourceProxy_getIStream(rproxy.getIStream("/resource2"),
rlipsum);
}
int main(int argc, char **argv)
{
// Initialize the EmbeddedResourceManager instance, add a few resources
// to it and call its selectLocale() method.
initResources();
test_RawEmbeddedResource();
test_ZlibEmbeddedResource();
test_getMissingResources();
test_addAlreadyExistingResource();
test_localeDependencyOfResourceFetching();
test_getLocaleAndSelectLocale();
test_EmbeddedResourceProxy();
return EXIT_SUCCESS;
}

Some files were not shown because too many files have changed in this diff Show More