Compare commits

..

7 Commits

Author SHA1 Message Date
James Turner
7fbdd9ad2f MSVC lacks stdint.h / inttypes.h
- we can't use simgear/misc/stdint.hxx here, since it's a C++
header and md5.c is compiled as plain C (no bool type available).
2014-02-10 18:03:38 +00:00
James Turner
24539968fd Replace md5 from RSA with OpenBSD version
- no more BSD-with-advertising-license
- files from http://sources.debian.net/src/dpkg/1.17.6/lib/compat
(with SG_ prefixes to avoid collision issues)
2014-02-10 18:03:13 +00:00
James Turner
f13b356531 Race-condition fix in get_effect.
Adapted from a fix by Jeff Biggs. (Shows up easier when using osgEarth,
and multiple osgDB pager threads)
2014-02-10 18:02:58 +00:00
Thomas Geymayer
477594d7bc Canvas: Fix bounding box calculation for paths with subpaths. 2014-02-08 00:25:03 +01:00
Thomas Geymayer
e955a18126 Canvas::Element: expose combined transformation matrix 2014-02-08 00:24:55 +01:00
James Turner
adec1c4507 Add desktop() accessor to SGPath
(Windows-only for the moment)
2014-02-01 09:23:21 +00:00
James Turner
172b8a032c Crash-fix: make mat-lib reference counted.
Make SGReaderWriterOptions own the material lib it's passing to
loader code, so it cannot be freed unexpectedly.
2014-01-23 09:18:39 +00:00
333 changed files with 7509 additions and 25781 deletions

View File

@@ -2,4 +2,3 @@ if (NOT SYSTEM_EXPAT)
add_subdirectory(expat)
endif()
add_subdirectory(utf8)

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,26 +1,8 @@
cmake_minimum_required (VERSION 2.8.11)
if(COMMAND cmake_policy)
if(POLICY CMP0054)
cmake_policy(SET CMP0054 NEW)
endif()
if(POLICY CMP0042)
cmake_policy(SET CMP0042 NEW)
endif()
endif()
cmake_minimum_required (VERSION 2.6.4)
include (CheckFunctionExists)
include (CheckIncludeFile)
include (CheckLibraryExists)
include (CheckCXXSourceCompiles)
include (CheckCXXCompilerFlag)
include (GenerateExportHeader)
# using 10.7 because boost requires libc++ and 10.6 doesn't include it
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.7)
# only relevant for building shared libs but let's set it regardless
set(CMAKE_OSX_RPATH 1)
project(SimGear)
@@ -52,7 +34,7 @@ SET(CPACK_INSTALL_CMAKE_PROJECTS ${CMAKE_CURRENT_BINARY_DIR};SimGear;ALL;/)
# split version string into components, note CMAKE_MATCH_0 is the entire regexp match
string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" CPACK_PACKAGE_VERSION ${SIMGEAR_VERSION} )
set(CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
set(CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
set(CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2})
set(CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3})
@@ -80,7 +62,12 @@ endif(NOT CMAKE_BUILD_TYPE)
# Determine name of library installation directory, i.e. "lib" vs "lib64", which
# differs between all Debian-based vs all other Linux distros.
# See cmake bug #11964, http://cmake.org/gitweb?p=cmake.git;a=commit;h=126c993d
include(GNUInstallDirs)
# GNUInstallDirs requires CMake >= 2.8.5, use own file for older cmake
if(${CMAKE_VERSION} VERSION_GREATER 2.8.4)
include(GNUInstallDirs)
else(${CMAKE_VERSION} VERSION_GREATER 2.8.4)
include(OldGNUInstallDirs)
endif(${CMAKE_VERSION} VERSION_GREATER 2.8.4)
message(STATUS "Library installation directory: ${CMAKE_INSTALL_LIBDIR}")
#####################################################################################
@@ -113,11 +100,11 @@ set(SYSTEM_EXPAT OFF)
endif()
option(SIMGEAR_HEADLESS "Set to ON to build SimGear without GUI/graphics support" OFF)
option(JPEG_FACTORY "Enable JPEG-factory support" OFF)
option(ENABLE_RTI "Set to ON to build SimGear with RTI support" OFF)
option(ENABLE_TESTS "Set to OFF to disable building SimGear's test applications" ON)
option(ENABLE_SOUND "Set to OFF to disable building SimGear's sound support" ON)
option(ENABLE_PKGUTIL "Set to ON to build the sg_pkgutil application (default)" ON)
option(ENABLE_CURL "Set to ON to use libCurl as the HTTP client backend" OFF)
if (MSVC)
GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} PATH)
@@ -174,24 +161,11 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
endif (MSVC AND MSVC_3RDPARTY_ROOT)
if(APPLE)
find_library(COCOA_LIBRARY Cocoa)
# this should be handled by setting CMAKE_OSX_DEPLOYMENT_TARGET
# but it's not working reliably, so forcing it for now
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.7")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.7")
find_library(CORE_SERVICES_LIBRARY CoreServices)
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR
${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
find_package(Threads REQUIRED)
endif()
# Somehow this only works if included before searching for Boost...
include(BoostTestTargets)
find_package(Boost REQUIRED)
set (BOOST_CXX_FLAGS "-DBOOST_BIMAP_DISABLE_SERIALIZATION")
set (BOOST_CXX_FLAGS "-DBOOST_MULTI_INDEX_DISABLE_SERIALIZATION -DBOOST_BIMAP_DISABLE_SERIALIZATION")
if(SIMGEAR_HEADLESS)
message(STATUS "SimGear mode: HEADLESS")
@@ -199,21 +173,25 @@ if(SIMGEAR_HEADLESS)
else()
message(STATUS "SimGear mode: NORMAL")
find_package(OpenGL REQUIRED)
if (ENABLE_SOUND)
find_package(OpenAL REQUIRED)
message(STATUS "Sound support: ENABLED")
endif(ENABLE_SOUND)
find_package(OpenSceneGraph 3.2.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgViewer osgUtil)
find_package(OpenSceneGraph 3.0.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgUtil)
endif(SIMGEAR_HEADLESS)
find_package(ZLIB REQUIRED)
if(JPEG_FACTORY)
message(STATUS "JPEG-factory: ENABLED")
find_package(JPEG REQUIRED)
include_directories(${JPEG_INCLUDE_DIR})
else()
message(STATUS "JPEG-factory: DISABLED")
endif(JPEG_FACTORY)
if (ENABLE_CURL)
find_package(CURL REQUIRED)
message(STATUS "Curl HTTP client: ENABLED")
endif()
find_package(ZLIB REQUIRED)
find_package(Threads REQUIRED)
if (SYSTEM_EXPAT)
message(STATUS "Requested to use system Expat library, forcing SIMGEAR_SHARED to true")
@@ -225,8 +203,8 @@ else()
# XML_STATIC is important to avoid sg_expat_external.h
# declaring symbols as declspec(import)
add_definitions(-DHAVE_EXPAT_CONFIG_H -DXML_STATIC)
set(EXPAT_INCLUDE_DIRS
${PROJECT_SOURCE_DIR}/3rdparty/expat
set(EXPAT_INCLUDE_DIRS
${PROJECT_SOURCE_DIR}/3rdparty/expat
${PROJECT_BINARY_DIR}/3rdparty/expat)
endif(SYSTEM_EXPAT)
@@ -291,9 +269,7 @@ check_cxx_source_compiles(
if(HAVE_DLFCN_H)
check_library_exists(dl dlerror "" HAVE_DL)
if(HAVE_DL)
set(DL_LIBRARY "dl")
endif()
set(DL_LIBRARY "dl")
endif()
SET(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "add a postfix, usually 'd' on windows")
@@ -303,13 +279,13 @@ SET(CMAKE_MINSIZEREL_POSTFIX "" CACHE STRING "add a postfix, usually empty on wi
# isnan might not be real symbol, so can't check using function_exists
check_cxx_source_compiles(
"#include <cmath>
int main() { return isnan(0.0);} "
"#include <cmath>
void f() { isnan(0.0);} "
HAVE_ISNAN)
check_cxx_source_compiles(
"#include <cmath>
int main() { return std::isnan(0.0);} "
"#include <cmath>
void f() { std::isnan(0.0);} "
HAVE_STD_ISNAN)
if(CMAKE_COMPILER_IS_GNUCXX)
@@ -325,12 +301,8 @@ if(CMAKE_COMPILER_IS_GNUCXX)
endif(CMAKE_COMPILER_IS_GNUCXX)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# Boost redeclares class members
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++")
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++")
set(WARNING_FLAGS_CXX "-Wall -Wno-overloaded-virtual")
set(WARNING_FLAGS_C "-Wall")
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
@@ -352,16 +324,16 @@ if(WIN32)
# foreach(warning 4244 4251 4267 4275 4290 4786 4305 4996)
# SET(WARNING_FLAGS "${WARNING_FLAGS} /wd${warning}")
# endforeach(warning)
set(MSVC_FLAGS "-DWIN32 -DNOMINMAX -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D__CRT_NONSTDC_NO_WARNINGS /wd4996 /wd4250 -Dstrdup=_strdup")
if (${MSVC_VERSION} GREATER 1599)
set( MSVC_LD_FLAGS "/FORCE:MULTIPLE" )
endif (${MSVC_VERSION} GREATER 1599)
endif(MSVC)
# assumed on Windows
set(HAVE_GETLOCALTIME 1)
set( WINSOCK_LIBRARY "ws2_32.lib" )
set( RT_LIBRARY "winmm" )
endif(WIN32)
@@ -370,17 +342,14 @@ 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}")
# use BEFORE to ensure local directories are used first,
# ahead of system-installed libs
include_directories(BEFORE ${PROJECT_SOURCE_DIR})
include_directories(BEFORE ${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
include_directories(BEFORE ${PROJECT_BINARY_DIR}/simgear)
include_directories(${PROJECT_SOURCE_DIR})
include_directories(${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
include_directories(${PROJECT_BINARY_DIR}/simgear)
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}
${Boost_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIR}
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}
${Boost_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIR}
${OPENAL_INCLUDE_DIR}
${CURL_INCLUDE_DIRS}
)
add_definitions(-DHAVE_CONFIG_H)
@@ -409,8 +378,7 @@ set(TEST_LIBS_INTERNAL_CORE
${WINSOCK_LIBRARY}
${RT_LIBRARY}
${DL_LIBRARY}
${COCOA_LIBRARY}
${CURL_LIBRARIES})
${CORE_SERVICES_LIBRARY})
set(TEST_LIBS SimGearCore ${TEST_LIBS_INTERNAL_CORE})
if(NOT SIMGEAR_HEADLESS)
@@ -419,45 +387,9 @@ endif()
install (FILES ${PROJECT_BINARY_DIR}/simgear/simgear_config.h DESTINATION include/simgear/)
include_directories(3rdparty/utf8/source)
add_subdirectory(3rdparty)
add_subdirectory(simgear)
#-----------------------------------------------------------------------------
### Export stuff, see https://cmake.org/cmake/help/v3.2/manual/cmake-packages.7.html#creating-packages
#-----------------------------------------------------------------------------
generate_export_header(SimGearCore)
if(NOT SIMGEAR_HEADLESS)
generate_export_header(SimGearScene)
endif()
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/SimGear/SimGearConfigVersion.cmake"
VERSION ${SIMGEAR_VERSION}
COMPATIBILITY AnyNewerVersion
)
configure_file(SimGearConfig.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/SimGear/SimGearConfig.cmake"
@ONLY
)
set(ConfigPackageLocation lib/cmake/SimGear)
install(EXPORT SimGearTargets
DESTINATION ${ConfigPackageLocation}
)
install(
FILES
"${CMAKE_CURRENT_BINARY_DIR}/SimGear/SimGearConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/SimGear/SimGearConfigVersion.cmake"
DESTINATION ${ConfigPackageLocation}
COMPONENT Devel
)
#-----------------------------------------------------------------------------
### uninstall target
#-----------------------------------------------------------------------------
@@ -467,3 +399,5 @@ CONFIGURE_FILE(
IMMEDIATE @ONLY)
ADD_CUSTOM_TARGET(uninstall
"${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")

View File

@@ -1,256 +0,0 @@
# - Add tests using boost::test
#
# Add this line to your test files in place of including a basic boost test header:
# #include <BoostTestTargetConfig.h>
#
# If you cannot do that and must use the included form for a given test,
# include the line
# // OVERRIDE_BOOST_TEST_INCLUDED_WARNING
# in the same file with the boost test include.
#
# include(BoostTestTargets)
# add_boost_test(<testdriver_name> SOURCES <source1> [<more sources...>]
# [FAIL_REGULAR_EXPRESSION <additional fail regex>]
# [LAUNCHER <generic launcher script>]
# [LIBRARIES <library> [<library>...]]
# [RESOURCES <resource> [<resource>...]]
# [TESTS <testcasename> [<testcasename>...]])
#
# If for some reason you need access to the executable target created,
# it can be found in ${${testdriver_name}_TARGET_NAME} as specified when
# you called add_boost_test
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Requires:
# GetForceIncludeDefinitions
# CopyResourcesToBuildTree
#
# Original Author:
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2009-2010.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
if(__add_boost_test)
return()
endif()
set(__add_boost_test YES)
set(BOOST_TEST_TARGET_PREFIX "test")
if(NOT Boost_FOUND)
find_package(Boost 1.34.0 QUIET)
endif()
if("${Boost_VERSION}0" LESS "1034000")
set(_shared_msg
"NOTE: boost::test-based targets and tests cannot "
"be added: boost >= 1.34.0 required but not found. "
"(found: '${Boost_VERSION}'; want >=103400) ")
if(ENABLE_TESTS)
message(FATAL_ERROR
${_shared_msg}
"You may disable ENABLE_TESTS to continue without the "
"tests.")
else()
message(STATUS
${_shared_msg}
"ENABLE_TESTS disabled, so continuing anyway.")
endif()
endif()
include(GetForceIncludeDefinitions)
include(CopyResourcesToBuildTree)
if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000")
set(_boosttesttargets_libs)
set(_boostConfig "BoostTestTargetsIncluded.h")
if(NOT Boost_UNIT_TEST_FRAMEWORK_LIBRARY)
find_package(Boost 1.34.0 QUIET COMPONENTS unit_test_framework)
endif()
if(Boost_UNIT_TEST_FRAMEWORK_LIBRARY)
set(_boosttesttargets_libs "${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}")
if(Boost_USE_STATIC_LIBS)
set(_boostConfig "BoostTestTargetsStatic.h")
else()
set(_boostConfig "BoostTestTargetsDynamic.h")
endif()
endif()
get_filename_component(_moddir ${CMAKE_CURRENT_LIST_FILE} PATH)
configure_file("${_moddir}/${_boostConfig}"
"${CMAKE_CURRENT_BINARY_DIR}/BoostTestTargetConfig.h"
COPYONLY)
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
endif()
function(add_boost_test _name)
if(NOT ENABLE_TESTS)
return()
endif()
# parse arguments
set(_nowhere)
set(_curdest _nowhere)
set(_val_args
SOURCES
FAIL_REGULAR_EXPRESSION
LAUNCHER
LIBRARIES
RESOURCES
TESTS)
set(_bool_args
USE_COMPILED_LIBRARY)
foreach(_arg ${_val_args} ${_bool_args})
set(${_arg})
endforeach()
foreach(_element ${ARGN})
list(FIND _val_args "${_element}" _val_arg_find)
list(FIND _bool_args "${_element}" _bool_arg_find)
if("${_val_arg_find}" GREATER "-1")
set(_curdest "${_element}")
elseif("${_bool_arg_find}" GREATER "-1")
set("${_element}" ON)
set(_curdest _nowhere)
else()
list(APPEND ${_curdest} "${_element}")
endif()
endforeach()
if(_nowhere)
message(FATAL_ERROR "Syntax error in use of add_boost_test!")
endif()
if(NOT SOURCES)
message(FATAL_ERROR
"Syntax error in use of add_boost_test: at least one source file required!")
endif()
if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000")
include_directories(${Boost_INCLUDE_DIRS})
set(includeType)
foreach(src ${SOURCES})
file(READ ${src} thefile)
if("${thefile}" MATCHES ".*BoostTestTargetConfig.h.*")
set(includeType CONFIGURED)
set(includeFileLoc ${src})
break()
elseif("${thefile}" MATCHES ".*boost/test/included/unit_test.hpp.*")
set(includeType INCLUDED)
set(includeFileLoc ${src})
set(_boosttesttargets_libs) # clear this out - linking would be a bad idea
if(NOT
"${thefile}"
MATCHES
".*OVERRIDE_BOOST_TEST_INCLUDED_WARNING.*")
message("Please replace the include line in ${src} with this alternate include line instead:")
message(" \#include <BoostTestTargetConfig.h>")
message("Once you've saved your changes, re-run CMake. (See BoostTestTargets.cmake for more info)")
endif()
break()
endif()
endforeach()
if(NOT _boostTestTargetsNagged${_name} STREQUAL "${includeType}")
if("includeType" STREQUAL "CONFIGURED")
message(STATUS
"Test '${_name}' uses the CMake-configurable form of the boost test framework - congrats! (Including File: ${includeFileLoc})")
elseif("${includeType}" STREQUAL "INCLUDED")
message("In test '${_name}': ${includeFileLoc} uses the 'included' form of the boost unit test framework.")
else()
message("In test '${_name}': Didn't detect the CMake-configurable boost test include.")
message("Please replace your existing boost test include in that test with the following:")
message(" \#include <BoostTestTargetConfig.h>")
message("Once you've saved your changes, re-run CMake. (See BoostTestTargets.cmake for more info)")
endif()
endif()
set(_boostTestTargetsNagged${_name}
"${includeType}"
CACHE
INTERNAL
""
FORCE)
if(RESOURCES)
list(APPEND SOURCES ${RESOURCES})
endif()
# Generate a unique target name, using the relative binary dir
# and provided name. (transform all / into _ and remove all other
# non-alphabet characters)
file(RELATIVE_PATH
targetpath
"${CMAKE_BINARY_DIR}"
"${CMAKE_CURRENT_BINARY_DIR}")
string(REGEX REPLACE "[^A-Za-z/_]" "" targetpath "${targetpath}")
string(REPLACE "/" "_" targetpath "${targetpath}")
set(_target_name ${BOOST_TEST_TARGET_PREFIX}-${targetpath}-${_name})
set(${_name}_TARGET_NAME "${_target_name}" PARENT_SCOPE)
# Build the test.
add_executable(${_target_name} ${SOURCES})
list(APPEND LIBRARIES ${_boosttesttargets_libs})
if(LIBRARIES)
target_link_libraries(${_target_name} ${LIBRARIES})
endif()
if(RESOURCES)
set_property(TARGET ${_target_name} PROPERTY RESOURCE ${RESOURCES})
copy_resources_to_build_tree(${_target_name})
endif()
if(NOT Boost_TEST_FLAGS)
# set(Boost_TEST_FLAGS --catch_system_error=yes --output_format=XML)
set(Boost_TEST_FLAGS --catch_system_error=yes)
endif()
# TODO: Figure out why only recent boost handles individual test running properly
if(LAUNCHER)
set(_test_command ${LAUNCHER} "\$<TARGET_FILE:${_target_name}>")
else()
set(_test_command ${_target_name})
endif()
if(TESTS AND ( "${Boost_VERSION}" VERSION_GREATER "103799" ))
foreach(_test ${TESTS})
add_test(
${_name}-${_test}
${_test_command} --run_test=${_test} ${Boost_TEST_FLAGS}
)
if(FAIL_REGULAR_EXPRESSION)
set_tests_properties(${_name}-${_test}
PROPERTIES
FAIL_REGULAR_EXPRESSION
"${FAIL_REGULAR_EXPRESSION}")
endif()
endforeach()
else()
add_test(
${_name}-boost_test
${_test_command} ${Boost_TEST_FLAGS}
)
if(FAIL_REGULAR_EXPRESSION)
set_tests_properties(${_name}-boost_test
PROPERTIES
FAIL_REGULAR_EXPRESSION
"${FAIL_REGULAR_EXPRESSION}")
endif()
endif()
# CppCheck the test if we can.
if(COMMAND add_cppcheck)
add_cppcheck(${_target_name} STYLE UNUSED_FUNCTIONS)
endif()
endif()
endfunction()

View File

@@ -1,8 +0,0 @@
// Small header computed by CMake to set up boost test.
// include AFTER #define BOOST_TEST_MODULE whatever
// but before any other boost test includes.
// Using the Boost UTF dynamic library
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>

View File

@@ -1,7 +0,0 @@
// Small header computed by CMake to set up boost test.
// include AFTER #define BOOST_TEST_MODULE whatever
// but before any other boost test includes.
// Using the Boost UTF included framework
#include <boost/test/included/unit_test.hpp>

View File

@@ -1,7 +0,0 @@
// Small header computed by CMake to set up boost test.
// include AFTER #define BOOST_TEST_MODULE whatever
// but before any other boost test includes.
// Using the Boost UTF static library
#include <boost/test/unit_test.hpp>

View File

@@ -1,83 +0,0 @@
# - Copy the resources your app needs to the build tree.
#
# copy_resources_to_build_tree(<target_name>)
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2009-2010.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
if(__copy_resources_to_build_tree)
return()
endif()
set(__copy_resources_to_build_tree YES)
function(copy_resources_to_build_tree _target)
get_target_property(_resources ${_target} RESOURCE)
if(NOT _resources)
# Bail if no resources
message(STATUS
"Told to copy resources for target ${_target}, but "
"no resources are set!")
return()
endif()
get_target_property(_path ${_target} LOCATION)
get_filename_component(_path "${_path}" PATH)
if(NOT MSVC AND NOT "${CMAKE_GENERATOR}" MATCHES "Makefiles")
foreach(_config ${CMAKE_CONFIGURATION_TYPES})
get_target_property(_path${_config} ${_target} LOCATION_${_config})
get_filename_component(_path${_config} "${_path${_config}}" PATH)
add_custom_command(TARGET ${_target}
POST_BUILD
COMMAND
${CMAKE_COMMAND}
ARGS -E make_directory "${_path${_config}}/"
COMMENT "Creating directory ${_path${_config}}/")
endforeach()
endif()
foreach(_res ${_resources})
if(NOT IS_ABSOLUTE "${_res}")
get_filename_component(_res "${_res}" ABSOLUTE)
endif()
get_filename_component(_name "${_res}" NAME)
if(MSVC)
# Working dir is solution file dir, not exe file dir.
add_custom_command(TARGET ${_target}
POST_BUILD
COMMAND
${CMAKE_COMMAND}
ARGS -E copy "${_res}" "${CMAKE_BINARY_DIR}/"
COMMENT "Copying ${_name} to ${CMAKE_BINARY_DIR}/ for MSVC")
else()
if("${CMAKE_GENERATOR}" MATCHES "Makefiles")
add_custom_command(TARGET ${_target}
POST_BUILD
COMMAND
${CMAKE_COMMAND}
ARGS -E copy "${_res}" "${_path}/"
COMMENT "Copying ${_name} to ${_path}/")
else()
foreach(_config ${CMAKE_CONFIGURATION_TYPES})
add_custom_command(TARGET ${_target}
POST_BUILD
COMMAND
${CMAKE_COMMAND}
ARGS -E copy "${_res}" "${_path${_config}}"
COMMENT "Copying ${_name} to ${_path${_config}}")
endforeach()
endif()
endif()
endforeach()
endfunction()

View File

@@ -1,44 +0,0 @@
# - Get the platform-appropriate flags to add to force inclusion of a file
#
# The most common use of this is to use a generated config.h-type file
# placed out of the source tree in all files.
#
# get_force_include_definitions(var forcedincludefiles...) -
# where var is the name of your desired output variable, and everything
# else is a source file to forcibly include.
# a list item to be filtered.
#
# Original Author:
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2009-2010.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
if(__get_force_include_definitions)
return()
endif()
set(__get_force_include_definitions YES)
function(get_force_include_definitions var)
set(_flagprefix)
if(CMAKE_COMPILER_IS_GNUCXX)
set(_flag "-include")
elseif(MSVC)
set(_flag "/FI")
else()
message(SEND_ERROR "You don't seem to be using MSVC or GCC, but")
message(SEND_ERROR "the project called get_force_include_definitions.")
message(SEND_ERROR "Contact this project with the name of your")
message(FATAL_ERROR "compiler and preferably the flag to force includes")
endif()
set(_out)
foreach(_item ${ARGN})
list(APPEND _out "${_flag} \"${_item}\"")
endforeach()
set(${var} "${_out}" PARENT_SCOPE)
endfunction()

View File

@@ -0,0 +1,182 @@
# - Define GNU standard installation directories
# Provides install directory variables as defined for GNU software:
# http://www.gnu.org/prep/standards/html_node/Directory-Variables.html
# Inclusion of this module defines the following variables:
# CMAKE_INSTALL_<dir> - destination for files of a given type
# CMAKE_INSTALL_FULL_<dir> - corresponding absolute path
# where <dir> is one of:
# BINDIR - user executables (bin)
# SBINDIR - system admin executables (sbin)
# LIBEXECDIR - program executables (libexec)
# SYSCONFDIR - read-only single-machine data (etc)
# SHAREDSTATEDIR - modifiable architecture-independent data (com)
# LOCALSTATEDIR - modifiable single-machine data (var)
# LIBDIR - object code libraries (lib or lib64)
# INCLUDEDIR - C header files (include)
# OLDINCLUDEDIR - C header files for non-gcc (/usr/include)
# DATAROOTDIR - read-only architecture-independent data root (share)
# DATADIR - read-only architecture-independent data (DATAROOTDIR)
# INFODIR - info documentation (DATAROOTDIR/info)
# LOCALEDIR - locale-dependent data (DATAROOTDIR/locale)
# MANDIR - man documentation (DATAROOTDIR/man)
# DOCDIR - documentation root (DATAROOTDIR/doc/PROJECT_NAME)
# Each CMAKE_INSTALL_<dir> value may be passed to the DESTINATION options of
# install() commands for the corresponding file type. If the includer does
# not define a value the above-shown default will be used and the value will
# appear in the cache for editing by the user.
# Each CMAKE_INSTALL_FULL_<dir> value contains an absolute path constructed
# from the corresponding destination by prepending (if necessary) the value
# of CMAKE_INSTALL_PREFIX.
#=============================================================================
# Copyright 2011 Nikita Krupen'ko <krnekit@gmail.com>
# Copyright 2011 Kitware, Inc.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
# Installation directories
#
if(NOT DEFINED CMAKE_INSTALL_BINDIR)
set(CMAKE_INSTALL_BINDIR "bin" CACHE PATH "user executables (bin)")
endif()
if(NOT DEFINED CMAKE_INSTALL_SBINDIR)
set(CMAKE_INSTALL_SBINDIR "sbin" CACHE PATH "system admin executables (sbin)")
endif()
if(NOT DEFINED CMAKE_INSTALL_LIBEXECDIR)
set(CMAKE_INSTALL_LIBEXECDIR "libexec" CACHE PATH "program executables (libexec)")
endif()
if(NOT DEFINED CMAKE_INSTALL_SYSCONFDIR)
set(CMAKE_INSTALL_SYSCONFDIR "etc" CACHE PATH "read-only single-machine data (etc)")
endif()
if(NOT DEFINED CMAKE_INSTALL_SHAREDSTATEDIR)
set(CMAKE_INSTALL_SHAREDSTATEDIR "com" CACHE PATH "modifiable architecture-independent data (com)")
endif()
if(NOT DEFINED CMAKE_INSTALL_LOCALSTATEDIR)
set(CMAKE_INSTALL_LOCALSTATEDIR "var" CACHE PATH "modifiable single-machine data (var)")
endif()
if(NOT DEFINED CMAKE_INSTALL_LIBDIR)
set(_LIBDIR_DEFAULT "lib")
# Override this default 'lib' with 'lib64' iff:
# - we are on Linux system but NOT cross-compiling
# - we are NOT on debian
# - we are on a 64 bits system
# reason is: amd64 ABI: http://www.x86-64.org/documentation/abi.pdf
# Note that the future of multi-arch handling may be even
# more complicated than that: http://wiki.debian.org/Multiarch
if(CMAKE_SYSTEM_NAME MATCHES "Linux"
AND NOT CMAKE_CROSSCOMPILING
AND NOT EXISTS "/etc/debian_version")
if(NOT DEFINED CMAKE_SIZEOF_VOID_P)
message(AUTHOR_WARNING
"Unable to determine default CMAKE_INSTALL_LIBDIR directory because no target architecture is known. "
"Please enable at least one language before including GNUInstallDirs.")
else()
if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
set(_LIBDIR_DEFAULT "lib64")
endif()
endif()
endif()
set(CMAKE_INSTALL_LIBDIR "${_LIBDIR_DEFAULT}" CACHE PATH "object code libraries (${_LIBDIR_DEFAULT})")
endif()
if(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR)
set(CMAKE_INSTALL_INCLUDEDIR "include" CACHE PATH "C header files (include)")
endif()
if(NOT DEFINED CMAKE_INSTALL_OLDINCLUDEDIR)
set(CMAKE_INSTALL_OLDINCLUDEDIR "/usr/include" CACHE PATH "C header files for non-gcc (/usr/include)")
endif()
if(NOT DEFINED CMAKE_INSTALL_DATAROOTDIR)
set(CMAKE_INSTALL_DATAROOTDIR "share" CACHE PATH "read-only architecture-independent data root (share)")
endif()
#-----------------------------------------------------------------------------
# Values whose defaults are relative to DATAROOTDIR. Store empty values in
# the cache and store the defaults in local variables if the cache values are
# not set explicitly. This auto-updates the defaults as DATAROOTDIR changes.
if(NOT CMAKE_INSTALL_DATADIR)
set(CMAKE_INSTALL_DATADIR "" CACHE PATH "read-only architecture-independent data (DATAROOTDIR)")
set(CMAKE_INSTALL_DATADIR "${CMAKE_INSTALL_DATAROOTDIR}")
endif()
if(NOT CMAKE_INSTALL_INFODIR)
set(CMAKE_INSTALL_INFODIR "" CACHE PATH "info documentation (DATAROOTDIR/info)")
set(CMAKE_INSTALL_INFODIR "${CMAKE_INSTALL_DATAROOTDIR}/info")
endif()
if(NOT CMAKE_INSTALL_LOCALEDIR)
set(CMAKE_INSTALL_LOCALEDIR "" CACHE PATH "locale-dependent data (DATAROOTDIR/locale)")
set(CMAKE_INSTALL_LOCALEDIR "${CMAKE_INSTALL_DATAROOTDIR}/locale")
endif()
if(NOT CMAKE_INSTALL_MANDIR)
set(CMAKE_INSTALL_MANDIR "" CACHE PATH "man documentation (DATAROOTDIR/man)")
set(CMAKE_INSTALL_MANDIR "${CMAKE_INSTALL_DATAROOTDIR}/man")
endif()
if(NOT CMAKE_INSTALL_DOCDIR)
set(CMAKE_INSTALL_DOCDIR "" CACHE PATH "documentation root (DATAROOTDIR/doc/PROJECT_NAME)")
set(CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME}")
endif()
#-----------------------------------------------------------------------------
mark_as_advanced(
CMAKE_INSTALL_BINDIR
CMAKE_INSTALL_SBINDIR
CMAKE_INSTALL_LIBEXECDIR
CMAKE_INSTALL_SYSCONFDIR
CMAKE_INSTALL_SHAREDSTATEDIR
CMAKE_INSTALL_LOCALSTATEDIR
CMAKE_INSTALL_LIBDIR
CMAKE_INSTALL_INCLUDEDIR
CMAKE_INSTALL_OLDINCLUDEDIR
CMAKE_INSTALL_DATAROOTDIR
CMAKE_INSTALL_DATADIR
CMAKE_INSTALL_INFODIR
CMAKE_INSTALL_LOCALEDIR
CMAKE_INSTALL_MANDIR
CMAKE_INSTALL_DOCDIR
)
# Result directories
#
foreach(dir
BINDIR
SBINDIR
LIBEXECDIR
SYSCONFDIR
SHAREDSTATEDIR
LOCALSTATEDIR
LIBDIR
INCLUDEDIR
OLDINCLUDEDIR
DATAROOTDIR
DATADIR
INFODIR
LOCALEDIR
MANDIR
DOCDIR
)
if(NOT IS_ABSOLUTE ${CMAKE_INSTALL_${dir}})
set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_${dir}}")
else()
set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_${dir}}")
endif()
endforeach()

2657
Doxyfile

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +0,0 @@
include(CMakeFindDependencyMacro)
find_dependency(ZLIB)
find_dependency(Threads)
# OSG
set(SIMGEAR_HEADLESS @SIMGEAR_HEADLESS@)
set(SIMGEAR_SOUND @ENABLE_SOUND@)
# OpenAL isn't a public dependency, so maybe not needed
#if (SIMGEAR_SOUND)
# find_dependency(OpenAL)
#endif()
include("${CMAKE_CURRENT_LIST_DIR}/SimGearTargets.cmake")

View File

@@ -1,7 +1,7 @@
file(WRITE ${PROJECT_BINARY_DIR}/simgear/version.h "#define SIMGEAR_VERSION ${SIMGEAR_VERSION}")
foreach( mylibfolder
foreach( mylibfolder
bucket
bvh
debug
@@ -54,11 +54,9 @@ if(SIMGEAR_SHARED)
set_property(TARGET SimGearCore PROPERTY LINKER_LANGUAGE CXX)
set_property(TARGET SimGearCore PROPERTY VERSION ${SIMGEAR_VERSION})
set_property(TARGET SimGearCore PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
install(TARGETS SimGearCore
EXPORT SimGearTargets
LIBRARY DESTINATION
${CMAKE_INSTALL_LIBDIR})
install(TARGETS SimGearCore EXPORT SimGearCoreConfig LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(EXPORT SimGearCoreConfig DESTINATION share/SimGearCore)
if(NOT SIMGEAR_HEADLESS)
add_library(SimGearScene SHARED ${sceneSources})
set_property(TARGET SimGearScene PROPERTY LINKER_LANGUAGE CXX)
@@ -66,12 +64,10 @@ if(SIMGEAR_SHARED)
set_property(TARGET SimGearScene PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
# EXPORT SimGearSceneConfig
install(TARGETS SimGearScene
EXPORT SimGearTargets
LIBRARY
DESTINATION ${CMAKE_INSTALL_LIBDIR} )
install(TARGETS SimGearScene LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} )
# install(EXPORT SimGearSceneConfig DESTINATION share/SimGearScene)
endif()
else()
message(STATUS "Library building mode: STATIC LIBRARIES")
@@ -94,10 +90,8 @@ else()
endforeach()
add_library(SimGearCore STATIC ${coreSources} ${localExpatSources})
install(TARGETS SimGearCore
EXPORT SimGearTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(TARGETS SimGearCore ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
if(NOT SIMGEAR_HEADLESS)
get_property(FG_GROUPS_SCENE_SOURCES_C GLOBAL PROPERTY FG_GROUPS_SCENE_SOURCES_C)
string(REPLACE "@" ";" groups ${FG_GROUPS_SCENE_SOURCES_C} )
@@ -118,9 +112,7 @@ else()
endforeach()
add_library(SimGearScene STATIC ${sceneSources})
install(TARGETS SimGearScene
EXPORT SimGearTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(TARGETS SimGearScene ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif(NOT SIMGEAR_HEADLESS)
endif(SIMGEAR_SHARED)
@@ -130,8 +122,7 @@ target_link_libraries(SimGearCore
${DL_LIBRARY}
${EXPAT_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
${COCOA_LIBRARY}
${CURL_LIBRARIES})
${CORE_SERVICES_LIBRARY})
if(NOT SIMGEAR_HEADLESS)
target_link_libraries(SimGearScene

View File

@@ -4,13 +4,4 @@ include (SimGearComponent)
set(HEADERS newbucket.hxx)
set(SOURCES newbucket.cxx)
simgear_component(bucket bucket "${SOURCES}" "${HEADERS}")
if(ENABLE_TESTS)
add_executable(test_bucket test_bucket.cxx)
add_test(test_bucket ${EXECUTABLE_OUTPUT_PATH}/test_bucket)
target_link_libraries(test_bucket ${TEST_LIBS})
endif(ENABLE_TESTS)
simgear_component(bucket bucket "${SOURCES}" "${HEADERS}")

View File

@@ -27,56 +27,36 @@
# include <simgear_config.h>
#endif
#include <cmath>
#include <cstdio> // some platforms need this for ::snprintf
#include <iostream>
#include <math.h>
#include <simgear/misc/sg_path.hxx>
#include <simgear/debug/logstream.hxx>
#include "newbucket.hxx"
// default constructor
SGBucket::SGBucket() :
lon(-1000),
lat(-1000),
x(0),
y(0)
{
SGBucket::SGBucket() {
}
bool SGBucket::isValid() const
{
// The most northerly valid latitude is 89, not 90. There is no tile
// whose *bottom* latitude is 90. Similar there is no tile whose left egde
// is 180 longitude.
return (lon >= -180) &&
(lon < 180) &&
(lat >= -90) &&
(lat < 90) &&
(x < 8) && (y < 8);
}
void SGBucket::make_bad()
{
lon = -1000;
lat = -1000;
}
#ifndef NO_DEPRECATED_API
// constructor for specified location
SGBucket::SGBucket(const double dlon, const double dlat) {
set_bucket(dlon, dlat);
}
#endif
SGBucket::SGBucket(const SGGeod& geod) {
innerSet(geod.getLongitudeDeg(),
geod.getLatitudeDeg());
set_bucket(geod);
}
// create an impossible bucket if false
SGBucket::SGBucket(const bool is_good) {
set_bucket(0.0, 0.0);
if ( !is_good ) {
lon = -1000;
}
}
// Parse a unique scenery tile index and find the lon, lat, x, and y
SGBucket::SGBucket(const long int bindex) {
long int index = bindex;
@@ -95,59 +75,48 @@ SGBucket::SGBucket(const long int bindex) {
x = index;
}
/* Calculate the greatest integral value less than
* or equal to the given value (floor(x)),
* but attribute coordinates close to the boundary to the next
* (increasing) integral
*/
static int floorWithEpsilon(double x)
{
return static_cast<int>(floor(x + SG_EPSILON));
}
#ifndef NO_DEPRECATED_API
void SGBucket::set_bucket(double dlon, double dlat)
{
innerSet(dlon, dlat);
}
void SGBucket::set_bucket(const SGGeod& geod)
{
innerSet(geod.getLongitudeDeg(), geod.getLatitudeDeg());
}
#endif
// Set the bucket params for the specified lat and lon
void SGBucket::innerSet( double dlon, double dlat )
{
if ((dlon < -180.0) || (dlon >= 180.0)) {
SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::set_bucket: passed longitude:" << dlon);
dlon = SGMiscd::normalizePeriodic(-180.0, 180.0, dlon);
}
if ((dlat < -90.0) || (dlat > 90.0)) {
SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::set_bucket: passed latitude" << dlat);
dlat = SGMiscd::clip(dlat, -90.0, 90.0);
}
void SGBucket::set_bucket( double *lonlat ) {
set_bucket( lonlat[0], lonlat[1] );
}
// Set the bucket params for the specified lat and lon
void SGBucket::set_bucket( double dlon, double dlat ) {
//
// longitude first
// latitude first
//
double span = sg_bucket_span( dlat );
// we do NOT need to special case lon=180 here, since
// normalizePeriodic will never return 180; it will
// return -180, which is what we want.
lon = floorWithEpsilon(dlon);
double diff = dlon - (double)(int)dlon;
// cout << "diff = " << diff << " span = " << span << endl;
/* Calculate the greatest integral longitude less than
* or equal to the given longitude (floor(dlon)),
* but attribute coordinates near the east border
* to the next tile.
*/
if ( (dlon >= 0) || (fabs(diff) < SG_EPSILON) ) {
lon = (int)dlon;
} else {
lon = (int)dlon - 1;
}
// find subdivision or super lon if needed
if ( span <= 1.0 ) {
if ( span < SG_EPSILON ) {
/* sg_bucket_span() never returns 0.0
* or anything near it, so this really
* should not occur at any time.
*/
// polar cap
lon = 0;
x = 0;
} else if ( span <= 1.0 ) {
/* We have more than one tile per degree of
* longitude, so we need an x offset.
*/
x = floorWithEpsilon((dlon - lon) / span);
x = (int)((dlon - lon) / span);
} else {
/* We have one or more degrees per tile,
* so we need to find the base longitude
@@ -160,28 +129,48 @@ void SGBucket::innerSet( double dlon, double dlat )
*
* That way, the Greenwich Meridian is always
* a tile border.
*
* This gets us into trouble with the polar caps,
* which have width 360 and thus either span
* the range from 0 to 360 or from -360 to 0
* degrees, depending on whether lon is positive
* or negative!
*
* We also get into trouble with the 8 degree tiles
* north of 88N and south of 88S, because the west-
* and east-most tiles in that range will cover 184W
* to 176W and 176E to 184E respectively, with their
* center at 180E/W!
*/
lon=static_cast<int>(floor(lon / span) * span);
x = 0;
lon=(int)floor(floor((lon+SG_EPSILON)/span)*span);
/* Correct the polar cap issue */
if ( lon < -180 ) {
lon = -180;
}
x = 0;
}
//
// then latitude
//
lat = floorWithEpsilon(dlat);
// special case when passing in the north pole point (possibly due to
// clipping latitude above). Ensures we generate a valid bucket in this
// scenario
if (lat == 90) {
lat = 89;
y = 7;
diff = dlat - (double)(int)dlat;
/* Again, a modified floor() function (see longitude) */
if ( (dlat >= 0) || (fabs(diff) < SG_EPSILON) ) {
lat = (int)dlat;
} else {
/* Latitude base and offset are easier, as
* tiles always are 1/8 degree of latitude wide.
*/
y = floorWithEpsilon((dlat - lat) * 8);
lat = (int)dlat - 1;
}
/* Latitude base and offset are easier, as
* tiles always are 1/8 degree of latitude wide.
*/
y = (int)((dlat - lat) * 8);
}
void SGBucket::set_bucket(const SGGeod& geod)
{
set_bucket(geod.getLongitudeDeg(), geod.getLatitudeDeg());
}
// Build the path name for this bucket
@@ -223,7 +212,7 @@ std::string SGBucket::gen_base_path() const {
main_lat *= -1;
}
::snprintf(raw_path, 256, "%c%03d%c%02d/%c%03d%c%02d",
snprintf(raw_path, 256, "%c%03d%c%02d/%c%03d%c%02d",
hem, top_lon, pole, top_lat,
hem, main_lon, pole, main_lat);
@@ -244,32 +233,17 @@ double SGBucket::get_height() const {
return SG_BUCKET_SPAN;
}
double SGBucket::get_highest_lat() const
{
unsigned char adjustedY = y;
if (lat >= 0) {
// tile is north of the equator, so we want the top edge. Add one
// to y to achieve this.
++adjustedY;
// return width of the tile in meters
double SGBucket::get_width_m() const {
double clat = (int)get_center_lat();
if ( clat > 0 ) {
clat = (int)clat + 0.5;
} else {
clat = (int)clat - 0.5;
}
return lat + (adjustedY / 8.0);
}
// return width of the tile in meters. This function is used by the
// tile-manager to estimate how many tiles are in the view distance, so
// we care about the smallest width, which occurs at the highest latitude.
double SGBucket::get_width_m() const
{
double clat_rad = get_highest_lat() * SGD_DEGREES_TO_RADIANS;
double clat_rad = clat * SGD_DEGREES_TO_RADIANS;
double cos_lat = cos( clat_rad );
if (fabs(cos_lat) < SG_EPSILON) {
// happens for polar tiles, since we pass in a latitude of 90
// return an arbitrary small value so all tiles are loaded
return 10.0;
}
double local_radius = cos_lat * SG_EQUATORIAL_RADIUS_M;
double local_perimeter = local_radius * SGD_2PI;
double degree_width = local_perimeter / 360.0;
@@ -286,41 +260,7 @@ double SGBucket::get_height_m() const {
return SG_BUCKET_SPAN * degree_height;
}
SGBucket SGBucket::sibling(int dx, int dy) const
{
if (!isValid()) {
SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::sibling: requesting sibling of invalid bucket");
return SGBucket();
}
double clat = get_center_lat() + dy * SG_BUCKET_SPAN;
// return invalid here instead of clipping, so callers can discard
// invalid buckets without having to check if it's an existing one
if ((clat < -90.0) || (clat > 90.0)) {
return SGBucket();
}
// find the lon span for the new latitude
double span = sg_bucket_span( clat );
double tmp = get_center_lon() + dx * span;
tmp = SGMiscd::normalizePeriodic(-180.0, 180.0, tmp);
SGBucket b;
b.innerSet(tmp, clat);
return b;
}
std::string SGBucket::gen_index_str() const
{
char tmp[20];
::snprintf(tmp, 20, "%ld",
(((long)lon + 180) << 14) + ((lat + 90) << 6)
+ (y << 3) + x);
return (std::string)tmp;
}
#ifndef NO_DEPRECATED_API
// find the bucket which is offset by the specified tile units in the
// X & Y direction. We need the current lon and lat to resolve
// ambiguities when going from a wider tile to a narrower one above or
@@ -347,7 +287,7 @@ SGBucket sgBucketOffset( double dlon, double dlat, int dx, int dy ) {
return result;
}
#endif
// calculate the offset between two buckets
void sgBucketDiff( const SGBucket& b1, const SGBucket& b2, int *dx, int *dy ) {
@@ -408,22 +348,10 @@ void sgBucketDiff( const SGBucket& b1, const SGBucket& b2, int *dx, int *dy ) {
void sgGetBuckets( const SGGeod& min, const SGGeod& max, std::vector<SGBucket>& list ) {
double lon, lat, span;
for (lat = min.getLatitudeDeg(); lat < max.getLatitudeDeg()+SG_BUCKET_SPAN; lat += SG_BUCKET_SPAN) {
for (lat = min.getLatitudeDeg(); lat <= max.getLatitudeDeg(); lat += SG_BUCKET_SPAN) {
span = sg_bucket_span( lat );
for (lon = min.getLongitudeDeg(); lon <= max.getLongitudeDeg(); lon += span)
{
SGBucket b(SGGeod::fromDeg(lon, lat));
if (!b.isValid()) {
continue;
}
list.push_back(b);
for (lon = min.getLongitudeDeg(); lon <= max.getLongitudeDeg(); lon += span) {
list.push_back( SGBucket(lon , lat) );
}
}
}
std::ostream& operator<< ( std::ostream& out, const SGBucket& b )
{
return out << b.lon << ":" << (int)b.x << ", " << b.lat << ":" << (int)b.y;
}

View File

@@ -39,12 +39,11 @@
#include <simgear/math/SGMath.hxx>
#include <cmath>
#include <cstdio> // sprintf()
#include <ostream>
#include <string>
#include <iosfwd>
#include <vector>
// #define NO_DEPRECATED_API
/**
* standard size of a bucket in degrees (1/8 of a degree)
*/
@@ -100,69 +99,75 @@ class SGBucket {
private:
short lon; // longitude index (-180 to 179)
short lat; // latitude index (-90 to 89)
unsigned char x; // x subdivision (0 to 7)
unsigned char y; // y subdivision (0 to 7)
char x; // x subdivision (0 to 7)
char y; // y subdivision (0 to 7)
void innerSet( double dlon, double dlat );
public:
/**
* Default constructor, creates an invalid SGBucket
* Default constructor.
*/
SGBucket();
/**
* Check if this bucket refers to a valid tile, or not.
*/
bool isValid() const;
#ifndef NO_DEPRECATED_API
/**
* Construct a bucket given a specific location.
* @param dlon longitude specified in degrees
* @param dlat latitude specified in degrees
*/
SGBucket(const double dlon, const double dlat);
#endif
/**
* Construct a bucket given a specific location.
*
* @param geod Geodetic location
* @param dlon longitude specified in degrees
* @param dlat latitude specified in degrees
*/
SGBucket(const SGGeod& geod);
/** Construct a bucket.
* @param is_good if false, create an invalid bucket. This is
* useful * if you are comparing cur_bucket to last_bucket and
* you want to * make sure last_bucket starts out as something
* impossible.
*/
SGBucket(const bool is_good);
/** Construct a bucket given a unique bucket index number.
*
* @param bindex unique bucket index
*/
SGBucket(const long int bindex);
#ifndef NO_DEPRECATED_API
/**
* Reset a bucket to represent a new location.
*
* @param geod New geodetic location
*/
void set_bucket(const SGGeod& geod);
/**
* Reset a bucket to represent a new lat and lon
* @param dlon longitude specified in degrees
* @param dlat latitude specified in degrees
*/
void set_bucket( double dlon, double dlat );
#endif
/**
* Reset a bucket to represent a new lat and lon
* @param lonlat an array of double[2] holding lon and lat
* (specified) in degrees
*/
void set_bucket( double *lonlat );
/**
* Reset a bucket to represent a new lat and lon
* @param dlon longitude specified in degrees
* @param dlat latitude specified in degrees
*/
void set_bucket(const SGGeod& geod);
/**
* Create an impossible bucket.
* This is useful if you are comparing cur_bucket to last_bucket
* and you want to make sure last_bucket starts out as something
* impossible.
*/
void make_bad();
inline void make_bad() {
set_bucket(0.0, 0.0);
lon = -1000;
}
/**
* Generate the unique scenery tile index for this bucket
*
@@ -187,8 +192,14 @@ public:
* string form.
* @return tile index in string form
*/
std::string gen_index_str() const;
inline std::string gen_index_str() const {
char tmp[20];
std::sprintf(tmp, "%ld",
(((long)lon + 180) << 14) + ((lat + 90) << 6)
+ (y << 3) + x);
return (std::string)tmp;
}
/**
* Build the base path name for this bucket.
* @return base path in string form
@@ -215,13 +226,6 @@ public:
return lat + y / 8.0 + SG_HALF_BUCKET_SPAN;
}
/**
* @return the highest (furthest from the equator) latitude of this
* tile. This is the top edge for tiles north of the equator, and
* the bottom edge for tiles south
*/
double get_highest_lat() const;
/**
* @return the width of the tile in degrees.
*/
@@ -283,11 +287,6 @@ public:
*/
inline int get_y() const { return y; }
/**
* @return bucket offset from this by dx,dy
*/
SGBucket sibling(int dx, int dy) const;
// friends
friend std::ostream& operator<< ( std::ostream&, const SGBucket& );
@@ -299,7 +298,7 @@ inline bool operator!= (const SGBucket& lhs, const SGBucket& rhs)
return !(lhs == rhs);
}
#ifndef NO_DEPRECATED_API
/**
* \relates SGBucket
* Return the bucket which is offset from the specified dlon, dlat by
@@ -311,7 +310,6 @@ inline bool operator!= (const SGBucket& lhs, const SGBucket& rhs)
* @return offset bucket
*/
SGBucket sgBucketOffset( double dlon, double dlat, int x, int y );
#endif
/**
@@ -339,7 +337,12 @@ void sgGetBuckets( const SGGeod& min, const SGGeod& max, std::vector<SGBucket>&
* @param out output stream
* @param b bucket
*/
std::ostream& operator<< ( std::ostream& out, const SGBucket& b );
inline std::ostream&
operator<< ( std::ostream& out, const SGBucket& b )
{
return out << b.lon << ":" << (int)b.x << ", " << b.lat << ":" << (int)b.y;
}
/**
* Compare two bucket structures for equality.

View File

@@ -1,283 +0,0 @@
/**************************************************************************
* test_bucket.cxx -- unit-tests for SGBucket class
*
* Copyright (C) 2014 James Turner - <zakalawe@mac.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Id$
**************************************************************************/
#include <simgear/compiler.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
using std::cout;
using std::cerr;
using std::endl;
#include <simgear/bucket/newbucket.hxx>
#include <simgear/misc/test_macros.hxx>
void testBucketSpans()
{
COMPARE(sg_bucket_span(0.0), 0.125);
COMPARE(sg_bucket_span(-20), 0.125);
COMPARE(sg_bucket_span(-40), 0.25);
COMPARE(sg_bucket_span(89.9), 12.0);
COMPARE(sg_bucket_span(88.1), 4.0);
COMPARE(sg_bucket_span(-89.9), 12.0);
}
void testBasic()
{
SGBucket b1(5.1, 55.05);
COMPARE(b1.get_chunk_lon(), 5);
COMPARE(b1.get_chunk_lat(), 55);
COMPARE(b1.get_x(), 0);
COMPARE(b1.get_y(), 0);
COMPARE(b1.gen_index(), 3040320);
COMPARE(b1.gen_base_path(), "e000n50/e005n55");
VERIFY(b1.isValid());
SGBucket b2(-10.1, -43.8);
COMPARE(b2.get_chunk_lon(), -11);
COMPARE(b2.get_chunk_lat(), -44);
COMPARE(b2.get_x(), 3);
COMPARE(b2.get_y(), 1); // latitude chunks numbered bottom to top, it seems
COMPARE(b2.gen_base_path(), "w020s50/w011s44");
VERIFY(b2.isValid());
SGBucket b3(123.48, 9.01);
COMPARE(b3.get_chunk_lon(), 123);
COMPARE(b3.get_chunk_lat(), 9);
COMPARE(b3.get_x(), 3);
COMPARE(b3.get_y(), 0);
COMPARE(b3.gen_base_path(), "e120n00/e123n09");
VERIFY(b3.isValid());
SGBucket defBuck;
VERIFY(!defBuck.isValid());
b3.make_bad();
VERIFY(!b3.isValid());
SGBucket atAntiMeridian(180.0, 12.3);
VERIFY(atAntiMeridian.isValid());
COMPARE(atAntiMeridian.get_chunk_lon(), -180);
COMPARE(atAntiMeridian.get_x(), 0);
SGBucket atAntiMeridian2(-180.0, -78.1);
VERIFY(atAntiMeridian2.isValid());
COMPARE(atAntiMeridian2.get_chunk_lon(), -180);
COMPARE(atAntiMeridian2.get_x(), 0);
// check comparisom operator overload
SGBucket b4(5.11, 55.1);
VERIFY(b1 == b4); // should be equal
VERIFY(b1 == b1);
VERIFY(b1 != defBuck);
VERIFY(b1 != b2);
// check wrapping/clipping of inputs
SGBucket wrapMeridian(-200.0, 45.0);
COMPARE(wrapMeridian.get_chunk_lon(), 160);
SGBucket clipPole(48.9, 91);
COMPARE(clipPole.get_chunk_lat(), 89);
}
void testPolar()
{
SGBucket b1(0.0, 89.92);
SGBucket b2(10.0, 89.96);
COMPARE(b1.get_chunk_lat(), 89);
COMPARE(b1.get_chunk_lon(), 0);
COMPARE(b1.get_x(), 0);
COMPARE(b1.get_y(), 7);
COMPARE_EP(b1.get_highest_lat(), 90.0);
COMPARE_EP(b1.get_width_m(), 10.0);
COMPARE(b2.get_chunk_lat(), 89);
COMPARE(b2.get_chunk_lon(), 0);
COMPARE(b2.get_x(), 0);
COMPARE(b2.get_y(), 7);
COMPARE(b1.gen_index(), b2.gen_index());
SGGeod actualNorthPole1 = b1.get_corner(2);
SGGeod actualNorthPole2 = b1.get_corner(3);
COMPARE_EP(actualNorthPole1.getLatitudeDeg(), 90.0);
COMPARE_EP(actualNorthPole1.getLongitudeDeg(), 12.0);
COMPARE_EP(actualNorthPole2.getLatitudeDeg(), 90.0);
COMPARE_EP(actualNorthPole2.getLongitudeDeg(), 0.0);
SGBucket b3(-2, 89.88);
SGBucket b4(-7, 89.88);
COMPARE(b3.gen_index(), b4.gen_index());
// south pole
SGBucket b5(-170, -89.88);
SGBucket b6(-179, -89.88);
COMPARE(b5.get_chunk_lat(), -90);
COMPARE(b5.get_chunk_lon(), -180);
COMPARE(b5.get_x(), 0);
COMPARE(b5.get_y(), 0);
COMPARE(b5.gen_index(), b6.gen_index());
COMPARE_EP(b5.get_highest_lat(), -90.0);
COMPARE_EP(b5.get_width_m(), 10.0);
SGGeod actualSouthPole1 = b5.get_corner(0);
SGGeod actualSouthPole2 = b5.get_corner(1);
COMPARE_EP(actualSouthPole1.getLatitudeDeg(), -90.0);
COMPARE_EP(actualSouthPole1.getLongitudeDeg(), -180);
COMPARE_EP(actualSouthPole2.getLatitudeDeg(), -90.0);
COMPARE_EP(actualSouthPole2.getLongitudeDeg(), -168);
SGBucket b7(200, 89.88);
COMPARE(b7.get_chunk_lon(), -168);
}
// test the tiles just below the pole (between 86 & 89 degrees N/S)
void testNearPolar()
{
SGBucket b1(1, 88.5);
SGBucket b2(-1, 88.8);
COMPARE(b1.get_chunk_lon(), 0);
COMPARE(b1.get_chunk_lat(), 88);
VERIFY(b1.gen_index() != b2.gen_index());
SGBucket b3(176.1, 88.5);
COMPARE(b3.get_chunk_lon(), 176);
SGBucket b4(-178, 88.5);
COMPARE(b4.get_chunk_lon(), -180);
}
void testOffset()
{
// bucket just below the 22 degree cutoff, so the next tile north
// is twice the width
SGBucket b1(-59.8, 21.9);
COMPARE(b1.get_chunk_lat(), 21);
COMPARE(b1.get_chunk_lon(), -60);
COMPARE(b1.get_x(), 1);
COMPARE(b1.get_y(), 7);
// offset vertically
SGBucket b2(b1.sibling(0, 1));
COMPARE(b2.get_chunk_lat(), 22);
COMPARE(b2.get_chunk_lon(), -60);
COMPARE(b2.get_x(), 0);
COMPARE(b2.get_y(), 0);
COMPARE(b2.gen_index(), sgBucketOffset(-59.8, 21.9, 0, 1));
// offset vertically and horizontally. We compute horizontal (x)
// movement at the target latitude, so this should move 0.25 * -3 degrees,
// NOT 0.125 * -3 degrees.
SGBucket b3(b1.sibling(-3, 1));
COMPARE(b3.get_chunk_lat(), 22);
COMPARE(b3.get_chunk_lon(), -61);
COMPARE(b3.get_x(), 1);
COMPARE(b3.get_y(), 0);
COMPARE(b3.gen_index(), sgBucketOffset(-59.8, 21.9, -3, 1));
}
void testPolarOffset()
{
SGBucket b1(-11.7, -89.6);
COMPARE(b1.get_chunk_lat(), -90);
COMPARE(b1.get_chunk_lon(), -12);
COMPARE(b1.get_x(), 0);
COMPARE(b1.get_y(), 3);
// offset horizontally
SGBucket b2(b1.sibling(-2, 0));
COMPARE(b2.get_chunk_lat(), -90);
COMPARE(b2.get_chunk_lon(), -36);
COMPARE(b2.get_x(), 0);
COMPARE(b2.get_y(), 3);
COMPARE(b2.gen_index(), sgBucketOffset(-11.7, -89.6, -2, 0));
// offset and wrap
SGBucket b3(-170, 89.1);
SGBucket b4(b3.sibling(-1, 0));
COMPARE(b4.get_chunk_lat(), 89);
COMPARE(b4.get_chunk_lon(), 168);
COMPARE(b4.get_x(), 0);
COMPARE(b4.get_y(), 0);
COMPARE(b4.gen_index(), sgBucketOffset(-170, 89.1, -1, 0));
SGBucket b5(177, 87.3);
SGBucket b6(b5.sibling(1, 1));
COMPARE(b6.get_chunk_lat(), 87);
COMPARE(b6.get_chunk_lon(), -180);
COMPARE(b6.get_x(), 0);
COMPARE(b6.get_y(), 3);
COMPARE(b6.gen_index(), sgBucketOffset(177, 87.3, 1, 1));
// offset vertically towards the pole
SGBucket b7(b1.sibling(0, -5));
VERIFY(!b7.isValid());
VERIFY(!SGBucket(0, 90).sibling(0, 1).isValid());
}
// test behaviour of bucket-offset near the anti-meridian (180-meridian)
void testOffsetWrap()
{
// near the equator
SGBucket b1(-179.8, 16.8);
COMPARE(b1.get_chunk_lat(), 16);
COMPARE(b1.get_chunk_lon(), -180);
COMPARE(b1.get_x(), 1);
COMPARE(b1.get_y(), 6);
SGBucket b2(b1.sibling(-2, 0));
COMPARE(b2.get_chunk_lat(), 16);
COMPARE(b2.get_chunk_lon(), 179);
COMPARE(b2.get_x(), 7);
COMPARE(b2.get_y(), 6);
COMPARE(b2.gen_index(), sgBucketOffset(-179.8, 16.8, -2, 0));
}
int main(int argc, char* argv[])
{
testBucketSpans();
testBasic();
testPolar();
testNearPolar();
testOffset();
testOffsetWrap();
testPolarOffset();
cout << "all tests passed OK" << endl;
return 0; // passed
}

View File

@@ -43,7 +43,7 @@ public:
void removeChild(BVHNode* child);
unsigned getNumChildren() const
{ return static_cast<unsigned>(_children.size()); }
{ return _children.size(); }
const BVHNode* getChild(unsigned i) const
{ if (_children.size() <= i) return 0; return _children[i]; }
BVHNode* getChild(unsigned i)

View File

@@ -31,13 +31,13 @@ public:
virtual ~BVHStaticData() {}
unsigned addVertex(const SGVec3f& vertex)
{ _vertices.push_back(vertex); return static_cast<unsigned>(_vertices.size() - 1); }
{ _vertices.push_back(vertex); return _vertices.size() - 1; }
const SGVec3f& getVertex(unsigned i) const
{ return _vertices[i]; }
unsigned addMaterial(const BVHMaterial* material)
{ _materials.push_back(material); return static_cast<unsigned>(_materials.size() - 1); }
{ _materials.push_back(material); return _materials.size() - 1; }
const BVHMaterial* getMaterial(unsigned i) const
{ if (_materials.size() <= i) return 0; return _materials[i]; }

View File

@@ -11,7 +11,7 @@ set(HEADERS
CanvasObjectPlacement.hxx
CanvasPlacement.hxx
CanvasSystemAdapter.hxx
CanvasWindow.hxx
MouseEvent.hxx
ODGauge.hxx
VGInitOperation.hxx
)
@@ -24,14 +24,11 @@ set(SOURCES
CanvasMgr.cxx
CanvasObjectPlacement.cxx
CanvasPlacement.cxx
CanvasWindow.cxx
ODGauge.cxx
VGInitOperation.cxx
)
add_subdirectory(ShivaVG/src)
add_subdirectory(elements)
add_subdirectory(events)
add_subdirectory(layout)
simgear_scene_component(canvas canvas "${SOURCES}" "${HEADERS}")

View File

@@ -19,9 +19,8 @@
#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/canvas/MouseEvent.hxx>
#include <simgear/canvas/CanvasPlacement.hxx>
#include <simgear/scene/util/parse_color.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
@@ -49,12 +48,8 @@ namespace canvas
void Canvas::CullCallback::operator()( osg::Node* node,
osg::NodeVisitor* nv )
{
if( (nv->getTraversalMask() & simgear::MODEL_BIT) )
{
CanvasPtr canvas = _canvas.lock();
if( canvas )
canvas->enableRendering();
}
if( (nv->getTraversalMask() & simgear::MODEL_BIT) && !_canvas.expired() )
_canvas.lock()->enableRendering();
traverse(node, nv);
}
@@ -77,13 +72,6 @@ namespace canvas
{
_status = 0;
setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
_root_group.reset( new Group(this, _node) );
// Remove automatically created property listener as we forward them on our
// own
_root_group->removeListener();
_cull_callback = new CullCallback(this);
}
//----------------------------------------------------------------------------
@@ -192,32 +180,6 @@ namespace canvas
return _root_group;
}
//----------------------------------------------------------------------------
void Canvas::setLayout(const LayoutRef& layout)
{
_layout = layout;
_layout->setCanvas(this);
}
//----------------------------------------------------------------------------
void Canvas::setFocusElement(const ElementPtr& el)
{
if( el && el->getCanvas().lock() != this )
{
SG_LOG(SG_GUI, SG_WARN, "setFocusElement: element not from this canvas");
return;
}
// TODO focus out/in events
_focus_element = el;
}
//----------------------------------------------------------------------------
void Canvas::clearFocusElement()
{
_focus_element.reset();
}
//----------------------------------------------------------------------------
void Canvas::enableRendering(bool force)
{
@@ -229,10 +191,11 @@ namespace canvas
//----------------------------------------------------------------------------
void Canvas::update(double delta_time_sec)
{
if( _status & (CREATE_FAILED | MISSING_SIZE) )
if( (!_texture.serviceable() && _status != STATUS_DIRTY)
|| (_status & CREATE_FAILED) )
return;
if( _status & STATUS_DIRTY )
if( _status == STATUS_DIRTY )
{
_texture.setSize(_size_x, _size_y);
@@ -274,28 +237,23 @@ namespace canvas
}
}
if( _layout )
_layout->setGeometry(SGRecti(0, 0, _view_width, _view_height));
if( _visible || _render_always )
{
BOOST_FOREACH(CanvasWeakPtr canvas_weak, _child_canvases)
BOOST_FOREACH(CanvasWeakPtr canvas, _child_canvases)
{
// TODO should we check if the image the child canvas is displayed
// within is really visible?
CanvasPtr canvas = canvas_weak.lock();
if( canvas )
canvas->_visible = true;
if( !canvas.expired() )
canvas.lock()->_visible = true;
}
if( _render_dirty )
{
// Also mark all canvases this canvas is displayed within as dirty
BOOST_FOREACH(CanvasWeakPtr canvas_weak, _parent_canvases)
BOOST_FOREACH(CanvasWeakPtr canvas, _parent_canvases)
{
CanvasPtr canvas = canvas_weak.lock();
if( canvas )
canvas->_render_dirty = true;
if( !canvas.expired() )
canvas.lock()->_render_dirty = true;
}
}
@@ -338,7 +296,11 @@ namespace canvas
if( placement_factory != _placement_factories.end() )
{
Placements& placements = _placements[ node->getIndex() ] =
placement_factory->second(node, this);
placement_factory->second
(
node,
boost::static_pointer_cast<Canvas>(_self.lock())
);
node->setStringValue
(
"status-msg",
@@ -355,20 +317,11 @@ namespace canvas
const EventListener& cb )
{
if( !_root_group.get() )
throw std::runtime_error("Canvas::addEventListener: no root group!");
throw std::runtime_error("Canvas::AddEventListener: no root group!");
return _root_group->addEventListener(type, cb);
}
//----------------------------------------------------------------------------
bool Canvas::dispatchEvent(const EventPtr& event)
{
if( !_root_group.get() )
throw std::runtime_error("Canvas::dispatchEvent: no root group!");
return _root_group->dispatchEvent(event);
}
//----------------------------------------------------------------------------
void Canvas::setSizeX(int sx)
{
@@ -456,11 +409,12 @@ namespace canvas
//----------------------------------------------------------------------------
bool Canvas::handleMouseEvent(const MouseEventPtr& event)
{
if( !_root_group )
if( !_root_group.get() )
return false;
EventVisitor visitor( EventVisitor::TRAVERSE_DOWN,
event->getClientPos(),
event->getDelta(),
_root_group );
if( !_root_group->accept(visitor) )
return false;
@@ -468,25 +422,6 @@ namespace canvas
return _event_manager->handleEvent(event, visitor.getPropagationPath());
}
//----------------------------------------------------------------------------
bool Canvas::handleKeyboardEvent(const KeyboardEventPtr& event)
{
ElementPtr target = _focus_element.lock();
if( !target )
target = _root_group;
if( !target )
return false;
return target->dispatchEvent(event);
}
//----------------------------------------------------------------------------
bool Canvas::propagateEvent( EventPtr const& event,
EventPropagationPath const& path )
{
return _event_manager->propagateEvent(event, path);
}
//----------------------------------------------------------------------------
void Canvas::childAdded( SGPropertyNode * parent,
SGPropertyNode * child )
@@ -518,10 +453,8 @@ namespace canvas
//----------------------------------------------------------------------------
void Canvas::valueChanged(SGPropertyNode* node)
{
const std::string& name = node->getNameString();
if( boost::starts_with(name, "status")
|| boost::starts_with(name, "data-") )
if( boost::starts_with(node->getNameString(), "status")
|| node->getParent()->getNameString() == "bounding-box" )
return;
_render_dirty = true;
@@ -560,7 +493,7 @@ namespace canvas
}
else if( node->getParent() == _node )
{
if( name == "background" )
if( node->getNameString() == "background" )
{
osg::Vec4 color;
if( _texture.getCamera() && parseColor(node->getStringValue(), color) )
@@ -569,41 +502,35 @@ namespace canvas
_render_dirty = true;
}
}
else if( name == "mipmapping"
|| name == "coverage-samples"
|| name == "color-samples" )
else if( node->getNameString() == "mipmapping"
|| node->getNameString() == "coverage-samples"
|| node->getNameString() == "color-samples" )
{
_sampling_dirty = true;
}
else if( name == "additive-blend" )
else if( node->getNameString() == "additive-blend" )
{
_texture.useAdditiveBlend( node->getBoolValue() );
}
else if( name == "render-always" )
else if( node->getNameString() == "render-always" )
{
_render_always = node->getBoolValue();
}
else if( name == "size" )
else if( node->getNameString() == "size" )
{
if( node->getIndex() == 0 )
setSizeX( node->getIntValue() );
else if( node->getIndex() == 1 )
setSizeY( node->getIntValue() );
}
else if( name == "update" )
{
if( _root_group )
_root_group->update(0);
return update(0);
}
else if( name == "view" )
else if( node->getNameString() == "view" )
{
if( node->getIndex() == 0 )
setViewWidth( node->getIntValue() );
else if( node->getIndex() == 1 )
setViewHeight( node->getIntValue() );
}
else if( name == "freeze" )
else if( node->getNameString() == "freeze" )
_texture.setRender( node->getBoolValue() );
else
handled = false;
@@ -688,6 +615,23 @@ namespace canvas
return _system_adapter;
}
//----------------------------------------------------------------------------
void Canvas::setSelf(const PropertyBasedElementPtr& self)
{
PropertyBasedElement::setSelf(self);
CanvasPtr canvas = boost::static_pointer_cast<Canvas>(self);
_root_group.reset( new Group(canvas, _node) );
_root_group->setSelf(_root_group);
// Remove automatically created property listener as we forward them on our
// own
_root_group->removeListener();
_cull_callback = new CullCallback(canvas);
}
//----------------------------------------------------------------------------
void Canvas::setStatusFlags(unsigned int flags, bool set)
{
@@ -704,7 +648,7 @@ namespace canvas
_status_msg = "Missing size-y";
else if( _status & CREATE_FAILED )
_status_msg = "Creating render target failed";
else if( _status & STATUS_DIRTY )
else if( _status == STATUS_DIRTY )
_status_msg = "Creation pending...";
else
_status_msg = "Ok";

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>
//
@@ -24,12 +23,9 @@
#include "ODGauge.hxx"
#include <simgear/canvas/elements/CanvasGroup.hxx>
#include <simgear/canvas/layout/Layout.hxx>
#include <simgear/math/SGRect.hxx>
#include <simgear/nasal/cppbind/NasalObject.hxx>
#include <simgear/props/PropertyBasedElement.hxx>
#include <simgear/props/propertyObject.hxx>
#include <osg/NodeCallback>
#include <osg/observer_ptr>
@@ -38,28 +34,22 @@
namespace simgear
{
/// Canvas 2D drawing API
namespace canvas
{
class CanvasMgr;
class MouseEvent;
/**
* Canvas to draw onto (to an off-screen render target).
*/
class Canvas:
public PropertyBasedElement,
public nasal::Object
public PropertyBasedElement
{
public:
enum StatusFlags
{
STATUS_OK,
STATUS_DIRTY = 1,
STATUS_DIRTY = 1,
MISSING_SIZE_X = STATUS_DIRTY << 1,
MISSING_SIZE_Y = MISSING_SIZE_X << 1,
MISSING_SIZE = MISSING_SIZE_X | MISSING_SIZE_Y,
CREATE_FAILED = MISSING_SIZE_Y << 1
};
@@ -130,28 +120,6 @@ namespace canvas
*/
GroupPtr getRootGroup();
/**
* Set the layout of the canvas (the layout will automatically update with
* the viewport size of the canvas)
*/
void setLayout(const LayoutRef& layout);
/**
* Set the focus to the given element.
*
* The focus element will receive all keyboard events propagated to this
* canvas. If there is no valid focus element the root group will receive
* the events instead.
*/
void setFocusElement(const ElementPtr& el);
/**
* Clear the focus element.
*
* @see setFocusElement()
*/
void clearFocusElement();
/**
* Enable rendering for the next frame
*
@@ -163,7 +131,6 @@ namespace canvas
void update(double delta_time_sec);
bool addEventListener(const std::string& type, const EventListener& cb);
bool dispatchEvent(const EventPtr& event);
void setSizeX(int sx);
void setSizeY(int sy);
@@ -179,10 +146,6 @@ namespace canvas
SGRect<int> getViewport() const;
bool handleMouseEvent(const MouseEventPtr& event);
bool handleKeyboardEvent(const KeyboardEventPtr& event);
bool propagateEvent( EventPtr const& event,
EventPropagationPath const& path );
virtual void childAdded( SGPropertyNode * parent,
SGPropertyNode * child );
@@ -228,24 +191,21 @@ namespace canvas
_visible;
ODGauge _texture;
GroupPtr _root_group;
LayoutRef _layout;
ElementWeakPtr _focus_element;
GroupPtr _root_group;
CullCallbackPtr _cull_callback;
bool _render_always; //!< Used to disable automatic lazy rendering (culling)
bool _render_always; //<! Used to disable automatic lazy rendering (culling)
std::vector<SGPropertyNode*> _dirty_placements;
std::vector<Placements> _placements;
std::set<CanvasWeakPtr> _parent_canvases, //!< Canvases showing this canvas
_child_canvases; //!< Canvases displayed within
std::set<CanvasWeakPtr> _parent_canvases, //<! Canvases showing this canvas
_child_canvases; //<! Canvases displayed within
// this canvas
typedef std::map<std::string, PlacementFactory> PlacementFactoryMap;
static PlacementFactoryMap _placement_factories;
virtual void setSelf(const PropertyBasedElementPtr& self);
void setStatusFlags(unsigned int flags, bool set = true);
private:

View File

@@ -26,9 +26,7 @@ namespace canvas
//----------------------------------------------------------------------------
Event::Event():
type(UNKNOWN),
time(-1),
propagation_stopped(false),
default_prevented(false)
propagation_stopped(false)
{
}
@@ -40,13 +38,7 @@ namespace canvas
}
//----------------------------------------------------------------------------
bool Event::canBubble() const
{
return true;
}
//----------------------------------------------------------------------------
int Event::getType() const
Event::Type Event::getType() const
{
return type;
}
@@ -54,7 +46,14 @@ namespace canvas
//----------------------------------------------------------------------------
std::string Event::getTypeString() const
{
return typeToStr(type);
switch( type )
{
# define ENUM_MAPPING(name, str) case name: return str;
# include "CanvasEventTypes.hxx"
# undef ENUM_MAPPING
default:
return "unknown";
}
}
//----------------------------------------------------------------------------
@@ -82,69 +81,23 @@ namespace canvas
}
//----------------------------------------------------------------------------
void Event::preventDefault()
{
default_prevented = true;
}
//----------------------------------------------------------------------------
bool Event::defaultPrevented() const
{
return default_prevented;
}
//----------------------------------------------------------------------------
int Event::getOrRegisterType(const std::string& type_str)
{
int type = strToType(type_str);
if( type == UNKNOWN )
{
// Register new type
TypeMap& type_map = getTypeMap();
type = type_map.size() + 1; // ids start with 1 (after UNKNOWN)
type_map.insert(TypeMap::value_type(type_str, type));
}
return type;
}
//----------------------------------------------------------------------------
int Event::strToType(const std::string& str)
{
TypeMap const& type_map = getTypeMap();
TypeMap::map_by<name>::const_iterator it = type_map.by<name>().find(str);
if( it == type_map.by<name>().end() )
return UNKNOWN;
return it->second;
}
//----------------------------------------------------------------------------
std::string Event::typeToStr(int type)
{
TypeMap const& type_map = getTypeMap();
TypeMap::map_by<id>::const_iterator it = type_map.by<id>().find(type);
if( it == type_map.by<id>().end() )
return "unknown";
return it->second;
}
//----------------------------------------------------------------------------
Event::TypeMap& Event::getTypeMap()
Event::Type Event::strToType(const std::string& str)
{
typedef std::map<std::string, Type> TypeMap;
static TypeMap type_map;
if( type_map.empty() )
{
# define ENUM_MAPPING(type, str, class_name)\
type_map.insert(TypeMap::value_type(str, type));
# include "CanvasEventTypes.hxx"
# undef ENUM_MAPPING
# define ENUM_MAPPING(type, str) type_map[ str ] = type;
# include "CanvasEventTypes.hxx"
# undef ENUM_MAPPING
}
return type_map;
TypeMap::const_iterator it = type_map.find(str);
if( it == type_map.end() )
return UNKNOWN;
return it->second;
}
} // 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>
//
@@ -21,43 +20,31 @@
#define CANVAS_EVENT_HXX_
#include "canvas_fwd.hxx"
#include <boost/bimap.hpp>
namespace simgear
{
namespace canvas
{
/**
* Base class for all Canvas events.
*
* The event system is closely following the specification of the DOM Level 3
* Event Model.
*/
class Event:
public SGReferenced
class Event
{
public:
/// Event type identifier
enum Type
{
UNKNOWN,
# define ENUM_MAPPING(name, str, class_name)\
name, /*!< class_name (type=str) */
# define ENUM_MAPPING(name, str) name,
# include "CanvasEventTypes.hxx"
# undef ENUM_MAPPING
CUSTOM_EVENT ///< First event type id available for user defined event
/// type.
/// @see CustomEvent
USER_TYPE ///<! first unused id to be used for user defined types (not
/// implemented yet)
};
int type;
Type type;
ElementWeakPtr target,
current_target;
double time;
bool propagation_stopped,
default_prevented;
bool propagation_stopped;
Event();
@@ -65,64 +52,17 @@ namespace canvas
// of the actual event instances.
virtual ~Event();
/**
* Get whether this events support bubbling
*/
virtual bool canBubble() const;
/**
* Set type of event.
*
* If no such type exists it is registered.
*/
void setType(const std::string& type);
int getType() const;
Type getType() const;
std::string getTypeString() const;
ElementWeakPtr getTarget() const;
ElementWeakPtr getCurrentTarget() const;
/**
* Get time at which the event was generated.
*/
double getTime() const;
/**
* Prevent further propagation of the event during event flow.
*
* @note This does not prevent calling further event handlers registered
* on the current event target.
*/
void stopPropagation();
/**
* Cancel any default action normally taken as result of this event.
*
* @note For event handlers registered on the DesktopGroup (Nasal:
* canvas.getDesktop()) this stops the event from being further
* propagated to the normal FlightGear input event handling code.
*/
void preventDefault();
/**
* Get if preventDefault() has been called.
*/
bool defaultPrevented() const;
static int getOrRegisterType(const std::string& type);
static int strToType(const std::string& type);
static std::string typeToStr(int type);
protected:
struct name {};
struct id {};
typedef boost::bimaps::bimap<
boost::bimaps::tagged<std::string, name>,
boost::bimaps::tagged<int, id>
> TypeMap;
static TypeMap& getTypeMap();
static Type strToType(const std::string& str);
};

View File

@@ -17,7 +17,7 @@
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "CanvasEventManager.hxx"
#include <simgear/canvas/events/MouseEvent.hxx>
#include "MouseEvent.hxx"
#include <simgear/canvas/elements/CanvasElement.hxx>
#include <cmath>
@@ -61,16 +61,6 @@ namespace canvas
return !path.empty() && time > 0;
}
//----------------------------------------------------------------------------
void EventManager::MouseEventInfo::set( const MouseEventPtr& event,
const EventPropagationPath& p )
{
path = p;
time = event->time;
button = event->button;
pos = event->screen_pos;
}
//----------------------------------------------------------------------------
EventManager::EventManager():
_current_click_count(0)
@@ -86,7 +76,7 @@ namespace canvas
switch( event->type )
{
case Event::MOUSE_DOWN:
_last_mouse_down.set(event, path);
_last_mouse_down = StampedPropagationPath(path, event->getTime());
break;
case Event::MOUSE_UP:
{
@@ -99,12 +89,12 @@ namespace canvas
// normal mouseup
handled |= propagateEvent(event, path);
if( !_last_mouse_down.valid() )
if( _last_mouse_down.path.empty() )
// Ignore mouse up without any previous mouse down
return handled;
// now handle click/dblclick
if( checkClickDistance(event->screen_pos, _last_mouse_down.pos) )
if( checkClickDistance(path, _last_mouse_down.path) )
handled |=
handleClick(event, getCommonAncestor(_last_mouse_down.path, path));
@@ -116,11 +106,7 @@ namespace canvas
if( !_last_mouse_down.valid() )
return false;
else
{
// OSG does not set button for drag events.
event->button = _last_mouse_down.button;
return propagateEvent(event, _last_mouse_down.path);
}
case Event::MOUSE_MOVE:
handled |= handleMove(event, path);
break;
@@ -144,71 +130,6 @@ namespace canvas
return handled | propagateEvent(event, path);
}
//----------------------------------------------------------------------------
bool EventManager::propagateEvent( const EventPtr& event,
const EventPropagationPath& path )
{
event->target = path.back().element;
MouseEventPtr mouse_event = dynamic_cast<MouseEvent*>(event.get());
// Event propagation similar to DOM Level 3 event flow:
// http://www.w3.org/TR/DOM-Level-3-Events/#event-flow
// Position update only needed for drag event (as event needs to be
// delivered to element of initial mousedown, but with update positions)
if( mouse_event && mouse_event->type == MouseEvent::DRAG )
{
osg::Vec2f local_pos = mouse_event->client_pos;
// Capturing phase (currently just update position)
for( EventPropagationPath::const_iterator it = path.begin();
it != path.end();
++it )
{
ElementPtr el = it->element.lock();
if( !el )
continue;
it->local_pos = local_pos = el->posToLocal(local_pos);
}
}
bool const do_bubble = event->canBubble();
// Bubbling phase
for( EventPropagationPath::const_reverse_iterator
it = path.rbegin();
it != path.rend();
++it )
{
ElementPtr el = it->element.lock();
if( !el )
{
// Ignore element if it has been destroyed while traversing the event
// (eg. removed by another event handler)
if( do_bubble )
continue;
else
break;
}
// TODO provide functions to convert delta to local coordinates on demand.
// Maybe also provide a clone method for events as local coordinates
// might differ between different elements receiving the same event.
if( mouse_event )
mouse_event->local_pos = it->local_pos;
event->current_target = el;
el->handleEvent(event);
if( event->propagation_stopped || !do_bubble )
return true;
}
return true;
}
//----------------------------------------------------------------------------
bool EventManager::handleClick( const MouseEventPtr& event,
const EventPropagationPath& path )
@@ -225,10 +146,8 @@ namespace canvas
if( _current_click_count > 1 )
{
// Reset current click count if moved too far or different button has
// been clicked
if( !checkClickDistance(event->screen_pos, _last_click.pos)
|| _last_click.button != event->button )
// Reset current click count if moved too far
if( !checkClickDistance(path, _last_click.path) )
_current_click_count = 1;
}
}
@@ -248,7 +167,7 @@ namespace canvas
handled |= propagateEvent( dbl_click,
getCommonAncestor(_last_click.path, path) );
_last_click.set(event, path);
_last_click = StampedPropagationPath(path, event->getTime());
return handled;
}
@@ -315,11 +234,88 @@ namespace canvas
}
//----------------------------------------------------------------------------
bool
EventManager::checkClickDistance( const osg::Vec2f& pos1,
const osg::Vec2f& pos2 ) const
bool EventManager::propagateEvent( const EventPtr& event,
const EventPropagationPath& path )
{
osg::Vec2 delta = pos1 - pos2;
event->target = path.back().element;
MouseEventPtr mouse_event = boost::dynamic_pointer_cast<MouseEvent>(event);
// Event propagation similar to DOM Level 3 event flow:
// http://www.w3.org/TR/DOM-Level-3-Events/#event-flow
// Capturing phase
// for( EventPropagationPath::const_iterator it = path.begin();
// it != path.end();
// ++it )
// {
// if( !it->element.expired() )
// std::cout << it->element.lock()->getProps()->getPath() << std::endl;
// }
// Check if event supports bubbling
const Event::Type types_no_bubbling[] = {
Event::MOUSE_ENTER,
Event::MOUSE_LEAVE,
};
const size_t num_types_no_bubbling = sizeof(types_no_bubbling)
/ sizeof(types_no_bubbling[0]);
bool do_bubble = true;
for( size_t i = 0; i < num_types_no_bubbling; ++i )
if( event->type == types_no_bubbling[i] )
{
do_bubble = false;
break;
}
// Bubbling phase
for( EventPropagationPath::const_reverse_iterator
it = path.rbegin();
it != path.rend();
++it )
{
ElementPtr el = it->element.lock();
if( !el )
{
// Ignore element if it has been destroyed while traversing the event
// (eg. removed by another event handler)
if( do_bubble )
continue;
else
break;
}
// TODO provide functions to convert delta to local coordinates on demand.
// Maybe also provide a clone method for events as local coordinates
// might differ between different elements receiving the same event.
if( mouse_event ) //&& event->type != Event::DRAG )
{
// TODO transform pos and delta for drag events. Maybe we should just
// store the global coordinates and convert to local coordinates
// on demand.
// Position and delta are specified in local coordinate system of
// current element
mouse_event->local_pos = it->local_pos;
//mouse_event->delta = it->local_delta;
}
event->current_target = el;
el->handleEvent(event);
if( event->propagation_stopped || !do_bubble )
return true;
}
return true;
}
//----------------------------------------------------------------------------
bool
EventManager::checkClickDistance( const EventPropagationPath& path1,
const EventPropagationPath& path2 ) const
{
osg::Vec2 delta = path1.front().local_pos - path2.front().local_pos;
return std::fabs(delta.x()) < drag_threshold
&& std::fabs(delta.y()) < drag_threshold;
}

View File

@@ -29,18 +29,11 @@ namespace canvas
struct EventTarget
{
ElementWeakPtr element;
// Used as storage by EventManager during event propagation
mutable osg::Vec2f local_pos;
EventTarget( Element* el,
const osg::Vec2f pos = osg::Vec2f() ):
element(el),
local_pos(pos)
{}
ElementWeakPtr element;
osg::Vec2f local_pos,
local_delta;
};
typedef std::deque<EventTarget> EventPropagationPath;
inline bool operator==(const EventTarget& t1, const EventTarget& t2)
{
return t1.element.lock() == t2.element.lock();
@@ -54,9 +47,6 @@ namespace canvas
bool handleEvent( const MouseEventPtr& event,
const EventPropagationPath& path );
bool propagateEvent( const EventPtr& event,
const EventPropagationPath& path );
protected:
struct StampedPropagationPath
{
@@ -72,20 +62,11 @@ namespace canvas
// TODO if we really need the paths modify to not copy around the paths
// that much.
StampedPropagationPath _last_mouse_over;
StampedPropagationPath _last_mouse_down,
_last_click,
_last_mouse_over;
size_t _current_click_count;
struct MouseEventInfo:
public StampedPropagationPath
{
int button;
osg::Vec2f pos;
void set( const MouseEventPtr& event,
const EventPropagationPath& path );
} _last_mouse_down,
_last_click;
/**
* Propagate click event and handle multi-click (eg. create dblclick)
*/
@@ -98,13 +79,16 @@ namespace canvas
bool handleMove( const MouseEventPtr& event,
const EventPropagationPath& path );
bool propagateEvent( const EventPtr& event,
const EventPropagationPath& path );
/**
* Check if two click events (either mousedown/up or two consecutive
* clicks) are inside a maximum distance to still create a click or
* dblclick event respectively.
*/
bool checkClickDistance( const osg::Vec2f& pos1,
const osg::Vec2f& pos2 ) const;
bool checkClickDistance( const EventPropagationPath& path1,
const EventPropagationPath& path2 ) const;
EventPropagationPath
getCommonAncestor( const EventPropagationPath& path1,
const EventPropagationPath& path2 ) const;

View File

@@ -20,17 +20,14 @@
# error "Don't include this file directly!"
#endif
ENUM_MAPPING(MOUSE_DOWN, "mousedown", MouseEvent)
ENUM_MAPPING(MOUSE_UP, "mouseup", MouseEvent)
ENUM_MAPPING(CLICK, "click", MouseEvent)
ENUM_MAPPING(DBL_CLICK, "dblclick", MouseEvent)
ENUM_MAPPING(DRAG, "drag", MouseEvent)
ENUM_MAPPING(WHEEL, "wheel", MouseEvent)
ENUM_MAPPING(MOUSE_MOVE, "mousemove", MouseEvent)
ENUM_MAPPING(MOUSE_OVER, "mouseover", MouseEvent)
ENUM_MAPPING(MOUSE_OUT, "mouseout", MouseEvent)
ENUM_MAPPING(MOUSE_ENTER, "mouseenter", MouseEvent)
ENUM_MAPPING(MOUSE_LEAVE, "mouseleave", MouseEvent)
ENUM_MAPPING(KEY_DOWN, "keydown", KeyboardEvent)
ENUM_MAPPING(KEY_UP, "keyup", KeyboardEvent)
ENUM_MAPPING(KEY_PRESS, "keypress", KeyboardEvent)
ENUM_MAPPING(MOUSE_DOWN, "mousedown")
ENUM_MAPPING(MOUSE_UP, "mouseup")
ENUM_MAPPING(CLICK, "click")
ENUM_MAPPING(DBL_CLICK, "dblclick")
ENUM_MAPPING(DRAG, "drag")
ENUM_MAPPING(WHEEL, "wheel")
ENUM_MAPPING(MOUSE_MOVE, "mousemove")
ENUM_MAPPING(MOUSE_OVER, "mouseover")
ENUM_MAPPING(MOUSE_OUT, "mouseout")
ENUM_MAPPING(MOUSE_ENTER, "mouseenter")
ENUM_MAPPING(MOUSE_LEAVE, "mouseleave")

View File

@@ -20,6 +20,7 @@
#include "CanvasEvent.hxx"
#include "CanvasEventVisitor.hxx"
#include <simgear/canvas/elements/CanvasElement.hxx>
#include <iostream>
namespace simgear
{
@@ -29,12 +30,16 @@ namespace canvas
//----------------------------------------------------------------------------
EventVisitor::EventVisitor( TraverseMode mode,
const osg::Vec2f& pos,
const osg::Vec2f& delta,
const ElementPtr& root ):
_traverse_mode( mode ),
_root(root)
{
if( mode == TRAVERSE_DOWN )
_target_path.push_back( EventTarget(NULL, pos) );
{
EventTarget target = {ElementWeakPtr(), pos, delta};
_target_path.push_back(target);
}
}
//----------------------------------------------------------------------------
@@ -58,18 +63,31 @@ namespace canvas
// We only need to check for hits while traversing down
if( _traverse_mode == TRAVERSE_DOWN )
{
// Transform event to local coordinates
const osg::Matrix& m = el.getMatrixTransform()->getInverseMatrix();
const osg::Vec2f& pos = _target_path.back().local_pos;
const osg::Vec2f local_pos = el.posToLocal(pos);
const osg::Vec2f local_pos
(
m(0, 0) * pos[0] + m(1, 0) * pos[1] + m(3, 0),
m(0, 1) * pos[0] + m(1, 1) * pos[1] + m(3, 1)
);
// Don't check specified root element for collision, as its purpose is to
// catch all events which have no target. This allows for example calling
// event listeners attached to the canvas itself (its root group) even if
// no element has been hit.
if( _root.get() != &el
&& !el.hitBound(_target_path.front().local_pos, pos, local_pos) )
if( _root.get() != &el && !el.hitBound(pos, local_pos) )
return false;
_target_path.push_back( EventTarget(&el, local_pos) );
const osg::Vec2f& delta = _target_path.back().local_delta;
const osg::Vec2f local_delta
(
m(0, 0) * delta[0] + m(1, 0) * delta[1],
m(0, 1) * delta[0] + m(1, 1) * delta[1]
);
EventTarget target = {el.getWeakPtr(), local_pos, local_delta};
_target_path.push_back(target);
if( el.traverse(*this) || &el == _root.get() )
return true;

View File

@@ -42,10 +42,12 @@ namespace canvas
*
* @param mode
* @param pos Mouse position
* @param delta Mouse movement since last mouse move event
* @param root Element to dispatch events to if no element is hit
*/
EventVisitor( TraverseMode mode,
const osg::Vec2f& pos,
const osg::Vec2f& delta,
const ElementPtr& root = ElementPtr() );
virtual ~EventVisitor();
virtual bool traverse(Element& el);

View File

@@ -45,25 +45,25 @@ namespace canvas
//----------------------------------------------------------------------------
CanvasPtr CanvasMgr::createCanvas(const std::string& name)
{
return static_cast<Canvas*>( createElement(name).get() );
return boost::static_pointer_cast<Canvas>( createElement(name) );
}
//----------------------------------------------------------------------------
CanvasPtr CanvasMgr::getCanvas(size_t index) const
{
return static_cast<Canvas*>( getElement(index).get() );
return boost::static_pointer_cast<Canvas>( getElement(index) );
}
//----------------------------------------------------------------------------
CanvasPtr CanvasMgr::getCanvas(const std::string& name) const
{
return static_cast<Canvas*>( getElement(name).get() );
return boost::static_pointer_cast<Canvas>( getElement(name) );
}
//----------------------------------------------------------------------------
void CanvasMgr::elementCreated(PropertyBasedElementPtr element)
{
CanvasPtr canvas = static_cast<Canvas*>(element.get());
CanvasPtr canvas = boost::static_pointer_cast<Canvas>(element);
canvas->setCanvasMgr(this);
}

View File

@@ -33,7 +33,7 @@ namespace canvas
public:
/**
* @param node Root node of branch used to control canvasses
* @param node Root node of branch used to control canvasses
*/
CanvasMgr(SGPropertyNode_ptr node);

View File

@@ -21,7 +21,7 @@
#include "Canvas.hxx"
#include "CanvasObjectPlacement.hxx"
#include <simgear/canvas/events/MouseEvent.hxx>
#include "MouseEvent.hxx"
#include <simgear/props/props.hxx>
#include <simgear/scene/util/SGPickCallback.hxx>

View File

@@ -1,8 +1,7 @@
///@file
/// Placement for putting a canvas texture onto OpenSceneGraph objects.
///
/// It also provides a SGPickCallback for passing mouse events to the canvas and
/// manages emissive lighting of the placed canvas.
// Canvas placement for placing a canvas texture onto osg objects.
//
// It also provides a SGPickCallback for passing mouse events to the canvas and
// manages emissive lighting of the placed canvas.
//
// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
//
@@ -20,7 +19,7 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef CANVAS_OBJECT_PLACEMENT_HXX_
#ifndef CANVAS_PICK_PLACEMENT_HXX_
#define CANVAS_OBJECT_PLACEMENT_HXX_
#include "CanvasPlacement.hxx"
@@ -34,9 +33,6 @@ namespace simgear
namespace canvas
{
/**
* Place a Canvas onto an osg object (as texture).
*/
class ObjectPlacement:
public Placement
{
@@ -76,4 +72,4 @@ namespace canvas
} // namespace canvas
} // namespace simgear
#endif /* CANVAS_OBJECT_PLACEMENT_HXX_ */
#endif /* CANVAS_PICK_PLACEMENT_HXX_ */

View File

@@ -21,11 +21,8 @@
#include "canvas_fwd.hxx"
class SGSubsystem;
namespace simgear
{
namespace HTTP { class Client; }
namespace canvas
{
@@ -38,8 +35,6 @@ namespace canvas
virtual void addCamera(osg::Camera* camera) const = 0;
virtual void removeCamera(osg::Camera* camera) const = 0;
virtual osg::Image* getImage(const std::string& path) const = 0;
virtual SGSubsystem* getSubsystem(const std::string& name) const = 0;
virtual HTTP::Client* getHTTPClient() const = 0;
};
} // namespace canvas

View File

@@ -1,346 +0,0 @@
// Window for placing a Canvas onto it (for dialogs, menus, etc.)
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "CanvasMgr.hxx"
#include "CanvasSystemAdapter.hxx"
#include "CanvasWindow.hxx"
#include <simgear/canvas/Canvas.hxx>
#include <simgear/scene/util/OsgMath.hxx>
#include <osgGA/GUIEventHandler>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/foreach.hpp>
namespace simgear
{
namespace canvas
{
//----------------------------------------------------------------------------
const std::string Window::TYPE_NAME = "window";
//----------------------------------------------------------------------------
Window::Window( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& parent_style,
Element* parent ):
Image(canvas, node, parent_style, parent),
_attributes_dirty(0),
_resizable(false),
_capture_events(true),
_resize_top(node, "resize-top"),
_resize_right(node, "resize-right"),
_resize_bottom(node, "resize-bottom"),
_resize_left(node, "resize-left"),
_resize_status(node, "resize-status")
{
node->setFloatValue("source/right", 1);
node->setFloatValue("source/bottom", 1);
node->setBoolValue("source/normalized", true);
}
//----------------------------------------------------------------------------
Window::~Window()
{
if( _canvas_decoration )
_canvas_decoration->destroy();
}
//----------------------------------------------------------------------------
void Window::update(double delta_time_sec)
{
if( _attributes_dirty & DECORATION )
{
updateDecoration();
_attributes_dirty &= ~DECORATION;
}
Image::update(delta_time_sec);
}
//----------------------------------------------------------------------------
void Window::valueChanged(SGPropertyNode * node)
{
bool handled = false;
if( node->getParent() == _node )
{
handled = true;
const std::string& name = node->getNameString();
if( name == "resize" )
_resizable = node->getBoolValue();
else if( name == "update" )
update(0);
else if( name == "capture-events" )
_capture_events = node->getBoolValue();
else if( name == "decoration-border" )
parseDecorationBorder(node->getStringValue());
else if( boost::starts_with(name, "shadow-")
|| name == "content-size" )
_attributes_dirty |= DECORATION;
else
handled = false;
}
if( !handled )
Image::valueChanged(node);
}
//----------------------------------------------------------------------------
osg::Group* Window::getGroup()
{
return getMatrixTransform();
}
//----------------------------------------------------------------------------
const SGVec2<float> Window::getPosition() const
{
const osg::Matrix& m = getMatrixTransform()->getMatrix();
return SGVec2<float>( m(3, 0), m(3, 1) );
}
//----------------------------------------------------------------------------
const SGRect<float> Window::getScreenRegion() const
{
return getPosition() + getRegion();
}
//----------------------------------------------------------------------------
void Window::setCanvasContent(CanvasPtr canvas)
{
_canvas_content = canvas;
if( _layout )
{
canvas->setLayout(_layout);
_layout.clear();
}
if( _image_content )
// Placement within decoration canvas
_image_content->setSrcCanvas(canvas);
else
setSrcCanvas(canvas);
}
//----------------------------------------------------------------------------
CanvasWeakPtr Window::getCanvasContent() const
{
return _canvas_content;
}
//----------------------------------------------------------------------------
void Window::setLayout(const LayoutRef& layout)
{
CanvasPtr canvas = _canvas_content.lock();
if( canvas )
canvas->setLayout(layout);
else
_layout = layout; // keep layout until content canvas is set
}
//----------------------------------------------------------------------------
CanvasPtr Window::getCanvasDecoration() const
{
return _canvas_decoration;
}
//----------------------------------------------------------------------------
bool Window::isResizable() const
{
return _resizable;
}
//----------------------------------------------------------------------------
bool Window::isCapturingEvents() const
{
return _capture_events;
}
//----------------------------------------------------------------------------
void Window::setVisible(bool visible)
{
LayoutItem::setVisible(visible);
Element::setVisible(LayoutItem::isVisible());
}
//----------------------------------------------------------------------------
bool Window::isVisible() const
{
return Element::isVisible();
}
//----------------------------------------------------------------------------
void Window::raise()
{
// on writing the z-index the window always is moved to the top of all other
// windows with the same z-index.
set<int>("z-index", get<int>("z-index", 0));
}
//----------------------------------------------------------------------------
void Window::handleResize( uint8_t mode,
const osg::Vec2f& offset )
{
if( mode == NONE )
{
_resize_status = 0;
return;
}
else if( mode & INIT )
{
_resize_top = getRegion().t();
_resize_right = getRegion().r();
_resize_bottom = getRegion().b();
_resize_left = getRegion().l();
_resize_status = 1;
}
if( mode & BOTTOM )
_resize_bottom = getRegion().b() + offset.y();
else if( mode & TOP )
_resize_top = getRegion().t() + offset.y();
if( mode & canvas::Window::RIGHT )
_resize_right = getRegion().r() + offset.x();
else if( mode & canvas::Window::LEFT )
_resize_left = getRegion().l() + offset.x();
}
//----------------------------------------------------------------------------
void Window::parseDecorationBorder(const std::string& str)
{
_decoration_border = simgear::CSSBorder::parse(str);
_attributes_dirty |= DECORATION;
}
//----------------------------------------------------------------------------
void Window::updateDecoration()
{
int shadow_radius = get<float>("shadow-radius") + 0.5;
if( shadow_radius < 2 )
shadow_radius = 0;
CanvasPtr content = _canvas_content.lock();
SGRect<int> content_view
(
0,
0,
get<int>("content-size[0]", content ? content->getViewWidth() : 400),
get<int>("content-size[1]", content ? content->getViewHeight() : 300)
);
if( _decoration_border.isNone() && !shadow_radius )
{
setSrcCanvas(content);
set<int>("size[0]", content_view.width());
set<int>("size[1]", content_view.height());
_image_content.reset();
_image_shadow.reset();
if( _canvas_decoration )
_canvas_decoration->destroy();
_canvas_decoration.reset();
return;
}
if( !_canvas_decoration )
{
CanvasMgr* mgr = dynamic_cast<CanvasMgr*>(
Canvas::getSystemAdapter()->getSubsystem("Canvas")
);
if( !mgr )
{
SG_LOG(SG_GENERAL, SG_WARN, "canvas::Window: no canvas manager!");
return;
}
_canvas_decoration = mgr->createCanvas("window-decoration");
_canvas_decoration->getProps()
->setStringValue("background", "rgba(0,0,0,0)");
setSrcCanvas(_canvas_decoration);
_image_content = _canvas_decoration->getRootGroup()
->createChild<Image>("content");
_image_content->setSrcCanvas(content);
// Forward keyboard events to content
_image_content->setFocus();
// Draw content on top of decoration
_image_content->set<int>("z-index", 1);
}
GroupPtr group_decoration =
_canvas_decoration->getOrCreateGroup("decoration");
group_decoration->set<int>("tf/t[0]", shadow_radius);
group_decoration->set<int>("tf/t[1]", shadow_radius);
// TODO do we need clipping or shall we trust the decorator not to draw over
// the shadow?
CSSBorder::Offsets const border =
_decoration_border.getAbsOffsets(content_view);
int shad2 = 2 * shadow_radius,
outer_width = border.l + content_view.width() + border.r + shad2,
outer_height = border.t + content_view.height() + border.b + shad2;
_canvas_decoration->setSizeX( outer_width );
_canvas_decoration->setSizeY( outer_height );
_canvas_decoration->setViewWidth( outer_width );
_canvas_decoration->setViewHeight( outer_height );
set<int>("size[0]", outer_width - shad2);
set<int>("size[1]", outer_height - shad2);
set<int>("outset", shadow_radius);
assert(_image_content);
_image_content->set<int>("x", shadow_radius + border.l);
_image_content->set<int>("y", shadow_radius + border.t);
_image_content->set<int>("size[0]", content_view.width());
_image_content->set<int>("size[1]", content_view.height());
if( !shadow_radius )
{
if( _image_shadow )
{
_image_shadow->destroy();
_image_shadow.reset();
}
return;
}
int shadow_inset = std::max<int>(get<float>("shadow-inset") + 0.5, 0),
slice_width = shadow_radius + shadow_inset;
_image_shadow = _canvas_decoration->getRootGroup()
->getOrCreateChild<Image>("shadow");
_image_shadow->set<std::string>("src", "gui/images/shadow.png");
_image_shadow->set<float>("slice", 7);
_image_shadow->set<std::string>("fill", "#000000");
_image_shadow->set<float>("slice-width", slice_width);
_image_shadow->set<int>("size[0]", outer_width);
_image_shadow->set<int>("size[1]", outer_height);
// Draw shadow below decoration
_image_shadow->set<int>("z-index", -1);
}
} // namespace canvas
} // namespace simgear

View File

@@ -1,134 +0,0 @@
// Window for placing a Canvas onto it (for dialogs, menus, etc.)
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef CANVAS_WINDOW_HXX_
#define CANVAS_WINDOW_HXX_
#include <simgear/canvas/elements/CanvasImage.hxx>
#include <simgear/canvas/layout/Layout.hxx>
#include <simgear/canvas/events/MouseEvent.hxx>
#include <simgear/props/PropertyBasedElement.hxx>
#include <simgear/props/propertyObject.hxx>
#include <simgear/misc/CSSBorder.hxx>
#include <osg/Geode>
#include <osg/Geometry>
namespace simgear
{
namespace canvas
{
class Window:
public Image,
public LayoutItem
{
public:
static const std::string TYPE_NAME;
enum Resize
{
NONE = 0,
LEFT = 1,
RIGHT = LEFT << 1,
TOP = RIGHT << 1,
BOTTOM = TOP << 1,
INIT = BOTTOM << 1
};
/**
* @param node Property node containing settings for this window:
* capture-events Disable/Enable event capturing
* content-size[0-1] Size of content area (excluding
* decoration border)
* decoration-border Size of decoration border
* resize Enable resize cursor and properties
* shadow-inset Inset of shadow image
* shadow-radius Radius/outset of shadow image
*/
Window( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& parent_style = Style(),
Element* parent = 0 );
virtual ~Window();
virtual void update(double delta_time_sec);
virtual void valueChanged(SGPropertyNode* node);
osg::Group* getGroup();
const SGVec2<float> getPosition() const;
const SGRect<float> getScreenRegion() const;
void setCanvasContent(CanvasPtr canvas);
simgear::canvas::CanvasWeakPtr getCanvasContent() const;
void setLayout(const LayoutRef& layout);
CanvasPtr getCanvasDecoration() const;
bool isResizable() const;
bool isCapturingEvents() const;
virtual void setVisible(bool visible);
virtual bool isVisible() const;
/**
* Moves window on top of all other windows with the same z-index.
*
* @note If no z-index is set it defaults to 0.
*/
void raise();
void handleResize( uint8_t mode,
const osg::Vec2f& offset = osg::Vec2f() );
protected:
enum Attributes
{
DECORATION = 1
};
uint32_t _attributes_dirty;
CanvasPtr _canvas_decoration;
CanvasWeakPtr _canvas_content;
LayoutRef _layout;
ImagePtr _image_content,
_image_shadow;
bool _resizable,
_capture_events;
PropertyObject<int> _resize_top,
_resize_right,
_resize_bottom,
_resize_left,
_resize_status;
CSSBorder _decoration_border;
void parseDecorationBorder(const std::string& str);
void updateDecoration();
};
} // namespace canvas
} // namespace simgear
#endif /* CANVAS_WINDOW_HXX_ */

View File

@@ -1,5 +1,4 @@
///@file
/// Mouse event
// Mouse event
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -20,24 +19,33 @@
#ifndef CANVAS_MOUSE_EVENT_HXX_
#define CANVAS_MOUSE_EVENT_HXX_
#include "DeviceEvent.hxx"
#include "CanvasEvent.hxx"
#include <osgGA/GUIEventAdapter>
namespace simgear
{
namespace canvas
{
/**
* Mouse (button/move/wheel) event
*/
class MouseEvent:
public DeviceEvent
public Event
{
public:
MouseEvent();
MouseEvent(const osgGA::GUIEventAdapter& ea);
MouseEvent():
button(-1),
state(-1),
mod(-1),
click_count(0)
{}
virtual bool canBubble() const;
MouseEvent(const osgGA::GUIEventAdapter& ea):
button(ea.getButton()),
state(ea.getButtonMask()),
mod(ea.getModKeyMask()),
click_count(0)
{
time = ea.getTime();
}
osg::Vec2f getScreenPos() const { return screen_pos; }
osg::Vec2f getClientPos() const { return client_pos; }
@@ -56,18 +64,16 @@ namespace canvas
float getDeltaX() const { return delta.x(); }
float getDeltaY() const { return delta.y(); }
int getButton() const { return button; }
int getButtonMask() const { return buttons; }
int getCurrentClickCount() const { return click_count; }
osg::Vec2f screen_pos, //!< Position in screen coordinates
client_pos, //!< Position in window/canvas coordinates
local_pos, //!< Position in local/element coordinates
osg::Vec2f screen_pos, //<! Position in screen coordinates
client_pos, //<! Position in window/canvas coordinates
local_pos, //<! Position in local/element coordinates
delta;
int button, //!< Button for this event
buttons, //!< Current button state
click_count; //!< Current click count
int button, //<! Button for this event
state, //<! Current button state
mod, //<! Keyboard modifier state
click_count; //<! Current click count
};
} // namespace canvas

View File

@@ -44,7 +44,6 @@
#include <osg/ShadeModel>
#include <osg/StateSet>
#include <osg/FrameBufferObject> // for GL_DEPTH_STENCIL_EXT on Windows
#include <osg/Version>
#include <osgUtil/RenderBin>
#include <cassert>
@@ -267,15 +266,7 @@ namespace canvas
//----------------------------------------------------------------------------
void ODGauge::reinit()
{
osg::NodeCallback* cull_callback =
camera
#if OSG_VERSION_LESS_THAN(3,3,2)
? camera->getCullCallback()
#else
? dynamic_cast<osg::NodeCallback*>(camera->getCullCallback())
#endif
: 0;
osg::NodeCallback* cull_callback = camera ? camera->getCullCallback() : 0;
clear();
allocRT(cull_callback);
}

View File

@@ -38,9 +38,4 @@
#endif
// We currently do not support using images (inside paths). If we were going to
// use it loading and unloading needs to happen within OpenSceneGraph to handle
// synchronization correctly in multithreading mode.
#define SH_NO_IMAGE
#endif // __SHCONFIG_H

View File

@@ -53,7 +53,7 @@ VG_API_CALL VGboolean vgCreateContextSH(VGint width, VGint height)
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, width, 0, height, -1, 1);
gluOrtho2D(0,width,0,height);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();*/
@@ -79,7 +79,7 @@ VG_API_CALL void vgResizeSurfaceSH(VGint width, VGint height)
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, width, 0, height, -1, 1);
gluOrtho2D(0,width,0,height);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

View File

@@ -34,7 +34,7 @@
#include <math.h>
#include <float.h>
#if !defined(VG_API_MACOSX) && !defined(__FreeBSD__)
#ifndef VG_API_MACOSX
# include <malloc.h>
#endif
@@ -156,16 +156,20 @@ SHfloat getMaxFloat();
/* OpenGL headers */
#if defined(VG_API_LINUX) || defined(VG_API_FREEBSD)
#if defined(VG_API_LINUX)
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glx.h>
#elif defined(VG_API_MACOSX)
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#elif defined(VG_API_WINDOWS)
#include <GL/gl.h>
#include <GL/glu.h>
#else
#define GL_GLEXT_LEGACY /* don't include glext.h */
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glx.h>
#endif

View File

@@ -37,7 +37,6 @@
#define _ARRAY_DEFINE
#include "shArrayBase.h"
#ifndef SH_NO_IMAGE
/*-----------------------------------------------------------
* Prepares the proper pixel pack/unpack info for the given
@@ -441,7 +440,6 @@ void shLoadColor(SHColor *c, const void *data, SHImageFormatDesc *f)
if (f->rmask == 0x0) { c->r = 1.0f; c->g = 1.0f; c->b = 1.0f; }
}
#endif // SH_NO_IMAGE
/*----------------------------------------------
* Color and Image constructors and destructors
@@ -463,28 +461,18 @@ void SHImage_ctor(SHImage *i)
i->data = NULL;
i->width = 0;
i->height = 0;
#ifdef SH_NO_IMAGE
printf("ShivaVG: images not supported!");
#else
glGenTextures(1, &i->texture);
#endif
}
void SHImage_dtor(SHImage *i)
{
if (i->data != NULL)
free(i->data);
#ifdef SH_NO_IMAGE
printf("ShivaVG: images not supported!");
#else
if (glIsTexture(i->texture))
glDeleteTextures(1, &i->texture);
#endif
}
#ifndef SH_NO_IMAGE
/*--------------------------------------------------------
* Finds appropriate OpenGL texture size for the size of
* the given image
@@ -565,7 +553,6 @@ void shUpdateImageTexture(SHImage *i, VGContext *c)
i->texwidth, i->texheight, 0,
i->fd.glformat, i->fd.gltype, i->data);
}
#endif // SH_NO_IMAGE
/*----------------------------------------------------------
* Creates a new image object and returns the handle to it
@@ -575,10 +562,6 @@ VG_API_CALL VGImage vgCreateImage(VGImageFormat format,
VGint width, VGint height,
VGbitfield allowedQuality)
{
#ifdef SH_NO_IMAGE
printf("ShivaVG: images not supported!");
return VG_INVALID_HANDLE;
#else
SHImage *i = NULL;
SHImageFormatDesc fd;
VG_GETCONTEXT(VG_INVALID_HANDLE);
@@ -631,16 +614,12 @@ VG_API_CALL VGImage vgCreateImage(VGImageFormat format,
/* Add to resource list */
shImageArrayPushBack(&context->images, i);
VG_RETURN((VGImage)i);
#endif // SH_NO_IMAGE
}
VG_API_CALL void vgDestroyImage(VGImage image)
{
#ifdef SH_NO_IMAGE
printf("ShivaVG: images not supported!");
#else
SHint index;
VG_GETCONTEXT(VG_NO_RETVAL);
@@ -651,9 +630,8 @@ VG_API_CALL void vgDestroyImage(VGImage image)
/* Delete object and remove resource */
SH_DELETEOBJ(SHImage, (SHImage*)image);
shImageArrayRemoveAt(&context->images, index);
VG_RETURN(VG_NO_RETVAL);
#endif // SH_NO_IMAGE
}
/*---------------------------------------------------
@@ -664,9 +642,6 @@ VG_API_CALL void vgDestroyImage(VGImage image)
VG_API_CALL void vgClearImage(VGImage image,
VGint x, VGint y, VGint width, VGint height)
{
#ifdef SH_NO_IMAGE
printf("ShivaVG: images not supported!");
#else
SHImage *i;
SHColor clear;
SHuint8 *data;
@@ -707,13 +682,9 @@ VG_API_CALL void vgClearImage(VGImage image,
}}
shUpdateImageTexture(i, context);
VG_RETURN(VG_NO_RETVAL);
#endif // SH_NO_IMAGE
}
#ifndef SH_NO_IMAGE
/*------------------------------------------------------------
* Generic function for copying a rectangle area of pixels
* of size (width,height) among two data buffers. The size of
@@ -824,8 +795,6 @@ void shCopyPixels(SHuint8 *dst, VGImageFormat dstFormat, SHint dstStride,
}
}
#endif // SH_NO_IMAGE
/*---------------------------------------------------------
* Copies a rectangle area of pixels of size (width,height)
* from given data buffer to image surface at destination
@@ -837,9 +806,6 @@ VG_API_CALL void vgImageSubData(VGImage image,
VGImageFormat dataFormat,
VGint x, VGint y, VGint width, VGint height)
{
#ifdef SH_NO_IMAGE
printf("ShivaVG: images not supported!");
#else
SHImage *i;
VG_GETCONTEXT(VG_NO_RETVAL);
@@ -870,9 +836,7 @@ VG_API_CALL void vgImageSubData(VGImage image,
x, y, 0, 0, width, height);
shUpdateImageTexture(i, context);
VG_RETURN(VG_NO_RETVAL);
#endif // SH_NO_IMAGE
}
/*---------------------------------------------------------
@@ -887,9 +851,6 @@ VG_API_CALL void vgGetImageSubData(VGImage image,
VGint x, VGint y,
VGint width, VGint height)
{
#ifdef SH_NO_IMAGE
printf("ShivaVG: images not supported!");
#else
SHImage *i;
VG_GETCONTEXT(VG_NO_RETVAL);
@@ -917,10 +878,9 @@ VG_API_CALL void vgGetImageSubData(VGImage image,
shCopyPixels(data, dataFormat, dataStride,
i->data, i->fd.vgformat, i->texwidth * i->fd.bytes,
width, height, i->width, i->height,
0,0,x,y,width,height);
0,0,x,x,width,height);
VG_RETURN(VG_NO_RETVAL);
#endif // SH_NO_IMAGE
}
/*----------------------------------------------------------
@@ -934,9 +894,6 @@ VG_API_CALL void vgCopyImage(VGImage dst, VGint dx, VGint dy,
VGint width, VGint height,
VGboolean dither)
{
#ifdef SH_NO_IMAGE
printf("ShivaVG: images not supported!");
#else
SHImage *s, *d;
SHuint8 *pixels;
@@ -976,9 +933,7 @@ VG_API_CALL void vgCopyImage(VGImage dst, VGint dx, VGint dy,
free(pixels);
shUpdateImageTexture(d, context);
VG_RETURN(VG_NO_RETVAL);
#endif // SH_NO_IMAGE
}
/*---------------------------------------------------------
@@ -991,9 +946,6 @@ VG_API_CALL void vgSetPixels(VGint dx, VGint dy,
VGImage src, VGint sx, VGint sy,
VGint width, VGint height)
{
#ifdef SH_NO_IMAGE
printf("ShivaVG: images not supported!");
#else
SHImage *i;
SHuint8 *pixels;
SHImageFormatDesc winfd;
@@ -1034,7 +986,6 @@ VG_API_CALL void vgSetPixels(VGint dx, VGint dy,
free(pixels);
VG_RETURN(VG_NO_RETVAL);
#endif // SH_NO_IMAGE
}
/*---------------------------------------------------------
@@ -1048,9 +999,6 @@ VG_API_CALL void vgWritePixels(const void * data, VGint dataStride,
VGint dx, VGint dy,
VGint width, VGint height)
{
#ifdef SH_NO_IMAGE
printf("ShivaVG: images not supported!");
#else
SHuint8 *pixels;
SHImageFormatDesc winfd;
@@ -1095,8 +1043,7 @@ VG_API_CALL void vgWritePixels(const void * data, VGint dataStride,
free(pixels);
VG_RETURN(VG_NO_RETVAL);
#endif // SH_NO_IMAGE
VG_RETURN(VG_NO_RETVAL);
}
/*-----------------------------------------------------------
@@ -1109,9 +1056,6 @@ VG_API_CALL void vgGetPixels(VGImage dst, VGint dx, VGint dy,
VGint sx, VGint sy,
VGint width, VGint height)
{
#ifdef SH_NO_IMAGE
printf("ShivaVG: images not supported!");
#else
SHImage *i;
SHuint8 *pixels;
SHImageFormatDesc winfd;
@@ -1149,9 +1093,7 @@ VG_API_CALL void vgGetPixels(VGImage dst, VGint dx, VGint dy,
free(pixels);
shUpdateImageTexture(i, context);
VG_RETURN(VG_NO_RETVAL);
#endif // SH_NO_IMAGE
}
/*-----------------------------------------------------------
@@ -1165,9 +1107,6 @@ VG_API_CALL void vgReadPixels(void * data, VGint dataStride,
VGint sx, VGint sy,
VGint width, VGint height)
{
#ifdef SH_NO_IMAGE
printf("ShivaVG: images not supported!");
#else
SHuint8 *pixels;
SHImageFormatDesc winfd;
VG_GETCONTEXT(VG_NO_RETVAL);
@@ -1207,9 +1146,8 @@ VG_API_CALL void vgReadPixels(void * data, VGint dataStride,
0, 0, 0, 0, width, height);
free(pixels);
VG_RETURN(VG_NO_RETVAL);
#endif // SH_NO_IMAGE
}
/*----------------------------------------------------------
@@ -1222,9 +1160,6 @@ VG_API_CALL void vgCopyPixels(VGint dx, VGint dy,
VGint sx, VGint sy,
VGint width, VGint height)
{
#ifdef SH_NO_IMAGE
printf("ShivaVG: images not supported!");
#else
VG_GETCONTEXT(VG_NO_RETVAL);
VG_RETURN_ERR_IF(width <= 0 || height <= 0,
@@ -1235,9 +1170,8 @@ VG_API_CALL void vgCopyPixels(VGint dx, VGint dy,
glRasterPos2i(dx, dy);
glCopyPixels(sx, sy, width, height, GL_COLOR);
glRasterPos2i(0, 0);
VG_RETURN(VG_NO_RETVAL);
#endif // SH_NO_IMAGE
}
VG_API_CALL VGImage vgChildImage(VGImage parent,

View File

@@ -36,9 +36,6 @@
#define _ARRAY_DEFINE
#include "shArrayBase.h"
// We currently do not use gradients which need textures, so disable them to
// prevent freeing resources outside the correct OpenGL thread/context.
#define SH_NO_PAINT_TEXTURE
void SHPaint_ctor(SHPaint *p)
{
@@ -54,26 +51,20 @@ void SHPaint_ctor(SHPaint *p)
for (i=0; i<4; ++i) p->linearGradient[i] = 0.0f;
for (i=0; i<5; ++i) p->radialGradient[i] = 0.0f;
p->pattern = VG_INVALID_HANDLE;
#ifndef SH_NO_PAINT_TEXTURE
glGenTextures(1, &p->texture);
glBindTexture(GL_TEXTURE_1D, p->texture);
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, SH_GRADIENT_TEX_SIZE, 0,
GL_RGBA, GL_FLOAT, NULL);
#else
p->texture = 0;
#endif
}
void SHPaint_dtor(SHPaint *p)
{
SH_DEINITOBJ(SHStopArray, p->instops);
SH_DEINITOBJ(SHStopArray, p->stops);
#ifndef SH_NO_PAINT_TEXTURE
if (glIsTexture(p->texture))
glDeleteTextures(1, &p->texture);
#endif
}
VG_API_CALL VGPaint vgCreatePaint(void)
@@ -152,7 +143,6 @@ VG_API_CALL void vgPaintPattern(VGPaint paint, VGImage pattern)
void shUpdateColorRampTexture(SHPaint *p)
{
#ifndef SH_NO_PAINT_TEXTURE
SHint s=0;
SHStop *stop1, *stop2;
SHfloat rgba[SH_GRADIENT_TEX_COORDSIZE];
@@ -187,15 +177,12 @@ void shUpdateColorRampTexture(SHPaint *p)
CSTORE_RGBA1D_F(c, rgba, x);
}
}
/* Update texture image */
glBindTexture(GL_TEXTURE_1D, p->texture);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexSubImage1D(GL_TEXTURE_1D, 0, 0, SH_GRADIENT_TEX_SIZE,
GL_RGBA, GL_FLOAT, rgba);
#else
printf("ShivaVG: gradients not supported!");
#endif
}
void shValidateInputStops(SHPaint *p)
@@ -357,7 +344,6 @@ void shGenerateStops(SHPaint *p, SHfloat minOffset, SHfloat maxOffset,
void shSetGradientTexGLState(SHPaint *p)
{
#ifndef SH_NO_PAINT_TEXTURE
glBindTexture(GL_TEXTURE_1D, p->texture);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@@ -373,9 +359,6 @@ void shSetGradientTexGLState(SHPaint *p)
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glColor4f(1,1,1,1);
#else
printf("ShivaVG: gradients not supported!");
#endif
}
void shSetPatternTexGLState(SHPaint *p, VGContext *c)

View File

@@ -314,9 +314,9 @@ VG_API_CALL void vgDrawPath(VGPath path, VGbitfield paintModes)
}
/* TODO: Turn antialiasing on/off */
// glDisable(GL_LINE_SMOOTH);
// glDisable(GL_POLYGON_SMOOTH);
// glEnable(GL_MULTISAMPLE);
glDisable(GL_LINE_SMOOTH);
glDisable(GL_POLYGON_SMOOTH);
glEnable(GL_MULTISAMPLE);
/* Pick paint if available or default*/
fill = (context->fillPaint ? context->fillPaint : &context->defaultPaint);
@@ -364,27 +364,26 @@ VG_API_CALL void vgDrawPath(VGPath path, VGbitfield paintModes)
/* TODO: Is there any way to do this safely along
with the paint generation pass?? */
glDisable(GL_BLEND);
// glDisable(GL_MULTISAMPLE);
glDisable(GL_MULTISAMPLE);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
shDrawBoundBox(context, p, VG_FILL_PATH);
/* Reset state */
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDisable(GL_STENCIL_TEST);
// glDisable(GL_BLEND);
glDisable(GL_BLEND);
}
/* TODO: Turn antialiasing on/off */
// glDisable(GL_LINE_SMOOTH);
// glDisable(GL_POLYGON_SMOOTH);
// glEnable(GL_MULTISAMPLE);
glDisable(GL_LINE_SMOOTH);
glDisable(GL_POLYGON_SMOOTH);
glEnable(GL_MULTISAMPLE);
if ((paintModes & VG_STROKE_PATH) &&
context->strokeLineWidth > 0.0f) {
#if 0
if (1) {/*context->strokeLineWidth > 1.0f) {*/
#endif
if (shIsStrokeCacheValid( context, p ) == VG_FALSE)
{
/* Generate stroke triangles in user space */
@@ -412,15 +411,15 @@ VG_API_CALL void vgDrawPath(VGPath path, VGbitfield paintModes)
/* Clear stencil for sure */
glDisable(GL_BLEND);
// glDisable(GL_MULTISAMPLE);
glDisable(GL_MULTISAMPLE);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
shDrawBoundBox(context, p, VG_STROKE_PATH);
/* Reset state */
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDisable(GL_STENCIL_TEST);
// glDisable(GL_BLEND);
#if 0
glDisable(GL_BLEND);
}else{
/* Simulate thin stroke by alpha */
@@ -439,7 +438,6 @@ VG_API_CALL void vgDrawPath(VGPath path, VGbitfield paintModes)
glDisable(GL_BLEND);
glDisable(GL_LINE_SMOOTH);
}
#endif
}

View File

@@ -124,13 +124,11 @@ int shLineLineXsection(SHVector2 *o1, SHVector2 *v1,
SHfloat DX = rightU * (-v2->y) - rightD * (-v2->x);
/*SHfloat DY = v1.x * rightD - v1.y * rightU;*/
SHfloat t1;
SHfloat t1 = DX / D;
if (D == 0.0f)
return 0;
t1 = DX / D;
xsection->x = o1->x + t1*v1->x;
xsection->y = o1->y + t1*v1->y;
return 1;

View File

@@ -39,8 +39,8 @@ namespace canvas
#define SG_FWD_DECL(name)\
class name;\
typedef SGSharedPtr<name> name##Ptr;\
typedef SGWeakPtr<name> name##WeakPtr;
typedef boost::shared_ptr<name> name##Ptr;\
typedef boost::weak_ptr<name> name##WeakPtr;
SG_FWD_DECL(Canvas)
SG_FWD_DECL(Element)
@@ -49,21 +49,9 @@ namespace canvas
SG_FWD_DECL(Map)
SG_FWD_DECL(Path)
SG_FWD_DECL(Text)
SG_FWD_DECL(Window)
SG_FWD_DECL(Event)
SG_FWD_DECL(CustomEvent)
SG_FWD_DECL(DeviceEvent)
SG_FWD_DECL(KeyboardEvent)
SG_FWD_DECL(MouseEvent)
#undef SG_FWD_DECL
#define SG_FWD_DECL(name)\
class name;\
typedef boost::shared_ptr<name> name##Ptr;\
typedef boost::weak_ptr<name> name##WeakPtr;
SG_FWD_DECL(Placement)
SG_FWD_DECL(SystemAdapter)
@@ -72,9 +60,6 @@ namespace canvas
class EventManager;
class EventVisitor;
struct EventTarget;
typedef std::deque<EventTarget> EventPropagationPath;
typedef std::map<std::string, const SGPropertyNode*> Style;
typedef ElementPtr (*ElementFactory)( const CanvasWeakPtr&,
const SGPropertyNode_ptr&,

View File

@@ -23,9 +23,4 @@ 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
LIBRARIES ${TEST_LIBS}
)
simgear_component(canvas-elements/detail canvas/elements/detail "" "${DETAIL_HEADERS}")

View File

@@ -17,17 +17,15 @@
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "CanvasElement.hxx"
#include <simgear/canvas/Canvas.hxx>
#include <simgear/canvas/CanvasEventVisitor.hxx>
#include <simgear/canvas/events/MouseEvent.hxx>
#include <simgear/canvas/MouseEvent.hxx>
#include <simgear/math/SGMisc.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/scene/material/parseBlendFunc.hxx>
#include <osg/Drawable>
#include <osg/Geode>
#include <osg/StateAttribute>
#include <osg/Version>
#include <osg/Scissor>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/foreach.hpp>
@@ -47,79 +45,19 @@ namespace canvas
* glScissor with coordinates relative to different reference frames.
*/
class Element::RelativeScissor:
public osg::StateAttribute
public osg::Scissor
{
public:
ReferenceFrame _coord_reference;
osg::observer_ptr<osg::Node> _node;
ReferenceFrame _coord_reference;
osg::Matrix _parent_inverse;
explicit RelativeScissor(osg::Node* node = NULL):
_coord_reference(GLOBAL),
_node(node),
_x(0),
_y(0),
_width(0),
_height(0)
{
}
/** Copy constructor using CopyOp to manage deep vs shallow copy. */
RelativeScissor( const RelativeScissor& vp,
const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY ):
StateAttribute(vp, copyop),
_coord_reference(vp._coord_reference),
_node(vp._node),
_x(vp._x),
_y(vp._y),
_width(vp._width),
_height(vp._height)
RelativeScissor():
_coord_reference(GLOBAL)
{}
META_StateAttribute(simgear, RelativeScissor, SCISSOR);
/** Return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs. */
virtual int compare(const StateAttribute& sa) const
{
// check the types are equal and then create the rhs variable
// used by the COMPARE_StateAttribute_Parameter macros below.
COMPARE_StateAttribute_Types(RelativeScissor,sa)
// compare each parameter in turn against the rhs.
COMPARE_StateAttribute_Parameter(_x)
COMPARE_StateAttribute_Parameter(_y)
COMPARE_StateAttribute_Parameter(_width)
COMPARE_StateAttribute_Parameter(_height)
COMPARE_StateAttribute_Parameter(_coord_reference)
COMPARE_StateAttribute_Parameter(_node)
return 0; // passed all the above comparison macros, must be equal.
}
virtual bool getModeUsage(StateAttribute::ModeUsage& usage) const
{
usage.usesMode(GL_SCISSOR_TEST);
return true;
}
inline float& x() { return _x; }
inline float x() const { return _x; }
inline float& y() { return _y; }
inline float y() const { return _y; }
inline float& width() { return _width; }
inline float width() const { return _width; }
inline float& height() { return _height; }
inline float height() const { return _height; }
virtual void apply(osg::State& state) const
{
if( _width <= 0 || _height <= 0 )
return;
const osg::Viewport* vp = state.getCurrentViewport();
float w2 = 0.5 * vp->width(),
h2 = 0.5 * vp->height();
@@ -135,28 +73,17 @@ namespace canvas
if( _coord_reference != GLOBAL )
{
osg::Node* ref_obj = _node.get();
model_view.preMult(state.getModelViewMatrix());
if( _coord_reference == PARENT )
{
if( _node->getNumParents() < 1 )
{
SG_LOG(SG_GL, SG_WARN, "RelativeScissor: missing parent.");
return;
}
ref_obj = _node->getParent(0);
}
osg::MatrixList const& parent_matrices = ref_obj->getWorldMatrices();
assert( !parent_matrices.empty() );
model_view.preMult(parent_matrices.front());
model_view.preMult(_parent_inverse);
}
const osg::Vec2 scale( model_view(0,0), model_view(1,1)),
offset(model_view(3,0), model_view(3,1));
// TODO check/warn for rotation?
GLint x = SGMiscf::roundToInt(scale.x() * _x + offset.x()),
y = SGMiscf::roundToInt(scale.y() * _y + offset.y()),
w = SGMiscf::roundToInt(std::fabs(scale.x()) * _width),
@@ -169,32 +96,6 @@ namespace canvas
glScissor(x, y, w, h);
}
bool contains(const osg::Vec2f& pos) const
{
return _x <= pos.x() && pos.x() <= _x + _width
&& _y <= pos.y() && pos.y() <= _y + _height;
}
bool contains( const osg::Vec2f& global_pos,
const osg::Vec2f& parent_pos,
const osg::Vec2f& local_pos ) const
{
switch( _coord_reference )
{
case GLOBAL: return contains(global_pos);
case PARENT: return contains(parent_pos);
case LOCAL: return contains(local_pos);
}
return false;
}
protected:
float _x,
_y,
_width,
_height;
};
//----------------------------------------------------------------------------
@@ -207,6 +108,30 @@ namespace canvas
//----------------------------------------------------------------------------
Element::~Element()
{
if( !_transform.valid() )
return;
for(unsigned int i = 0; i < _transform->getNumChildren(); ++i)
{
OSGUserData* ud =
static_cast<OSGUserData*>(_transform->getChild(i)->getUserData());
if( ud )
// Ensure parent is cleared to prevent accessing released memory if an
// element somehow survives longer than his parent.
ud->element->_parent = 0;
}
}
//----------------------------------------------------------------------------
void Element::setSelf(const PropertyBasedElementPtr& self)
{
PropertyBasedElement::setSelf(self);
_transform->setUserData
(
new OSGUserData(boost::static_pointer_cast<Element>(self))
);
}
//----------------------------------------------------------------------------
@@ -221,41 +146,43 @@ namespace canvas
{
parent->removeChild(_transform.get());
}
// Hide in case someone still holds a reference
setVisible(false);
removeListener();
_parent = 0;
_transform = 0;
}
//----------------------------------------------------------------------------
ElementPtr Element::getParent() const
ElementWeakPtr Element::getWeakPtr() const
{
return _parent.lock();
return boost::static_pointer_cast<Element>(_self.lock());
}
//----------------------------------------------------------------------------
CanvasWeakPtr Element::getCanvas() const
ElementPtr Element::getParent()
{
return _canvas;
return _parent ? _parent->getWeakPtr().lock() : ElementPtr();
}
//----------------------------------------------------------------------------
void Element::update(double dt)
{
if( !isVisible() )
if( !_transform->getNodeMask() )
// Don't do anything if element is hidden
return;
// Trigger matrix update
getMatrix();
if( _attributes_dirty & SCISSOR_COORDS )
{
if( _scissor && _scissor->_coord_reference != GLOBAL )
_scissor->_parent_inverse = _transform->getInverseMatrix();
_attributes_dirty &= ~SCISSOR_COORDS;
}
// Update bounding box on manual update (manual updates pass zero dt)
if( dt == 0 && _drawable )
_drawable->getBound();
if( (_attributes_dirty & BLEND_FUNC) && _transform.valid() )
if( _attributes_dirty & BLEND_FUNC )
{
parseBlendFunc(
_transform->getOrCreateStateSet(),
@@ -281,7 +208,17 @@ namespace canvas
"addEventListener(" << _node->getPath() << ", " << type_str << ")"
);
_listener[ Event::getOrRegisterType(type_str) ].push_back(cb);
Event::Type type = Event::strToType(type_str);
if( type == Event::UNKNOWN )
{
SG_LOG( SG_GENERAL,
SG_WARN,
"addEventListener: Unknown event type " << type_str );
return false;
}
_listener[ type ].push_back(cb);
return true;
}
@@ -291,14 +228,6 @@ namespace canvas
_listener.clear();
}
//----------------------------------------------------------------------------
void Element::setFocus()
{
CanvasPtr canvas = _canvas.lock();
if( canvas )
canvas->setFocusElement(this);
}
//----------------------------------------------------------------------------
bool Element::accept(EventVisitor& visitor)
{
@@ -311,9 +240,8 @@ namespace canvas
//----------------------------------------------------------------------------
bool Element::ascend(EventVisitor& visitor)
{
ElementPtr parent = getParent();
if( parent )
return parent->accept(visitor);
if( _parent )
return _parent->accept(visitor);
return true;
}
@@ -324,89 +252,30 @@ namespace canvas
}
//----------------------------------------------------------------------------
size_t Element::numEventHandler(int type) const
{
ListenerMap::const_iterator listeners = _listener.find(type);
if( listeners != _listener.end() )
return listeners->second.size();
return 0;
}
//----------------------------------------------------------------------------
bool Element::handleEvent(const EventPtr& event)
bool Element::handleEvent(canvas::EventPtr event)
{
ListenerMap::iterator listeners = _listener.find(event->getType());
if( listeners == _listener.end() )
return false;
BOOST_FOREACH(EventListener const& listener, listeners->second)
try
{
listener(event);
}
catch( std::exception const& ex )
{
SG_LOG(
SG_GENERAL,
SG_WARN,
"canvas::Element: event handler error: '" << ex.what() << "'"
);
}
listener(event);
return true;
}
//----------------------------------------------------------------------------
bool Element::dispatchEvent(const EventPtr& event)
{
EventPropagationPath path;
path.push_back( EventTarget(this) );
for( ElementPtr parent = getParent();
parent.valid();
parent = parent->getParent() )
path.push_front( EventTarget(parent) );
CanvasPtr canvas = _canvas.lock();
if( !canvas )
return false;
return canvas->propagateEvent(event, path);
}
//----------------------------------------------------------------------------
bool Element::hitBound( const osg::Vec2f& global_pos,
const osg::Vec2f& parent_pos,
bool Element::hitBound( const osg::Vec2f& pos,
const osg::Vec2f& local_pos ) const
{
if( _scissor && !_scissor->contains(global_pos, parent_pos, local_pos) )
return false;
const osg::Vec3f pos3(parent_pos, 0);
const osg::Vec3f pos3(pos, 0);
// Drawables have a bounding box...
if( _drawable )
return _drawable->
#if OSG_VERSION_LESS_THAN(3,3,2)
getBound()
#else
getBoundingBox()
#endif
.contains(osg::Vec3f(local_pos, 0));
else if( _transform.valid() )
// ... for other elements, i.e. groups only a bounding sphere is available
return _transform->getBound().contains(osg::Vec3f(parent_pos, 0));
return _drawable->getBound().contains(osg::Vec3f(local_pos, 0));
// ... for other elements, i.e. groups only a bounding sphere is available
else
return false;
}
//----------------------------------------------------------------------------
void Element::setVisible(bool visible)
{
if( _transform.valid() )
// TODO check if we need another nodemask
_transform->setNodeMask(visible ? 0xffffffff : 0);
return _transform->getBound().contains(osg::Vec3f(pos, 0));
}
//----------------------------------------------------------------------------
@@ -427,18 +296,6 @@ namespace canvas
return _transform.get();
}
//----------------------------------------------------------------------------
osg::Vec2f Element::posToLocal(const osg::Vec2f& pos) const
{
getMatrix();
const osg::Matrix& m = _transform->getInverseMatrix();
return osg::Vec2f
(
m(0, 0) * pos[0] + m(1, 0) * pos[1] + m(3, 0),
m(0, 1) * pos[0] + m(1, 1) * pos[1] + m(3, 1)
);
}
//----------------------------------------------------------------------------
void Element::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
{
@@ -523,9 +380,7 @@ namespace canvas
if( parent == _node )
{
const std::string& name = child->getNameString();
if( boost::starts_with(name, "data-") )
return;
else if( StyleInfo const* style_info = getStyleInfo(name) )
if( StyleInfo const* style_info = getStyleInfo(name) )
{
SGPropertyNode const* style = child;
if( isStyleEmpty(child) )
@@ -538,11 +393,13 @@ namespace canvas
}
else if( name == "update" )
return update(0);
else if( name == "visible" )
// TODO check if we need another nodemask
return _transform->setNodeMask( child->getBoolValue() ? 0xffffffff : 0 );
else if( boost::starts_with(name, "blend-") )
return (void)(_attributes_dirty |= BLEND_FUNC);
}
else if( parent
&& parent->getParent() == _node
else if( parent->getParent() == _node
&& parent->getNameString() == NAME_TRANSFORM )
{
_attributes_dirty |= TRANSFORM;
@@ -562,13 +419,9 @@ namespace canvas
//----------------------------------------------------------------------------
void Element::setClip(const std::string& clip)
{
osg::StateSet* ss = getOrCreateStateSet();
if( !ss )
return;
if( clip.empty() || clip == "auto" )
{
ss->removeAttribute(osg::StateAttribute::SCISSOR);
getOrCreateStateSet()->removeAttribute(osg::StateAttribute::SCISSOR);
_scissor = 0;
return;
}
@@ -615,53 +468,47 @@ namespace canvas
return;
}
if( !_scissor )
_scissor = new RelativeScissor(_transform.get());
_scissor = new RelativeScissor();
// <top>, <right>, <bottom>, <left>
_scissor->x() = values[3];
_scissor->y() = values[0];
_scissor->width() = width;
_scissor->height() = height;
_scissor->x() = SGMiscf::roundToInt(values[3]);
_scissor->y() = SGMiscf::roundToInt(values[0]);
_scissor->width() = SGMiscf::roundToInt(width);
_scissor->height() = SGMiscf::roundToInt(height);
getOrCreateStateSet()->setAttributeAndModes(_scissor);
SGPropertyNode* clip_frame = _node->getChild("clip-frame", 0);
if( clip_frame )
valueChanged(clip_frame);
else
_scissor->_coord_reference = GLOBAL;
ss->setAttributeAndModes(_scissor);
}
//----------------------------------------------------------------------------
void Element::setClipFrame(ReferenceFrame rf)
{
if( _scissor )
{
_scissor->_coord_reference = rf;
_attributes_dirty |= SCISSOR_COORDS;
}
}
//----------------------------------------------------------------------------
osg::BoundingBox Element::getBoundingBox() const
void Element::setBoundingBox(const osg::BoundingBox& bb)
{
if( _drawable )
#if OSG_VERSION_LESS_THAN(3,3,2)
return _drawable->getBound();
#else
return _drawable->getBoundingBox();
#endif
if( _bounding_box.empty() )
{
SGPropertyNode* bb_node = _node->getChild("bounding-box", 0, true);
_bounding_box.resize(4);
_bounding_box[0] = bb_node->getChild("min-x", 0, true);
_bounding_box[1] = bb_node->getChild("min-y", 0, true);
_bounding_box[2] = bb_node->getChild("max-x", 0, true);
_bounding_box[3] = bb_node->getChild("max-y", 0, true);
}
osg::BoundingBox bb;
if( _transform.valid() )
bb.expandBy(_transform->getBound());
return bb;
}
//----------------------------------------------------------------------------
osg::BoundingBox Element::getTightBoundingBox() const
{
return getTransformedBounds(getMatrix());
_bounding_box[0]->setFloatValue(bb._min.x());
_bounding_box[1]->setFloatValue(bb._min.y());
_bounding_box[2]->setFloatValue(bb._max.x());
_bounding_box[3]->setFloatValue(bb._max.y());
}
//----------------------------------------------------------------------------
@@ -671,13 +518,7 @@ namespace canvas
return osg::BoundingBox();
osg::BoundingBox transformed;
const osg::BoundingBox& bb =
#if OSG_VERSION_LESS_THAN(3,3,2)
_drawable->getBound();
#else
_drawable->getBoundingBox();
#endif
const osg::BoundingBox& bb = _drawable->getBound();
for(int i = 0; i < 4; ++i)
transformed.expandBy( bb.corner(i) * m );
@@ -687,9 +528,6 @@ namespace canvas
//----------------------------------------------------------------------------
osg::Matrix Element::getMatrix() const
{
if( !_transform )
return osg::Matrix::identity();
if( !(_attributes_dirty & TRANSFORM) )
return _transform->getMatrix();
@@ -749,6 +587,7 @@ namespace canvas
}
_transform->setMatrix(m);
_attributes_dirty &= ~TRANSFORM;
_attributes_dirty |= SCISSOR_COORDS;
return m;
}
@@ -760,7 +599,7 @@ namespace canvas
Element::Element( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& parent_style,
ElementWeakPtr parent ):
Element* parent ):
PropertyBasedElement(node),
_canvas( canvas ),
_parent( parent ),
@@ -787,8 +626,6 @@ namespace canvas
"PreOrderBin",
osg::StateSet::OVERRIDE_RENDERBIN_DETAILS
);
_transform->setUserData( new OSGUserData(this) );
}
//----------------------------------------------------------------------------
@@ -799,7 +636,6 @@ namespace canvas
addStyle("clip", "", &Element::setClip, false);
addStyle("clip-frame", "", &Element::setClipFrame, false);
addStyle("visible", "", &Element::setVisible, false);
}
//----------------------------------------------------------------------------
@@ -859,12 +695,11 @@ namespace canvas
Element::getParentStyle(const SGPropertyNode* child) const
{
// Try to get value from parent...
ElementPtr parent = getParent();
if( parent )
if( _parent )
{
Style::const_iterator style =
parent->_style.find(child->getNameString());
if( style != parent->_style.end() )
_parent->_style.find(child->getNameString());
if( style != _parent->_style.end() )
return style->second;
}
@@ -886,12 +721,8 @@ namespace canvas
//----------------------------------------------------------------------------
osg::StateSet* Element::getOrCreateStateSet()
{
if( _drawable.valid() )
return _drawable->getOrCreateStateSet();
if( _transform.valid() )
return _transform->getOrCreateStateSet();
return 0;
return _drawable ? _drawable->getOrCreateStateSet()
: _transform->getOrCreateStateSet();
}
//----------------------------------------------------------------------------

View File

@@ -1,5 +1,4 @@
///@file
/// Interface for 2D Canvas elements
// Interface for 2D Canvas elements
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -42,9 +41,6 @@ namespace simgear
namespace canvas
{
/**
* Base class for Elements displayed inside a Canvas.
*/
class Element:
public PropertyBasedElement
{
@@ -73,10 +69,10 @@ namespace canvas
};
struct StyleInfo
{
StyleSetter setter; ///< Function(s) for setting this style
std::string type; ///< Interpolation type
bool inheritable; ///< Whether children can inherit this style from
/// their parents
StyleSetter setter; ///!< Function(s) for setting this style
std::string type; ///!< Interpolation type
bool inheritable; ///!< Whether children can inherit this style from
/// their parents
};
/**
@@ -84,20 +80,22 @@ namespace canvas
*/
enum ReferenceFrame
{
GLOBAL, ///< Global coordinates
PARENT, ///< Coordinates relative to parent coordinate frame
LOCAL ///< Coordinates relative to local coordinates (parent
/// coordinates with local transformations applied)
GLOBAL, ///!< Global coordinates
PARENT, ///!< Coordinates relative to parent coordinate frame
LOCAL ///!< Coordinates relative to local coordinates (parent
/// coordinates with local transformations applied)
};
/**
*
*/
virtual ~Element() = 0;
virtual void setSelf(const PropertyBasedElementPtr& self);
virtual void onDestroy();
ElementPtr getParent() const;
CanvasWeakPtr getCanvas() const;
ElementWeakPtr getWeakPtr() const;
ElementPtr getParent();
/**
* Called every frame to update internal state
@@ -109,47 +107,24 @@ namespace canvas
bool addEventListener(const std::string& type, const EventListener& cb);
virtual void clearEventListener();
/// Get (keyboard) input focus.
void setFocus();
virtual bool accept(EventVisitor& visitor);
virtual bool ascend(EventVisitor& visitor);
virtual bool traverse(EventVisitor& visitor);
/// Get the number of event handlers for the given type
size_t numEventHandler(int type) const;
virtual bool handleEvent(canvas::EventPtr event);
virtual bool handleEvent(const EventPtr& event);
bool dispatchEvent(const EventPtr& event);
/**
*
* @param global_pos Position in global (canvas) coordinate frame
* @param parent_pos Position in parent coordinate frame
* @param local_pos Position in local (element) coordinate frame
*/
virtual bool hitBound( const osg::Vec2f& global_pos,
const osg::Vec2f& parent_pos,
virtual bool hitBound( const osg::Vec2f& pos,
const osg::Vec2f& local_pos ) const;
/**
* Set visibility of the element.
* Get whether the element is visible or hidden (Can be changed with
* setting property "visible" accordingly).
*/
virtual void setVisible(bool visible);
/**
* Get whether the element is visible or hidden.
*/
virtual bool isVisible() const;
bool isVisible() const;
osg::MatrixTransform* getMatrixTransform();
osg::MatrixTransform const* getMatrixTransform() const;
/**
* Transform position to local coordinages.
*/
osg::Vec2f posToLocal(const osg::Vec2f& pos) const;
virtual void childAdded( SGPropertyNode * parent,
SGPropertyNode * child );
virtual void childRemoved( SGPropertyNode * parent,
@@ -173,16 +148,9 @@ namespace canvas
void setClipFrame(ReferenceFrame rf);
/**
* Get bounding box (may not be as tight as bounding box returned by
* #getTightBoundingBox)
* Write the given bounding box to the property tree
*/
osg::BoundingBox getBoundingBox() const;
/**
* Get tight bounding box (child points are transformed to elements
* coordinate space before calculating the bounding box).
*/
osg::BoundingBox getTightBoundingBox() const;
void setBoundingBox(const osg::BoundingBox& bb);
/**
* Get bounding box with children/drawables transformed by passed matrix
@@ -206,10 +174,12 @@ namespace canvas
ElementPtr
>::type create( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& style = Style(),
Element* parent = NULL )
const Style& style,
Element* parent )
{
return ElementPtr( new Derived(canvas, node, style, parent) );
ElementPtr el( new Derived(canvas, node, style, parent) );
el->setSelf(el);
return el;
}
protected:
@@ -218,7 +188,8 @@ namespace canvas
{
TRANSFORM = 1,
BLEND_FUNC = TRANSFORM << 1,
LAST_ATTRIBUTE = BLEND_FUNC << 1
SCISSOR_COORDS = BLEND_FUNC << 1,
LAST_ATTRIBUTE = SCISSOR_COORDS << 1
};
enum TransformType
@@ -232,31 +203,32 @@ namespace canvas
class RelativeScissor;
CanvasWeakPtr _canvas;
ElementWeakPtr _parent;
CanvasWeakPtr _canvas;
Element *_parent;
mutable uint32_t _attributes_dirty;
osg::observer_ptr<osg::MatrixTransform> _transform;
std::vector<TransformType> _transform_types;
Style _style;
RelativeScissor *_scissor;
Style _style;
std::vector<SGPropertyNode_ptr> _bounding_box;
RelativeScissor *_scissor;
typedef std::vector<EventListener> Listener;
typedef std::map<int, Listener> ListenerMap;
typedef std::map<Event::Type, Listener> ListenerMap;
ListenerMap _listener;
typedef std::map<std::string, StyleInfo> StyleSetters;
static StyleSetters _style_setters;
static StyleSetters _style_setters;
static void staticInit();
Element( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& parent_style,
ElementWeakPtr parent );
Element* parent );
/**
* Returns false on first call and true on any successive call. Use to
@@ -279,10 +251,9 @@ namespace canvas
/**
* Register a function for setting a style specified by the given property
*
* @param name Property name
* @param type Interpolation type
* @param setter Setter function
* @param inheritable If this style propagates to child elements
* @param name Property name
* @param type Interpolation type
* @param setter Setter function
*
* @tparam T1 Type of value used to retrieve value from property
* node
@@ -564,7 +535,7 @@ namespace canvas
/**
* Get stateset of drawable if available or use transform otherwise
*/
virtual osg::StateSet* getOrCreateStateSet();
osg::StateSet* getOrCreateStateSet();
void setupStyle();

View File

@@ -22,7 +22,7 @@
#include "CanvasPath.hxx"
#include "CanvasText.hxx"
#include <simgear/canvas/CanvasEventVisitor.hxx>
#include <simgear/canvas/events/MouseEvent.hxx>
#include <simgear/canvas/MouseEvent.hxx>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
@@ -70,7 +70,7 @@ namespace canvas
Group::Group( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& parent_style,
ElementWeakPtr parent ):
Element* parent ):
Element(canvas, node, parent_style, parent)
{
staticInit();
@@ -146,7 +146,7 @@ namespace canvas
if( el->get<std::string>("id") == id )
return el;
Group* group = dynamic_cast<Group*>(el.get());
GroupPtr group = boost::dynamic_pointer_cast<Group>(el);
if( group )
groups.push_back(group);
}
@@ -320,12 +320,10 @@ namespace canvas
//----------------------------------------------------------------------------
void Group::childChanged(SGPropertyNode* node)
{
SGPropertyNode* parent = node->getParent();
SGPropertyNode* grand_parent = parent ? parent->getParent() : NULL;
if( grand_parent == _node
if( node->getParent()->getParent() == _node
&& node->getNameString() == "z-index" )
return handleZIndexChanged(getChild(parent), node->getIntValue());
return handleZIndexChanged( getChild(node->getParent()),
node->getIntValue() );
}
//----------------------------------------------------------------------------

View File

@@ -45,7 +45,7 @@ namespace canvas
Group( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& parent_style = Style(),
ElementWeakPtr parent = 0 );
Element* parent = 0 );
virtual ~Group();
ElementPtr createChild( const std::string& type,
@@ -56,27 +56,28 @@ namespace canvas
const std::string& id );
template<class T>
SGSharedPtr<T> createChild(const std::string& id = "")
boost::shared_ptr<T> createChild(const std::string& id = "")
{
return dynamic_cast<T*>( createChild(T::TYPE_NAME, id).get() );
return boost::dynamic_pointer_cast<T>( createChild(T::TYPE_NAME, id) );
}
template<class T>
SGSharedPtr<T> getChild(const SGPropertyNode* node)
boost::shared_ptr<T> getChild(const SGPropertyNode* node)
{
return dynamic_cast<T*>( getChild(node).get() );
return boost::dynamic_pointer_cast<T>( getChild(node) );
}
template<class T>
SGSharedPtr<T> getChild(const std::string& id)
boost::shared_ptr<T> getChild(const std::string& id)
{
return dynamic_cast<T*>( getChild(id).get() );
return boost::dynamic_pointer_cast<T>( getChild(id) );
}
template<class T>
SGSharedPtr<T> getOrCreateChild(const std::string& id)
boost::shared_ptr<T> getOrCreateChild(const std::string& id)
{
return dynamic_cast<T*>( getOrCreateChild(T::TYPE_NAME, id).get() );
return
boost::dynamic_pointer_cast<T>( getOrCreateChild(T::TYPE_NAME, id) );
}
/**

View File

@@ -21,8 +21,7 @@
#include <simgear/canvas/Canvas.hxx>
#include <simgear/canvas/CanvasMgr.hxx>
#include <simgear/canvas/CanvasSystemAdapter.hxx>
#include <simgear/canvas/events/KeyboardEvent.hxx>
#include <simgear/canvas/events/MouseEvent.hxx>
#include <simgear/canvas/MouseEvent.hxx>
#include <simgear/scene/util/OsgMath.hxx>
#include <simgear/scene/util/parse_color.hxx>
#include <simgear/misc/sg_path.hxx>
@@ -30,8 +29,8 @@
#include <osg/Array>
#include <osg/Geometry>
#include <osg/PrimitiveSet>
#include <osgDB/Registry>
#include <osg/Version>
#include <boost/algorithm/string/predicate.hpp>
namespace simgear
{
@@ -76,9 +75,8 @@ namespace canvas
osg::Drawable* drawable,
osg::RenderInfo* renderInfo ) const
{
CanvasPtr canvas = _canvas.lock();
if( canvas )
canvas->enableRendering();
if( !_canvas.expired() )
_canvas.lock()->enableRendering();
if( !_cull_next_frame )
// TODO check if window/image should be culled
@@ -98,21 +96,16 @@ namespace canvas
return;
addStyle("fill", "color", &Image::setFill);
addStyle("outset", "", &Image::setOutset);
addStyle("preserveAspectRatio", "", &Image::setPreserveAspectRatio);
addStyle("slice", "", &Image::setSlice);
addStyle("slice-width", "", &Image::setSliceWidth);
osgDB::Registry* reg = osgDB::Registry::instance();
if( !reg->getReaderWriterForExtension("png") )
SG_LOG(SG_GL, SG_ALERT, "canvas::Image: Missing 'png' image reader");
addStyle("outset", "", &Image::setOutset);
}
//----------------------------------------------------------------------------
Image::Image( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& parent_style,
ElementWeakPtr parent ):
Element* parent ):
Element(canvas, node, parent_style, parent),
_texture(new osg::Texture2D),
_node_src_rect( node->getNode("source", 0, true) ),
@@ -135,11 +128,12 @@ namespace canvas
_texCoords = new osg::Vec2Array(4);
_texCoords->setDataVariance(osg::Object::DYNAMIC);
_geom->setTexCoordArray(0, _texCoords, osg::Array::BIND_PER_VERTEX);
_geom->setTexCoordArray(0, _texCoords);
_colors = new osg::Vec4Array(1);
_colors->setDataVariance(osg::Object::DYNAMIC);
_geom->setColorArray(_colors, osg::Array::BIND_OVERALL);
_geom->setColorArray(_colors);
_geom->setColorBinding(osg::Geometry::BIND_OVERALL);
_prim = new osg::DrawArrays(osg::PrimitiveSet::QUADS);
_prim->set(osg::PrimitiveSet::QUADS, 0, 4);
@@ -155,8 +149,7 @@ namespace canvas
//----------------------------------------------------------------------------
Image::~Image()
{
if( _http_request )
_http_request->abort("image destroyed");
}
//----------------------------------------------------------------------------
@@ -221,10 +214,6 @@ namespace canvas
if( !_slice.isValid() )
{
setQuad(0, region.getMin(), region.getMax());
if( !_preserve_aspect_ratio.scaleToFill() )
// We need to update texture coordinates to keep the aspect ratio
_attributes_dirty |= SRC_RECT;
}
else
{
@@ -283,6 +272,7 @@ namespace canvas
_vertices->dirty();
_attributes_dirty &= ~DEST_SIZE;
_geom->dirtyBound();
setBoundingBox(_geom->getBound());
}
if( _attributes_dirty & SRC_RECT )
@@ -301,66 +291,6 @@ namespace canvas
if( !_slice.isValid() )
{
// Image scaling preserving aspect ratio. Change texture coordinates to
// scale image accordingly.
//
// TODO allow to specify what happens to not filled space (eg. color,
// or texture repeat/mirror)
//
// http://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
if( !_preserve_aspect_ratio.scaleToFill() )
{
osg::BoundingBox const& bb = getBoundingBox();
float dst_width = bb._max.x() - bb._min.x(),
dst_height = bb._max.y() - bb._min.y();
float scale_x = dst_width / tex_dim.width(),
scale_y = dst_height / tex_dim.height();
float scale = _preserve_aspect_ratio.scaleToFit()
? std::min(scale_x, scale_y)
: std::max(scale_x, scale_y);
if( scale_x != scale )
{
float d = scale_x / scale - 1;
if( _preserve_aspect_ratio.alignX()
== SVGpreserveAspectRatio::ALIGN_MIN )
{
src_rect.r() += d;
}
else if( _preserve_aspect_ratio.alignX()
== SVGpreserveAspectRatio::ALIGN_MAX )
{
src_rect.l() -= d;
}
else
{
src_rect.l() -= d / 2;
src_rect.r() += d / 2;
}
}
if( scale_y != scale )
{
float d = scale_y / scale - 1;
if( _preserve_aspect_ratio.alignY()
== SVGpreserveAspectRatio::ALIGN_MIN )
{
src_rect.b() -= d;
}
else if( _preserve_aspect_ratio.alignY()
== SVGpreserveAspectRatio::ALIGN_MAX )
{
src_rect.t() += d;
}
else
{
src_rect.t() += d / 2;
src_rect.b() -= d / 2;
}
}
}
setQuadUV(0, src_rect.getMin(), src_rect.getMax());
}
else
@@ -414,14 +344,7 @@ namespace canvas
&& 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() );
CullCallback* cb = static_cast<CullCallback*>(_geom->getCullCallback());
if( cb )
cb->cullNextFrame();
}
@@ -491,20 +414,6 @@ namespace canvas
_colors->dirty();
}
//----------------------------------------------------------------------------
void Image::setOutset(const std::string& outset)
{
_outset = CSSBorder::parse(outset);
_attributes_dirty |= DEST_SIZE;
}
//----------------------------------------------------------------------------
void Image::setPreserveAspectRatio(const std::string& scale)
{
_preserve_aspect_ratio = SVGpreserveAspectRatio::parse(scale);
_attributes_dirty |= SRC_RECT;
}
//----------------------------------------------------------------------------
void Image::setSlice(const std::string& slice)
{
@@ -519,6 +428,13 @@ namespace canvas
_attributes_dirty |= DEST_SIZE;
}
//----------------------------------------------------------------------------
void Image::setOutset(const std::string& outset)
{
_outset = CSSBorder::parse(outset);
_attributes_dirty |= DEST_SIZE;
}
//----------------------------------------------------------------------------
const SGRect<float>& Image::getRegion() const
{
@@ -526,7 +442,7 @@ namespace canvas
}
//----------------------------------------------------------------------------
bool Image::handleEvent(const EventPtr& event)
bool Image::handleEvent(EventPtr event)
{
bool handled = Element::handleEvent(event);
@@ -534,9 +450,11 @@ namespace canvas
if( !src_canvas )
return handled;
if( MouseEventPtr mouse_event = dynamic_cast<MouseEvent*>(event.get()) )
MouseEventPtr mouse_event = boost::dynamic_pointer_cast<MouseEvent>(event);
if( mouse_event )
{
mouse_event.reset( new MouseEvent(*mouse_event) );
event = mouse_event;
mouse_event->client_pos = mouse_event->local_pos
- toOsg(_region.getMin());
@@ -556,16 +474,9 @@ namespace canvas
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;
return handled | src_canvas->handleMouseEvent(mouse_event);
}
//----------------------------------------------------------------------------
@@ -610,41 +521,20 @@ namespace canvas
_attributes_dirty |= DEST_SIZE;
}
else if( name == "src" || name == "file" )
else if( name == "file" )
{
if( name == "file" )
SG_LOG(SG_GL, SG_WARN, "'file' is deprecated. Use 'src' instead");
static const std::string CANVAS_PROTOCOL = "canvas://";
const std::string& path = child->getStringValue();
// Abort pending request
if( _http_request )
CanvasPtr canvas = _canvas.lock();
if( !canvas )
{
_http_request->abort("setting new image");
_http_request.reset();
SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No canvas available");
return;
}
static const std::string PROTOCOL_SEP = "://";
std::string url = child->getStringValue(),
protocol, path;
size_t sep_pos = url.find(PROTOCOL_SEP);
if( sep_pos != std::string::npos )
if( boost::starts_with(path, CANVAS_PROTOCOL) )
{
protocol = url.substr(0, sep_pos);
path = url.substr(sep_pos + PROTOCOL_SEP.length());
}
else
path = url;
if( protocol == "canvas" )
{
CanvasPtr canvas = _canvas.lock();
if( !canvas )
{
SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No canvas available");
return;
}
CanvasMgr* canvas_mgr = canvas->getCanvasMgr();
if( !canvas_mgr )
{
@@ -655,7 +545,7 @@ namespace canvas
const SGPropertyNode* canvas_node =
canvas_mgr->getPropertyRoot()
->getParent()
->getNode( path );
->getNode( path.substr(CANVAS_PROTOCOL.size()) );
if( !canvas_node )
{
SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No such canvas: " << path);
@@ -673,16 +563,6 @@ namespace canvas
setSrcCanvas(src_canvas);
}
else if( protocol == "http" || protocol == "https" )
// TODO check https
{
_http_request =
Canvas::getSystemAdapter()
->getHTTPClient()
->load(url)
// TODO handle capture of 'this'
->done(this, &Image::handleImageLoadDone);
}
else
{
setImage( Canvas::getSystemAdapter()->getImage(path) );
@@ -758,65 +638,5 @@ namespace canvas
(*_texCoords)[i + 3].set(tl.x(), br.y());
}
//----------------------------------------------------------------------------
void Image::handleImageLoadDone(HTTP::Request* req)
{
// Ignore stale/expired requests
if( _http_request != req )
return;
_http_request.reset();
if( req->responseCode() != 200 )
{
SG_LOG(SG_IO, SG_WARN, "failed to download '" << req->url() << "': "
<< req->responseReason());
return;
}
const std::string ext = SGPath(req->path()).extension(),
mime = req->responseMime();
SG_LOG(SG_IO, SG_INFO, "received " << req->url() <<
" (ext=" << ext << ", MIME=" << mime << ")");
const std::string& img_data =
static_cast<HTTP::MemoryRequest*>(req)->responseBody();
osgDB::Registry* reg = osgDB::Registry::instance();
// First try to detect image type by extension
osgDB::ReaderWriter* rw = reg->getReaderWriterForExtension(ext);
if( rw && loadImage(*rw, img_data, *req, "extension") )
return;
// Now try with MIME type
rw = reg->getReaderWriterForMimeType(mime);
if( rw && loadImage(*rw, img_data, *req, "MIME type") )
return;
SG_LOG(SG_IO, SG_WARN, "unable to read image '" << req->url() << "'");
}
//----------------------------------------------------------------------------
bool Image::loadImage( osgDB::ReaderWriter& reader,
const std::string& data,
HTTP::Request& request,
const std::string& type )
{
SG_LOG(SG_IO, SG_DEBUG, "use image reader detected by " << type);
std::istringstream data_strm(data);
osgDB::ReaderWriter::ReadResult result = reader.readImage(data_strm);
if( result.success() )
{
setImage( result.takeImage() );
return true;
}
SG_LOG(SG_IO, SG_WARN, "failed to read image '" << request.url() << "': "
<< result.message());
return false;
}
} // namespace canvas
} // namespace simgear

View File

@@ -22,14 +22,11 @@
#include "CanvasElement.hxx"
#include <simgear/canvas/canvas_fwd.hxx>
#include <simgear/io/HTTPClient.hxx>
#include <simgear/misc/CSSBorder.hxx>
#include <simgear/misc/SVGpreserveAspectRatio.hxx>
#include <osg/Texture2D>
namespace simgear
{
namespace HTTP { class Request; }
namespace canvas
{
@@ -50,7 +47,7 @@ namespace canvas
Image( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& parent_style = Style(),
ElementWeakPtr parent = 0 );
Element* parent = 0 );
virtual ~Image();
virtual void update(double dt);
@@ -62,17 +59,6 @@ namespace canvas
void setImage(osg::Image *img);
void setFill(const std::string& fill);
/**
* @see http://www.w3.org/TR/css3-background/#border-image-outset
*/
void setOutset(const std::string& outset);
/**
* @see
* http://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
*/
void setPreserveAspectRatio(const std::string& scale);
/**
* Set image slice (aka. 9-scale)
*
@@ -91,9 +77,14 @@ namespace canvas
*/
void setSliceWidth(const std::string& width);
/**
* http://www.w3.org/TR/css3-background/#border-image-outset
*/
void setOutset(const std::string& outset);
const SGRect<float>& getRegion() const;
bool handleEvent(const EventPtr& event);
bool handleEvent(EventPtr event);
protected:
@@ -112,16 +103,9 @@ namespace canvas
void setQuad(size_t index, const SGVec2f& tl, const SGVec2f& br);
void setQuadUV(size_t index, const SGVec2f& tl, const SGVec2f& br);
void handleImageLoadDone(HTTP::Request*);
bool loadImage( osgDB::ReaderWriter& reader,
const std::string& data,
HTTP::Request& request,
const std::string& type );
osg::ref_ptr<osg::Texture2D> _texture;
// TODO optionally forward events to canvas
CanvasWeakPtr _src_canvas;
HTTP::Request_ptr _http_request;
osg::ref_ptr<osg::Geometry> _geom;
osg::ref_ptr<osg::DrawArrays>_prim;
@@ -133,11 +117,9 @@ namespace canvas
SGRect<float> _src_rect,
_region;
SVGpreserveAspectRatio _preserve_aspect_ratio;
CSSBorder _outset,
_slice,
_slice_width;
CSSBorder _slice,
_slice_width,
_outset;
};
} // namespace canvas

View File

@@ -44,7 +44,6 @@ namespace canvas
//----------------------------------------------------------------------------
const std::string GEO = "-geo";
const std::string HDG = "hdg";
const std::string Map::TYPE_NAME = "map";
//----------------------------------------------------------------------------
@@ -62,7 +61,7 @@ namespace canvas
Map::Map( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& parent_style,
ElementWeakPtr parent ):
Element* parent ):
Group(canvas, node, parent_style, parent),
// TODO make projection configurable
_projection(new SansonFlamsteedProjection),
@@ -113,50 +112,87 @@ namespace canvas
//----------------------------------------------------------------------------
void Map::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
{
if( boost::ends_with(child->getNameString(), GEO) )
_geo_nodes[child].reset(new GeoNodePair());
else if( parent != _node && child->getNameString() == HDG )
_hdg_nodes.insert(child);
else
if( !boost::ends_with(child->getNameString(), GEO) )
return Element::childAdded(parent, child);
_geo_nodes[child].reset(new GeoNodePair());
}
//----------------------------------------------------------------------------
void Map::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
{
if( boost::ends_with(child->getNameString(), GEO) )
// TODO remove from other node
_geo_nodes.erase(child);
else if( parent != _node && child->getName() == HDG )
{
_hdg_nodes.erase(child);
// Remove rotation matrix (tf[0]) and return to element always being
// oriented upwards (or any orientation inside other matrices).
child->getParent()->removeChild("tf", 0);
}
else
if( !boost::ends_with(child->getNameString(), GEO) )
return Element::childRemoved(parent, child);
// TODO remove from other node
_geo_nodes.erase(child);
}
//----------------------------------------------------------------------------
void Map::valueChanged(SGPropertyNode* child)
void Map::valueChanged(SGPropertyNode * child)
{
if( child->getParent() != _node )
const std::string& name = child->getNameString();
if( !boost::ends_with(name, GEO) )
return Group::valueChanged(child);
GeoNodes::iterator it_geo_node = _geo_nodes.find(child);
if( it_geo_node == _geo_nodes.end() )
LOG_GEO_RET("geo node not found!")
GeoNodePair* geo_node = it_geo_node->second.get();
geo_node->setDirty();
if( geo_node->getStatus() & GeoNodePair::INCOMPLETE )
{
const std::string& name = child->getNameString();
// Detect lat, lon tuples...
GeoCoord coord = parseGeoCoord(child->getStringValue());
int index_other = -1;
if( boost::ends_with(name, GEO) )
return geoNodeChanged(child);
else if( name == HDG )
return hdgNodeChanged(child);
switch( coord.type )
{
case GeoCoord::LATITUDE:
index_other = child->getIndex() + 1;
geo_node->setNodeLat(child);
break;
case GeoCoord::LONGITUDE:
index_other = child->getIndex() - 1;
geo_node->setNodeLon(child);
break;
default:
LOG_GEO_RET("Invalid geo coord")
}
SGPropertyNode *other = child->getParent()->getChild(name, index_other);
if( !other )
return;
GeoCoord coord_other = parseGeoCoord(other->getStringValue());
if( coord_other.type == GeoCoord::INVALID
|| coord_other.type == coord.type )
return;
GeoNodes::iterator it_geo_node_other = _geo_nodes.find(other);
if( it_geo_node_other == _geo_nodes.end() )
LOG_GEO_RET("other geo node not found!")
GeoNodePair* geo_node_other = it_geo_node_other->second.get();
// Let use both nodes use the same GeoNodePair instance
if( geo_node_other != geo_node )
it_geo_node_other->second = it_geo_node->second;
if( coord_other.type == GeoCoord::LATITUDE )
geo_node->setNodeLat(other);
else
geo_node->setNodeLon(other);
// Set name for resulting screen coordinate nodes
geo_node->setTargetName( name.substr(0, name.length() - GEO.length()) );
}
return Group::valueChanged(child);
}
//----------------------------------------------------------------------------
void Map::childChanged(SGPropertyNode* child)
void Map::childChanged(SGPropertyNode * child)
{
if( child->getParent() != _node )
return Group::childChanged(child);
@@ -165,14 +201,8 @@ namespace canvas
|| child->getNameString() == "ref-lon" )
_projection->setWorldPosition( _node->getDoubleValue("ref-lat"),
_node->getDoubleValue("ref-lon") );
else if( child->getNameString() == HDG )
{
else if( child->getNameString() == "hdg" )
_projection->setOrientation(child->getFloatValue());
for( NodeSet::iterator it = _hdg_nodes.begin();
it != _hdg_nodes.end();
++it )
hdgNodeChanged(*it);
}
else if( child->getNameString() == "range" )
_projection->setRange(child->getDoubleValue());
else if( child->getNameString() == "screen-range" )
@@ -183,74 +213,6 @@ namespace canvas
_projection_dirty = true;
}
//----------------------------------------------------------------------------
void Map::geoNodeChanged(SGPropertyNode* child)
{
GeoNodes::iterator it_geo_node = _geo_nodes.find(child);
if( it_geo_node == _geo_nodes.end() )
LOG_GEO_RET("GeoNode not found!")
GeoNodePair* geo_node = it_geo_node->second.get();
geo_node->setDirty();
if( !(geo_node->getStatus() & GeoNodePair::INCOMPLETE) )
return;
// Detect lat, lon tuples...
GeoCoord coord = parseGeoCoord(child->getStringValue());
int index_other = -1;
switch( coord.type )
{
case GeoCoord::LATITUDE:
index_other = child->getIndex() + 1;
geo_node->setNodeLat(child);
break;
case GeoCoord::LONGITUDE:
index_other = child->getIndex() - 1;
geo_node->setNodeLon(child);
break;
default:
LOG_GEO_RET("Invalid geo coord")
}
const std::string& name = child->getNameString();
SGPropertyNode *other = child->getParent()->getChild(name, index_other);
if( !other )
return;
GeoCoord coord_other = parseGeoCoord(other->getStringValue());
if( coord_other.type == GeoCoord::INVALID
|| coord_other.type == coord.type )
return;
GeoNodes::iterator it_geo_node_other = _geo_nodes.find(other);
if( it_geo_node_other == _geo_nodes.end() )
LOG_GEO_RET("other geo node not found!")
GeoNodePair* geo_node_other = it_geo_node_other->second.get();
// Let use both nodes use the same GeoNodePair instance
if( geo_node_other != geo_node )
it_geo_node_other->second = it_geo_node->second;
if( coord_other.type == GeoCoord::LATITUDE )
geo_node->setNodeLat(other);
else
geo_node->setNodeLon(other);
// Set name for resulting screen coordinate nodes
geo_node->setTargetName( name.substr(0, name.length() - GEO.length()) );
}
//----------------------------------------------------------------------------
void Map::hdgNodeChanged(SGPropertyNode* child)
{
child->getParent()->setFloatValue(
"tf[0]/rot",
SGMiscf::deg2rad(child->getFloatValue() - _projection->orientation())
);
}
//----------------------------------------------------------------------------
Map::GeoCoord Map::parseGeoCoord(const std::string& val) const
{

View File

@@ -24,7 +24,6 @@
#include <boost/shared_ptr.hpp>
#include <boost/unordered_map.hpp>
#include <boost/unordered_set.hpp>
namespace simgear
{
@@ -42,7 +41,7 @@ namespace canvas
Map( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& parent_style,
ElementWeakPtr parent = 0 );
Element* parent = 0 );
virtual ~Map();
virtual void update(double dt);
@@ -60,18 +59,14 @@ namespace canvas
typedef boost::unordered_map< SGPropertyNode*,
boost::shared_ptr<GeoNodePair>
> GeoNodes;
typedef boost::unordered_set<SGPropertyNode*> NodeSet;
GeoNodes _geo_nodes;
NodeSet _hdg_nodes;
boost::shared_ptr<HorizontalProjection> _projection;
bool _projection_dirty;
struct GeoCoord
{
GeoCoord():
type(INVALID),
value(0)
type(INVALID)
{}
enum
{
@@ -82,9 +77,6 @@ namespace canvas
double value;
};
void geoNodeChanged(SGPropertyNode * child);
void hdgNodeChanged(SGPropertyNode * child);
GeoCoord parseGeoCoord(const std::string& val) const;
};

View File

@@ -20,7 +20,6 @@
#include <simgear/scene/util/parse_color.hxx>
#include <osg/Drawable>
#include <osg/Version>
#include <vg/openvg.h>
#include <cassert>
@@ -70,8 +69,7 @@ namespace canvas
_mode(0),
_fill_rule(VG_EVEN_ODD),
_stroke_width(1),
_stroke_linecap(VG_CAP_BUTT),
_stroke_linejoin(VG_JOIN_MITER)
_stroke_linecap(VG_CAP_BUTT)
{
setSupportsDisplayList(false);
setDataVariance(Object::DYNAMIC);
@@ -204,21 +202,6 @@ namespace canvas
_stroke_linecap = VG_CAP_BUTT;
}
/**
* Set stroke-linejoin
*
* @see http://www.w3.org/TR/SVG/painting.html#StrokeLinejoinProperty
*/
void setStrokeLinejoin(const std::string& linejoin)
{
if( linejoin == "round" )
_stroke_linejoin = VG_JOIN_ROUND;
else if( linejoin == "bevel" )
_stroke_linejoin = VG_JOIN_BEVEL;
else
_stroke_linejoin = VG_JOIN_MITER;
}
/**
* Draw callback
*/
@@ -234,10 +217,9 @@ namespace canvas
state->setClientActiveTextureUnit(0);
state->disableAllVertexArrays();
bool was_blend_enabled = state->getLastAppliedMode(GL_BLEND);
bool was_stencil_enabled = state->getLastAppliedMode(GL_STENCIL_TEST);
osg::StateAttribute const* blend_func =
state->getLastAppliedAttribute(osg::StateAttribute::BLENDFUNC);
glPushAttrib(~0u); // Don't use GL_ALL_ATTRIB_BITS as on my machine it
// eg. doesn't include GL_MULTISAMPLE_BIT
glPushClientAttrib(~0u);
// Initialize/Update the paint
if( _attributes_dirty & STROKE_COLOR )
@@ -268,7 +250,6 @@ namespace canvas
vgSetf(VG_STROKE_LINE_WIDTH, _stroke_width);
vgSeti(VG_STROKE_CAP_STYLE, _stroke_linecap);
vgSeti(VG_STROKE_JOIN_STYLE, _stroke_linejoin);
vgSetfv( VG_STROKE_DASH_PATTERN,
_stroke_dash.size(),
_stroke_dash.empty() ? 0 : &_stroke_dash[0] );
@@ -288,11 +269,8 @@ namespace canvas
if( err != VG_NO_ERROR )
SG_LOG(SG_GL, SG_ALERT, "vgError: " << err);
// Restore OpenGL state (TODO check if more is needed or integrate
// better with OpenSceneGraph)
if( was_blend_enabled ) glEnable(GL_BLEND);
if( was_stencil_enabled ) glEnable(GL_STENCIL_TEST);
if( blend_func ) blend_func->apply(*state);
glPopAttrib();
glPopClientAttrib();
}
osg::BoundingBox getTransformedBounds(const osg::Matrix& mat) const
@@ -390,13 +368,7 @@ namespace canvas
/**
* Compute the bounding box
*/
virtual osg::BoundingBox
#if OSG_VERSION_LESS_THAN(3,3,2)
computeBound()
#else
computeBoundingBox()
#endif
const
virtual osg::BoundingBox computeBound() const
{
if( _path == VG_INVALID_HANDLE || (_attributes_dirty & PATH) )
return osg::BoundingBox();
@@ -409,11 +381,14 @@ namespace canvas
// vgPathBounds doesn't take stroke width into account
float ext = 0.5 * _stroke_width;
return osg::BoundingBox
osg::BoundingBox bb
(
min[0] - ext, min[1] - ext, -0.1,
min[0] + size[0] + ext, min[1] + size[1] + ext, 0.1
);
_path_element->setBoundingBox(bb);
return bb;
}
private:
@@ -443,7 +418,6 @@ namespace canvas
VGfloat _stroke_width;
std::vector<VGfloat> _stroke_dash;
VGCapStyle _stroke_linecap;
VGJoinStyle _stroke_linejoin;
osg::Vec3f transformPoint( const osg::Matrix& m,
osg::Vec2f pos ) const
@@ -489,12 +463,7 @@ namespace canvas
}
if( _attributes_dirty & BOUNDING_BOX )
{
dirtyBound();
// Recalculate bounding box now (prevent race condition)
getBound();
}
}
struct PathUpdateCallback:
@@ -524,14 +493,13 @@ namespace canvas
addStyle("stroke-width", "numeric", &PathDrawable::setStrokeWidth, path);
addStyle("stroke-dasharray", "", &PathDrawable::setStrokeDashArray, path);
addStyle("stroke-linecap", "", &PathDrawable::setStrokeLinecap, path);
addStyle("stroke-linejoin", "", &PathDrawable::setStrokeLinejoin, path);
}
//----------------------------------------------------------------------------
Path::Path( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& parent_style,
ElementWeakPtr parent ):
Element* parent ):
Element(canvas, node, parent_style, parent),
_path( new PathDrawable(this) )
{

View File

@@ -36,7 +36,7 @@ namespace canvas
Path( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& parent_style,
ElementWeakPtr parent = 0 );
Element* parent = 0 );
virtual ~Path();
virtual void update(double dt);

View File

@@ -21,7 +21,6 @@
#include <simgear/canvas/CanvasSystemAdapter.hxx>
#include <simgear/scene/util/parse_color.hxx>
#include <osg/Version>
#include <osgDB/Registry>
#include <osgText/Text>
namespace simgear
@@ -32,199 +31,26 @@ namespace canvas
public osgText::Text
{
public:
TextOSG(canvas::Text* text);
void setFontResolution(int res);
void setCharacterAspect(float aspect);
void setLineHeight(float factor);
void setFill(const std::string& fill);
void setStroke(const std::string& color);
void setBackgroundColor(const std::string& fill);
float lineHeight() const;
osg::Vec2 handleHit(const osg::Vec2f& pos);
/// Get the number of lines
size_t lineCount() const;
/// Get line @a i
TextLine lineAt(size_t i) const;
/// Get nearest line to given y-coordinate
TextLine nearestLine(float pos_y) const;
SGVec2i sizeForWidth(int w) const;
virtual osg::BoundingBox
#if OSG_VERSION_LESS_THAN(3,3,2)
computeBound()
#else
computeBoundingBox()
#endif
const;
virtual osg::BoundingBox computeBound() const;
protected:
friend class TextLine;
canvas::Text *_text_element;
virtual void computePositions(unsigned int contextID) const;
};
class TextLine
{
public:
TextLine();
TextLine(size_t line, Text::TextOSG const* text);
/// Number of characters on this line
size_t size() const;
bool empty() const;
osg::Vec2 cursorPos(size_t i) const;
osg::Vec2 nearestCursor(float x) const;
protected:
typedef Text::TextOSG::GlyphQuads GlyphQuads;
Text::TextOSG const *_text;
GlyphQuads const *_quads;
size_t _line,
_begin,
_end;
};
//----------------------------------------------------------------------------
TextLine::TextLine():
_text(NULL),
_quads(NULL),
_line(0),
_begin(-1),
_end(-1)
{
}
//----------------------------------------------------------------------------
TextLine::TextLine(size_t line, Text::TextOSG const* text):
_text(text),
_quads(NULL),
_line(line),
_begin(-1),
_end(-1)
{
if( !text || text->_textureGlyphQuadMap.empty() || !_text->lineCount() )
return;
_quads = &text->_textureGlyphQuadMap.begin()->second;
GlyphQuads::LineNumbers const& line_numbers = _quads->_lineNumbers;
GlyphQuads::LineNumbers::const_iterator begin_it =
std::lower_bound(line_numbers.begin(), line_numbers.end(), _line);
if( begin_it == line_numbers.end() || *begin_it != _line )
// empty line or past last line
return;
_begin = begin_it - line_numbers.begin();
_end = std::upper_bound(begin_it, line_numbers.end(), _line)
- line_numbers.begin();
}
//----------------------------------------------------------------------------
size_t TextLine::size() const
{
return _end - _begin;
}
//----------------------------------------------------------------------------
bool TextLine::empty() const
{
return _end == _begin;
}
//----------------------------------------------------------------------------
osg::Vec2 TextLine::cursorPos(size_t i) const
{
if( !_quads )
return osg::Vec2(0, 0);
if( i > size() )
// Position after last character if out of range (TODO better exception?)
i = size();
osg::Vec2 pos(0, _text->_offset.y() + _line * _text->lineHeight());
if( empty() )
return pos;
#if OSG_VERSION_LESS_THAN(3,3,5)
GlyphQuads::Coords2 const& coords = _quads->_coords;
#else
GlyphQuads::Coords2 refCoords = _quads->_coords;
GlyphQuads::Coords2::element_type &coords = *refCoords.get();
#endif
size_t global_i = _begin + i;
if( global_i == _begin )
// before first character of line
pos.x() = coords[_begin * 4].x();
else if( global_i == _end )
// After Last character of line
pos.x() = coords[(_end - 1) * 4 + 2].x();
else
{
float prev_l = coords[(global_i - 1) * 4].x(),
prev_r = coords[(global_i - 1) * 4 + 2].x(),
cur_l = coords[global_i * 4].x();
if( prev_l == prev_r )
// If previous character width is zero set to begin of next character
// (Happens eg. with spaces)
pos.x() = cur_l;
else
// position at center between characters
pos.x() = 0.5 * (prev_r + cur_l);
}
return pos;
}
//----------------------------------------------------------------------------
osg::Vec2 TextLine::nearestCursor(float x) const
{
if( empty() )
return cursorPos(0);
GlyphQuads::Glyphs const& glyphs = _quads->_glyphs;
#if OSG_VERSION_LESS_THAN(3,3,5)
GlyphQuads::Coords2 const& coords = _quads->_coords;
#else
GlyphQuads::Coords2 refCoords = _quads->_coords;
GlyphQuads::Coords2::element_type &coords = *refCoords.get();
#endif
float const HIT_FRACTION = 0.6;
float const character_width = _text->getCharacterHeight()
* _text->getCharacterAspectRatio();
size_t i = _begin;
for(; i < _end; ++i)
{
// Get threshold for mouse x position for setting cursor before or after
// current character
float threshold = coords[i * 4].x()
+ HIT_FRACTION * glyphs[i]->getHorizontalAdvance()
* character_width;
if( x <= threshold )
break;
}
return cursorPos(i - _begin);
}
//----------------------------------------------------------------------------
Text::TextOSG::TextOSG(canvas::Text* text):
_text_element(text)
@@ -261,19 +87,6 @@ namespace canvas
setColor( color );
}
//----------------------------------------------------------------------------
void Text::TextOSG::setStroke(const std::string& stroke)
{
osg::Vec4 color;
if( stroke == "none" || !parseColor(stroke, color) )
setBackdropType(NONE);
else
{
setBackdropType(OUTLINE);
setBackdropColor(color);
}
}
//----------------------------------------------------------------------------
void Text::TextOSG::setBackgroundColor(const std::string& fill)
{
@@ -283,345 +96,101 @@ namespace canvas
}
//----------------------------------------------------------------------------
float Text::TextOSG::lineHeight() const
osg::Vec2 Text::TextOSG::handleHit(const osg::Vec2f& pos)
{
return (1 + _lineSpacing) * _characterHeight;
}
float line_height = _characterHeight + _lineSpacing;
//----------------------------------------------------------------------------
size_t Text::TextOSG::lineCount() const
{
return _lineCount;
}
// TODO check with align other than TOP
float first_line_y = -0.5 * _lineSpacing;//_offset.y() - _characterHeight;
size_t line = std::max<int>(0, (pos.y() - first_line_y) / line_height);
//----------------------------------------------------------------------------
TextLine Text::TextOSG::lineAt(size_t i) const
{
return TextLine(i, this);
}
if( _textureGlyphQuadMap.empty() )
return osg::Vec2(-1, -1);
//----------------------------------------------------------------------------
TextLine Text::TextOSG::nearestLine(float pos_y) const
{
osgText::Font const* font = getActiveFont();
if( !font || lineCount() <= 0 )
return TextLine(0, this);
// TODO check when it can be larger
assert( _textureGlyphQuadMap.size() == 1 );
float asc = .9f, desc = -.2f;
font->getVerticalSize(asc, desc);
const GlyphQuads& glyphquad = _textureGlyphQuadMap.begin()->second;
const GlyphQuads::Glyphs& glyphs = glyphquad._glyphs;
const GlyphQuads::Coords2& coords = glyphquad._coords;
const GlyphQuads::LineNumbers& line_numbers = glyphquad._lineNumbers;
float first_line_y = _offset.y()
- (1 + _lineSpacing / 2 + desc) * _characterHeight;
const float HIT_FRACTION = 0.6;
const float character_width = getCharacterHeight()
* getCharacterAspectRatio();
size_t line_num = std::min<size_t>(
std::max<size_t>(0, (pos_y - first_line_y) / lineHeight()),
lineCount() - 1
);
float y = (line + 0.5) * line_height;
return TextLine(line_num, this);
}
//----------------------------------------------------------------------------
// simplified version of osgText::Text::computeGlyphRepresentation() to
// just calculate the size for a given weight. Glpyh calculations/creating
// is not necessary for this...
SGVec2i Text::TextOSG::sizeForWidth(int w) const
{
if( _text.empty() )
return SGVec2i(0, 0);
osgText::Font* activefont = const_cast<osgText::Font*>(getActiveFont());
if( !activefont )
return SGVec2i(-1, -1);
float max_width_safe = _maximumWidth;
const_cast<TextOSG*>(this)->_maximumWidth = w;
SGRecti bb;
osg::Vec2 startOfLine_coords(0.0f,0.0f);
osg::Vec2 cursor(startOfLine_coords);
osg::Vec2 local(0.0f,0.0f);
unsigned int previous_charcode = 0;
unsigned int line_length = 0;
bool horizontal = _layout != VERTICAL;
bool kerning = true;
float hr = _characterHeight;
float wr = hr / getCharacterAspectRatio();
// osg should really care more about const :-/
osgText::String& text = const_cast<osgText::String&>(_text);
typedef osgText::String::iterator TextIterator;
for( TextIterator itr = text.begin(); itr != text.end(); )
bool line_found = false;
for(size_t i = 0; i < line_numbers.size(); ++i)
{
// record the start of the current line
TextIterator startOfLine_itr = itr;
// find the end of the current line.
osg::Vec2 endOfLine_coords(cursor);
TextIterator endOfLine_itr =
const_cast<TextOSG*>(this)->computeLastCharacterOnLine(
endOfLine_coords, itr, text.end()
);
line_length = endOfLine_itr - startOfLine_itr;
// Set line position to correct alignment.
switch( _layout )
if( line_numbers[i] != line )
{
case LEFT_TO_RIGHT:
if( !line_found )
{
switch( _alignment )
{
// nothing to be done for these
//case LEFT_TOP:
//case LEFT_CENTER:
//case LEFT_BOTTOM:
//case LEFT_BASE_LINE:
//case LEFT_BOTTOM_BASE_LINE:
// break;
case CENTER_TOP:
case CENTER_CENTER:
case CENTER_BOTTOM:
case CENTER_BASE_LINE:
case CENTER_BOTTOM_BASE_LINE:
cursor.x() = (cursor.x() - endOfLine_coords.x()) * 0.5f;
break;
case RIGHT_TOP:
case RIGHT_CENTER:
case RIGHT_BOTTOM:
case RIGHT_BASE_LINE:
case RIGHT_BOTTOM_BASE_LINE:
cursor.x() = cursor.x() - endOfLine_coords.x();
break;
default:
break;
}
break;
}
case RIGHT_TO_LEFT:
{
switch( _alignment )
{
case LEFT_TOP:
case LEFT_CENTER:
case LEFT_BOTTOM:
case LEFT_BASE_LINE:
case LEFT_BOTTOM_BASE_LINE:
cursor.x() = 2 * cursor.x() - endOfLine_coords.x();
break;
case CENTER_TOP:
case CENTER_CENTER:
case CENTER_BOTTOM:
case CENTER_BASE_LINE:
case CENTER_BOTTOM_BASE_LINE:
cursor.x() = cursor.x()
+ (cursor.x() - endOfLine_coords.x()) * 0.5f;
break;
// nothing to be done for these
//case RIGHT_TOP:
//case RIGHT_CENTER:
//case RIGHT_BOTTOM:
//case RIGHT_BASE_LINE:
//case RIGHT_BOTTOM_BASE_LINE:
// break;
default:
break;
}
break;
}
case VERTICAL:
{
switch( _alignment )
{
// TODO: current behaviour top baselines lined up in both cases - need to implement
// top of characters alignment - Question is this necessary?
// ... otherwise, nothing to be done for these 6 cases
//case LEFT_TOP:
//case CENTER_TOP:
//case RIGHT_TOP:
// break;
//case LEFT_BASE_LINE:
//case CENTER_BASE_LINE:
//case RIGHT_BASE_LINE:
// break;
case LEFT_CENTER:
case CENTER_CENTER:
case RIGHT_CENTER:
cursor.y() = cursor.y()
+ (cursor.y() - endOfLine_coords.y()) * 0.5f;
break;
case LEFT_BOTTOM_BASE_LINE:
case CENTER_BOTTOM_BASE_LINE:
case RIGHT_BOTTOM_BASE_LINE:
cursor.y() = cursor.y() - (line_length * _characterHeight);
break;
case LEFT_BOTTOM:
case CENTER_BOTTOM:
case RIGHT_BOTTOM:
cursor.y() = 2 * cursor.y() - endOfLine_coords.y();
break;
default:
break;
}
break;
if( line_numbers[i] < line )
// Wait for the correct line...
continue;
// We have already passed the correct line -> It's empty...
return osg::Vec2(0, y);
}
// Next line and not returned -> not before any character
// -> return position after last character of line
return osg::Vec2(coords[(i - 1) * 4 + 2].x(), y);
}
if( itr != endOfLine_itr )
line_found = true;
// Get threshold for mouse x position for setting cursor before or after
// current character
float threshold = coords[i * 4].x()
+ HIT_FRACTION * glyphs[i]->getHorizontalAdvance()
* character_width;
if( pos.x() <= threshold )
{
osg::Vec2 hit(0, y);
if( i == 0 || line_numbers[i - 1] != line )
// first character of line
hit.x() = coords[i * 4].x();
else if( coords[(i - 1) * 4].x() == coords[(i - 1) * 4 + 2].x() )
// If previous character width is zero set to begin of next character
// (Happens eg. with spaces)
hit.x() = coords[i * 4].x();
else
// position at center between characters
hit.x() = 0.5 * (coords[(i - 1) * 4 + 2].x() + coords[i * 4].x());
for(;itr != endOfLine_itr;++itr)
{
unsigned int charcode = *itr;
osgText::Glyph* glyph = activefont->getGlyph(_fontSize, charcode);
if( glyph )
{
float width = (float) (glyph->getWidth()) * wr;
float height = (float) (glyph->getHeight()) * hr;
if( _layout == RIGHT_TO_LEFT )
{
cursor.x() -= glyph->getHorizontalAdvance() * wr;
}
// adjust cursor position w.r.t any kerning.
if( kerning && previous_charcode )
{
switch( _layout )
{
case LEFT_TO_RIGHT:
{
osg::Vec2 delta( activefont->getKerning( previous_charcode,
charcode,
_kerningType ) );
cursor.x() += delta.x() * wr;
cursor.y() += delta.y() * hr;
break;
}
case RIGHT_TO_LEFT:
{
osg::Vec2 delta( activefont->getKerning( charcode,
previous_charcode,
_kerningType ) );
cursor.x() -= delta.x() * wr;
cursor.y() -= delta.y() * hr;
break;
}
case VERTICAL:
break; // no kerning when vertical.
}
}
local = cursor;
osg::Vec2 bearing( horizontal ? glyph->getHorizontalBearing()
: glyph->getVerticalBearing() );
local.x() += bearing.x() * wr;
local.y() += bearing.y() * hr;
// set up the coords of the quad
osg::Vec2 upLeft = local + osg::Vec2(0.f, height);
osg::Vec2 lowLeft = local;
osg::Vec2 lowRight = local + osg::Vec2(width, 0.f);
osg::Vec2 upRight = local + osg::Vec2(width, height);
// move the cursor onto the next character.
// also expand bounding box
switch( _layout )
{
case LEFT_TO_RIGHT:
cursor.x() += glyph->getHorizontalAdvance() * wr;
bb.expandBy(lowLeft.x(), lowLeft.y());
bb.expandBy(upRight.x(), upRight.y());
break;
case VERTICAL:
cursor.y() -= glyph->getVerticalAdvance() * hr;
bb.expandBy(upLeft.x(), upLeft.y());
bb.expandBy(lowRight.x(), lowRight.y());
break;
case RIGHT_TO_LEFT:
bb.expandBy(lowRight.x(), lowRight.y());
bb.expandBy(upLeft.x(), upLeft.y());
break;
}
previous_charcode = charcode;
}
}
// skip over spaces and return.
while( itr != text.end() && *itr == ' ' )
++itr;
if( itr != text.end() && *itr == '\n' )
++itr;
}
else
{
++itr;
}
// move to new line.
switch( _layout )
{
case LEFT_TO_RIGHT:
{
startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing);
cursor = startOfLine_coords;
previous_charcode = 0;
break;
}
case RIGHT_TO_LEFT:
{
startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing);
cursor = startOfLine_coords;
previous_charcode = 0;
break;
}
case VERTICAL:
{
startOfLine_coords.x() += _characterHeight * (1.0 + _lineSpacing)
/ getCharacterAspectRatio();
cursor = startOfLine_coords;
previous_charcode = 0;
break;
}
return hit;
}
}
const_cast<TextOSG*>(this)->_maximumWidth = max_width_safe;
return bb.size();
// Nothing found -> return position after last character
return osg::Vec2
(
coords.back().x(),
(_lineCount - 0.5) * line_height
);
}
//----------------------------------------------------------------------------
osg::BoundingBox
#if OSG_VERSION_LESS_THAN(3,3,2)
Text::TextOSG::computeBound()
#else
Text::TextOSG::computeBoundingBox()
#endif
const
osg::BoundingBox Text::TextOSG::computeBound() const
{
osg::BoundingBox bb =
#if OSG_VERSION_LESS_THAN(3,3,2)
osgText::Text::computeBound();
#else
osgText::Text::computeBoundingBox();
#endif
osg::BoundingBox bb = osgText::Text::computeBound();
if( !bb.valid() )
return bb;
#if OSG_VERSION_LESS_THAN(3,1,0)
if( bb.valid() )
{
// TODO bounding box still doesn't seem always right (eg. with center
// horizontal alignment not completely accurate)
bb._min.y() += _offset.y();
bb._max.y() += _offset.y();
}
// TODO bounding box still doesn't seem always right (eg. with center
// horizontal alignment not completely accurate)
bb._min.y() += _offset.y();
bb._max.y() += _offset.y();
#endif
_text_element->setBoundingBox(bb);
return bb;
}
@@ -636,13 +205,7 @@ namespace canvas
const GlyphQuads& quads = _textureGlyphQuadMap.begin()->second;
const GlyphQuads::Glyphs& glyphs = quads._glyphs;
#if OSG_VERSION_LESS_THAN(3,3,5)
GlyphQuads::Coords2 const& coords = quads._coords;
#else
GlyphQuads::Coords2 refCoords = quads._coords;
GlyphQuads::Coords2::element_type &coords = *refCoords.get();
#endif
const GlyphQuads::Coords2& coords = quads._coords;
const GlyphQuads::LineNumbers& line_numbers = quads._lineNumbers;
float wr = _characterHeight / getCharacterAspectRatio();
@@ -708,7 +271,6 @@ namespace canvas
addStyle("fill", "color", &TextOSG::setFill, text);
addStyle("background", "color", &TextOSG::setBackgroundColor, text);
addStyle("stroke", "color", &TextOSG::setStroke, text);
addStyle("character-size",
"numeric",
static_cast<
@@ -730,17 +292,13 @@ namespace canvas
addStyle("font", "", &Text::setFont);
addStyle("alignment", "", &Text::setAlignment);
addStyle("text", "", &Text::setText, false);
osgDB::Registry* reg = osgDB::Registry::instance();
if( !reg->getReaderWriterForExtension("ttf") )
SG_LOG(SG_GL, SG_ALERT, "canvas::Text: Missing 'ttf' font reader");
}
//----------------------------------------------------------------------------
Text::Text( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& parent_style,
ElementWeakPtr parent ):
Element* parent ):
Element(canvas, node, parent_style, parent),
_text( new Text::TextOSG(this) )
{
@@ -812,51 +370,10 @@ namespace canvas
}
#endif
//----------------------------------------------------------------------------
int Text::heightForWidth(int w) const
{
return _text->sizeForWidth(w).y();
}
//----------------------------------------------------------------------------
int Text::maxWidth() const
{
return _text->sizeForWidth(INT_MAX).x();
}
//----------------------------------------------------------------------------
size_t Text::lineCount() const
{
return _text->lineCount();
}
//----------------------------------------------------------------------------
size_t Text::lineLength(size_t line) const
{
return _text->lineAt(line).size();
}
//----------------------------------------------------------------------------
osg::Vec2 Text::getNearestCursor(const osg::Vec2& pos) const
{
return _text->nearestLine(pos.y()).nearestCursor(pos.x());
}
//----------------------------------------------------------------------------
osg::Vec2 Text::getCursorPos(size_t line, size_t character) const
{
return _text->lineAt(line).cursorPos(character);
}
//----------------------------------------------------------------------------
osg::StateSet* Text::getOrCreateStateSet()
{
if( !_transform.valid() )
return 0;
// Only check for StateSet on Transform, as the text stateset is shared
// between all text instances using the same font (texture).
return _transform->getOrCreateStateSet();
return _text->handleHit(pos);
}
} // namespace canvas

View File

@@ -29,7 +29,6 @@ namespace simgear
namespace canvas
{
class TextLine;
class Text:
public Element
{
@@ -40,33 +39,20 @@ namespace canvas
Text( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& parent_style,
ElementWeakPtr parent = 0 );
Element* parent = 0 );
~Text();
void setText(const char* text);
void setFont(const char* name);
void setAlignment(const char* align);
int heightForWidth(int w) const;
int maxWidth() const;
/// Number of text lines.
size_t lineCount() const;
/// Number of characters in @a line.
size_t lineLength(size_t line) const;
osg::Vec2 getNearestCursor(const osg::Vec2& pos) const;
osg::Vec2 getCursorPos(size_t line, size_t character) const;
protected:
friend class TextLine;
class TextOSG;
osg::ref_ptr<TextOSG> _text;
virtual osg::StateSet* getOrCreateStateSet();
};
} // namespace canvas

View File

@@ -1,66 +0,0 @@
/// Unit tests for canvas::Element
#define BOOST_TEST_MODULE canvas
#include <BoostTestTargetConfig.h>
#include "CanvasElement.hxx"
#include "CanvasGroup.hxx"
namespace sc = simgear::canvas;
BOOST_AUTO_TEST_CASE( attr_data )
{
// http://www.w3.org/TR/html5/dom.html#attr-data-*
#define SG_CHECK_ATTR2PROP(attr, prop)\
BOOST_CHECK_EQUAL(sc::Element::attrToDataPropName(attr), prop)
// If name starts with "data-", for each "-" (U+002D) character in the name
// that is followed by a lowercase ASCII letter, remove the "-" (U+002D)
// character and replace the character that followed it by the same character
// converted to ASCII uppercase.
SG_CHECK_ATTR2PROP("no-data", "");
SG_CHECK_ATTR2PROP("data-blub", "blub");
SG_CHECK_ATTR2PROP("data-blub-x-y", "blubXY");
SG_CHECK_ATTR2PROP("data-blub-x-y-", "blubXY-");
#undef SG_CHECK_ATTR2PROP
#define SG_CHECK_PROP2ATTR(prop, attr)\
BOOST_CHECK_EQUAL(sc::Element::dataPropToAttrName(prop), attr)
// If name contains a "-" (U+002D) character followed by a lowercase ASCII
// letter, throw a SyntaxError exception (empty string) and abort these steps.
// For each uppercase ASCII letter in name, insert a "-" (U+002D) character
// before the character and replace the character with the same character
// converted to ASCII lowercase.
// Insert the string "data-" at the front of name.
SG_CHECK_PROP2ATTR("test", "data-test");
SG_CHECK_PROP2ATTR("testIt", "data-test-it");
SG_CHECK_PROP2ATTR("testIt-Hyphen", "data-test-it--hyphen");
SG_CHECK_PROP2ATTR("-test", "");
SG_CHECK_PROP2ATTR("test-", "data-test-");
#undef SG_CHECK_PROP2ATTR
SGPropertyNode_ptr node = new SGPropertyNode;
sc::ElementPtr el =
sc::Element::create<sc::Group>(sc::CanvasWeakPtr(), node);
el->setDataProp("myData", 3);
BOOST_CHECK_EQUAL( el->getDataProp<int>("myData"), 3 );
BOOST_CHECK_EQUAL( node->getIntValue("data-my-data"), 3 );
SGPropertyNode* prop = el->getDataProp<SGPropertyNode*>("notExistingProp");
BOOST_CHECK( !prop );
prop = el->getDataProp<SGPropertyNode*>("myData");
BOOST_CHECK( prop );
BOOST_CHECK_EQUAL( prop->getParent(), node );
BOOST_CHECK_EQUAL( prop->getIntValue(), 3 );
BOOST_CHECK( el->hasDataProp("myData") );
el->removeDataProp("myData");
BOOST_CHECK( !el->hasDataProp("myData") );
BOOST_CHECK_EQUAL( el->getDataProp("myData", 5), 5 );
}

View File

@@ -34,10 +34,7 @@ namespace canvas
public:
struct ScreenPosition
{
ScreenPosition():
x(0),
y(0)
{}
ScreenPosition() {}
ScreenPosition(double x, double y):
x(x),
@@ -70,11 +67,8 @@ namespace canvas
public:
HorizontalProjection():
_ref_lat(0),
_ref_lon(0),
_angle(0),
_cos_angle(1),
_sin_angle(0),
_cos_rot(1),
_sin_rot(0),
_range(5)
{
setScreenRange(200);
@@ -94,19 +88,9 @@ namespace canvas
*/
void setOrientation(float hdg)
{
_angle = hdg;
hdg = SGMiscf::deg2rad(hdg);
_sin_angle = sin(hdg);
_cos_angle = cos(hdg);
}
/**
* Get orientation/heading of the projection (in degree)
*/
float orientation() const
{
return _angle;
_sin_rot = sin(hdg);
_cos_rot = cos(hdg);
}
void setRange(double range)
@@ -130,8 +114,8 @@ namespace canvas
pos.y *= scale;
return ScreenPosition
(
_cos_angle * pos.x - _sin_angle * pos.y,
-_sin_angle * pos.x - _cos_angle * pos.y
_cos_rot * pos.x - _sin_rot * pos.y,
-_sin_rot * pos.x - _cos_rot * pos.y
);
}
@@ -145,11 +129,10 @@ namespace canvas
*/
virtual ScreenPosition project(double lat, double lon) const = 0;
double _ref_lat, ///<! Reference latitude (radian)
_ref_lon, ///<! Reference latitude (radian)
_angle, ///<! Map rotation angle (degree)
_cos_angle,
_sin_angle,
double _ref_lat,
_ref_lon,
_cos_rot,
_sin_rot,
_range;
};

View File

@@ -1,28 +0,0 @@
include (SimGearComponent)
set(HEADERS
CustomEvent.hxx
DeviceEvent.hxx
KeyboardEvent.hxx
MouseEvent.hxx
)
set(SOURCES
CustomEvent.cxx
DeviceEvent.cxx
KeyboardEvent.cxx
MouseEvent.cxx
)
simgear_scene_component(canvas-events canvas/events "${SOURCES}" "${HEADERS}")
add_boost_test(canvas_event
SOURCES event_test.cpp
LIBRARIES ${TEST_LIBS}
)
add_executable(input_event_demo input_event_demo.cxx)
target_link_libraries(input_event_demo
${TEST_LIBS}
${OPENSCENEGRAPH_LIBRARIES}
)

View File

@@ -1,55 +0,0 @@
// Canvas user defined event
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "CustomEvent.hxx"
namespace simgear
{
namespace canvas
{
//----------------------------------------------------------------------------
CustomEvent::CustomEvent( std::string const& type_str,
bool bubbles,
StringMap const& data ):
detail(data),
bubbles(bubbles)
{
type = getOrRegisterType(type_str);
}
//----------------------------------------------------------------------------
CustomEvent::CustomEvent( int type_id,
bool bubbles,
StringMap const& data ):
detail(data),
bubbles(bubbles)
{
type = type_id;
// TypeMap::map_by<id>::type const& type_map = getTypeMap().by<id>();
// assert( type_map.find(type_id) != type_map.end() );
}
//----------------------------------------------------------------------------
void CustomEvent::setDetail(StringMap const& data)
{
detail = data;
}
} // namespace canvas
} // namespace simgear

View File

@@ -1,86 +0,0 @@
///@file
/// Canvas user defined event
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef CANVAS_CUSTOM_EVENT_HXX_
#define CANVAS_CUSTOM_EVENT_HXX_
#include <simgear/canvas/CanvasEvent.hxx>
#include <simgear/structure/map.hxx>
namespace simgear
{
namespace canvas
{
/**
* User defined event (optionally carrying additional context information or
* data).
*/
class CustomEvent:
public Event
{
public:
/**
*
* @param type_str Event type name (if name does not exist yet it will
* be registered as new event type)
* @param bubbles If this event should take part in the bubbling phase
* @param data Optional user data stored in event
*/
CustomEvent( std::string const& type_str,
bool bubbles = false,
StringMap const& data = StringMap() );
/**
*
* @param type_id Event type id
* @param bubbles If this event should take part in the bubbling phase
* @param data Optional user data stored in event
*/
CustomEvent( int type_id,
bool bubbles = false,
StringMap const& data = StringMap() );
/**
* Set user data
*/
void setDetail(StringMap const& data);
/**
* Get user data
*/
StringMap const& getDetail() const { return detail; }
/**
* Get whether this event supports bubbling.
*
* @see #bubbles
* @see CustomEvent()
*/
virtual bool canBubble() const { return bubbles; }
StringMap detail; //!< User data map
bool bubbles; //!< Whether the event supports bubbling
};
} // namespace canvas
} // namespace simgear
#endif /* CANVAS_CUSTOM_EVENT_HXX_ */

View File

@@ -1,66 +0,0 @@
// Input device event
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "DeviceEvent.hxx"
#include <osgGA/GUIEventAdapter>
namespace simgear
{
namespace canvas
{
//----------------------------------------------------------------------------
DeviceEvent::DeviceEvent():
modifiers(0)
{
}
//----------------------------------------------------------------------------
DeviceEvent::DeviceEvent(const osgGA::GUIEventAdapter& ea):
modifiers(ea.getModKeyMask())
{
time = ea.getTime();
}
//----------------------------------------------------------------------------
bool DeviceEvent::ctrlKey() const
{
return (modifiers & osgGA::GUIEventAdapter::MODKEY_CTRL) != 0;
}
//----------------------------------------------------------------------------
bool DeviceEvent::shiftKey() const
{
return (modifiers & osgGA::GUIEventAdapter::MODKEY_SHIFT) != 0;
}
//----------------------------------------------------------------------------
bool DeviceEvent::altKey() const
{
return (modifiers & osgGA::GUIEventAdapter::MODKEY_ALT) != 0;
}
//----------------------------------------------------------------------------
bool DeviceEvent::metaKey() const
{
return (modifiers & osgGA::GUIEventAdapter::MODKEY_META) != 0;
}
} // namespace canvas
} // namespace simgear

View File

@@ -1,67 +0,0 @@
///@file
/// Input device event (keyboard/mouse)
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef CANVAS_DEVICE_EVENT_HXX_
#define CANVAS_DEVICE_EVENT_HXX_
#include <simgear/canvas/CanvasEvent.hxx>
namespace osgGA { class GUIEventAdapter; }
namespace simgear
{
namespace canvas
{
/**
* Common interface for input device events.
*/
class DeviceEvent:
public Event
{
public:
/// Default initialization (no active keyboard modifier).
DeviceEvent();
/// Initialize from an OpenSceneGraph event.
DeviceEvent(const osgGA::GUIEventAdapter& ea);
/// Get mask of active keyboard modifiers at the time of the event.
int getModifiers() const { return modifiers; }
/// Get if a Ctrl modifier was active.
bool ctrlKey() const;
/// Get if a Shift modifier was active.
bool shiftKey() const;
/// Get if an Alt modifier was active.
bool altKey() const;
/// Get if a Meta modifier was active.
bool metaKey() const;
protected:
int modifiers; //!< Keyboard modifier state
};
} // namespace canvas
} // namespace simgear
#endif /* CANVAS_DEVICE_EVENT_HXX_ */

View File

@@ -1,323 +0,0 @@
// Keyboard event
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "KeyboardEvent.hxx"
#include "utf8.h"
#include <osgGA/GUIEventAdapter>
#include <boost/version.hpp>
#if BOOST_VERSION >= 104800
# include <boost/container/flat_map.hpp>
# include <boost/container/flat_set.hpp>
#else
# include <map>
# include <set>
#endif
#include <iterator>
namespace simgear
{
namespace canvas
{
typedef osgGA::GUIEventAdapter EA;
// TODO check Win/Mac keycode for altgr/ISO Level3 Shift
const uint32_t KEY_AltGraph = 0xfe03;
//----------------------------------------------------------------------------
KeyboardEvent::KeyboardEvent():
_key(0),
_unmodified_key(0),
_repeat(false),
_location(DOM_KEY_LOCATION_STANDARD)
{
}
//----------------------------------------------------------------------------
KeyboardEvent::KeyboardEvent(const osgGA::GUIEventAdapter& ea):
DeviceEvent(ea),
_key(ea.getKey()),
_unmodified_key(ea.getUnmodifiedKey()),
_repeat(false),
_location(DOM_KEY_LOCATION_STANDARD)
{
if( ea.getEventType() == EA::KEYDOWN )
type = KEY_DOWN;
else if( ea.getEventType() == EA::KEYUP )
type = KEY_UP;
// else
// // TODO what to do with wrong event type?
}
//----------------------------------------------------------------------------
void KeyboardEvent::setKey(uint32_t key)
{
_name.clear();
_key = key;
}
//----------------------------------------------------------------------------
void KeyboardEvent::setUnmodifiedKey(uint32_t key)
{
_name.clear();
_unmodified_key = key;
}
//----------------------------------------------------------------------------
void KeyboardEvent::setRepeat(bool repeat)
{
_repeat = repeat;
}
//----------------------------------------------------------------------------
std::string KeyboardEvent::key() const
{
if( !_name.empty() )
return _name;
// We need to make sure only valid const char* pointers are passed. The best
// way is just to use string constants.
// Use an empty string ("") to just use the value reported by the operating
// system.
typedef std::pair<const char*, uint8_t> InternalKeyInfo;
#if BOOST_VERSION >= 104800
typedef boost::container::flat_map<int, InternalKeyInfo> InternalKeyMap;
typedef boost::container::flat_set<int> KeyList;
#else
# warning "Use Boost >= 1.48 for faster and more memory efficient key lookup"
typedef std::map<int, InternalKeyInfo> InternalKeyMap;
typedef std::set<int> KeyList;
#endif
static InternalKeyMap key_map;
static KeyList num_pad_keys;
if( key_map.empty() )
{
const uint8_t S = DOM_KEY_LOCATION_STANDARD,
L = DOM_KEY_LOCATION_LEFT,
R = DOM_KEY_LOCATION_RIGHT,
N = DOM_KEY_LOCATION_NUMPAD;
key_map[ EA::KEY_BackSpace ] = std::make_pair("Backspace", S);
key_map[ EA::KEY_Tab ] = std::make_pair("Tab", S);
key_map[ EA::KEY_Linefeed ] = std::make_pair("Linefeed", S);
key_map[ EA::KEY_Clear ] = std::make_pair("Clear", S);
key_map[ EA::KEY_Return ] = std::make_pair("Enter", S);
key_map[ EA::KEY_Pause ] = std::make_pair("Pause", S);
key_map[ EA::KEY_Scroll_Lock ] = std::make_pair("ScrollLock", S);
key_map[ EA::KEY_Sys_Req ] = std::make_pair("SystemRequest", S);
key_map[ EA::KEY_Escape ] = std::make_pair("Escape", S);
key_map[ EA::KEY_Delete ] = std::make_pair("Delete", S);
key_map[ EA::KEY_Home ] = std::make_pair("Home", S);
key_map[ EA::KEY_Left ] = std::make_pair("Left", S);
key_map[ EA::KEY_Up ] = std::make_pair("Up", S);
key_map[ EA::KEY_Right ] = std::make_pair("Right", S);
key_map[ EA::KEY_Down ] = std::make_pair("Down", S);
key_map[ EA::KEY_Page_Up ] = std::make_pair("PageUp", S);
key_map[ EA::KEY_Page_Down ] = std::make_pair("PageDown", S);
key_map[ EA::KEY_End ] = std::make_pair("End", S);
key_map[ EA::KEY_Begin ] = std::make_pair("Begin", S);
key_map[ EA::KEY_Select ] = std::make_pair("Select", S);
key_map[ EA::KEY_Print ] = std::make_pair("PrintScreen", S);
key_map[ EA::KEY_Execute ] = std::make_pair("Execute", S);
key_map[ EA::KEY_Insert ] = std::make_pair("Insert", S);
key_map[ EA::KEY_Undo ] = std::make_pair("Undo", S);
key_map[ EA::KEY_Redo ] = std::make_pair("Redo", S);
key_map[ EA::KEY_Menu ] = std::make_pair("ContextMenu", S);
key_map[ EA::KEY_Find ] = std::make_pair("Find", S);
key_map[ EA::KEY_Cancel ] = std::make_pair("Cancel", S);
key_map[ EA::KEY_Help ] = std::make_pair("Help", S);
key_map[ EA::KEY_Break ] = std::make_pair("Break", S);
key_map[ EA::KEY_Mode_switch ] = std::make_pair("ModeChange", S);
key_map[ EA::KEY_Num_Lock ] = std::make_pair("NumLock", S);
key_map[ EA::KEY_KP_Space ] = std::make_pair(" ", N);
key_map[ EA::KEY_KP_Tab ] = std::make_pair("Tab", N);
key_map[ EA::KEY_KP_Enter ] = std::make_pair("Enter", N);
key_map[ EA::KEY_KP_F1 ] = std::make_pair("F1", N);
key_map[ EA::KEY_KP_F2 ] = std::make_pair("F2", N);
key_map[ EA::KEY_KP_F3 ] = std::make_pair("F3", N);
key_map[ EA::KEY_KP_F4 ] = std::make_pair("F4", N);
key_map[ EA::KEY_KP_Home ] = std::make_pair("Home", N);
key_map[ EA::KEY_KP_Left ] = std::make_pair("Left", N);
key_map[ EA::KEY_KP_Up ] = std::make_pair("Up", N);
key_map[ EA::KEY_KP_Right ] = std::make_pair("Right", N);
key_map[ EA::KEY_KP_Down ] = std::make_pair("Down", N);
key_map[ EA::KEY_KP_Page_Up ] = std::make_pair("PageUp", N);
key_map[ EA::KEY_KP_Page_Down ] = std::make_pair("PageDown", N);
key_map[ EA::KEY_KP_End ] = std::make_pair("End", N);
key_map[ EA::KEY_KP_Begin ] = std::make_pair("Begin", N);
key_map[ EA::KEY_KP_Insert ] = std::make_pair("Insert", N);
key_map[ EA::KEY_KP_Delete ] = std::make_pair("Delete", N);
key_map[ EA::KEY_KP_Equal ] = std::make_pair("=", N);
key_map[ EA::KEY_KP_Multiply ] = std::make_pair("*", N);
key_map[ EA::KEY_KP_Add ] = std::make_pair("+", N);
key_map[ EA::KEY_KP_Separator ] = std::make_pair("", N);
key_map[ EA::KEY_KP_Subtract ] = std::make_pair("-", N);
key_map[ EA::KEY_KP_Decimal ] = std::make_pair("", N);
key_map[ EA::KEY_KP_Divide ] = std::make_pair("/", N);
key_map[ EA::KEY_KP_0 ] = std::make_pair("0", N);
key_map[ EA::KEY_KP_1 ] = std::make_pair("1", N);
key_map[ EA::KEY_KP_2 ] = std::make_pair("2", N);
key_map[ EA::KEY_KP_3 ] = std::make_pair("3", N);
key_map[ EA::KEY_KP_4 ] = std::make_pair("4", N);
key_map[ EA::KEY_KP_5 ] = std::make_pair("5", N);
key_map[ EA::KEY_KP_6 ] = std::make_pair("6", N);
key_map[ EA::KEY_KP_7 ] = std::make_pair("7", N);
key_map[ EA::KEY_KP_8 ] = std::make_pair("8", N);
key_map[ EA::KEY_KP_9 ] = std::make_pair("9", N);
key_map[ EA::KEY_F1 ] = std::make_pair("F1", S);
key_map[ EA::KEY_F2 ] = std::make_pair("F2", S);
key_map[ EA::KEY_F3 ] = std::make_pair("F3", S);
key_map[ EA::KEY_F4 ] = std::make_pair("F4", S);
key_map[ EA::KEY_F5 ] = std::make_pair("F5", S);
key_map[ EA::KEY_F6 ] = std::make_pair("F6", S);
key_map[ EA::KEY_F7 ] = std::make_pair("F7", S);
key_map[ EA::KEY_F8 ] = std::make_pair("F8", S);
key_map[ EA::KEY_F9 ] = std::make_pair("F9", S);
key_map[ EA::KEY_F10 ] = std::make_pair("F10", S);
key_map[ EA::KEY_F11 ] = std::make_pair("F11", S);
key_map[ EA::KEY_F12 ] = std::make_pair("F12", S);
key_map[ EA::KEY_F13 ] = std::make_pair("F13", S);
key_map[ EA::KEY_F14 ] = std::make_pair("F14", S);
key_map[ EA::KEY_F15 ] = std::make_pair("F15", S);
key_map[ EA::KEY_F16 ] = std::make_pair("F16", S);
key_map[ EA::KEY_F17 ] = std::make_pair("F17", S);
key_map[ EA::KEY_F18 ] = std::make_pair("F18", S);
key_map[ EA::KEY_F19 ] = std::make_pair("F19", S);
key_map[ EA::KEY_F20 ] = std::make_pair("F20", S);
key_map[ EA::KEY_F21 ] = std::make_pair("F21", S);
key_map[ EA::KEY_F22 ] = std::make_pair("F22", S);
key_map[ EA::KEY_F23 ] = std::make_pair("F23", S);
key_map[ EA::KEY_F24 ] = std::make_pair("F24", S);
key_map[ EA::KEY_F25 ] = std::make_pair("F25", S);
key_map[ EA::KEY_F26 ] = std::make_pair("F26", S);
key_map[ EA::KEY_F27 ] = std::make_pair("F27", S);
key_map[ EA::KEY_F28 ] = std::make_pair("F28", S);
key_map[ EA::KEY_F29 ] = std::make_pair("F29", S);
key_map[ EA::KEY_F30 ] = std::make_pair("F30", S);
key_map[ EA::KEY_F31 ] = std::make_pair("F31", S);
key_map[ EA::KEY_F32 ] = std::make_pair("F32", S);
key_map[ EA::KEY_F33 ] = std::make_pair("F33", S);
key_map[ EA::KEY_F34 ] = std::make_pair("F34", S);
key_map[ EA::KEY_F35 ] = std::make_pair("F35", S);
key_map[ KEY_AltGraph ] = std::make_pair("AltGraph", S);
key_map[ EA::KEY_Shift_L ] = std::make_pair("Shift", L);
key_map[ EA::KEY_Shift_R ] = std::make_pair("Shift", R);
key_map[ EA::KEY_Control_L ] = std::make_pair("Control", L);
key_map[ EA::KEY_Control_R ] = std::make_pair("Control", R);
key_map[ EA::KEY_Caps_Lock ] = std::make_pair("CapsLock", S);
key_map[ EA::KEY_Shift_Lock ] = std::make_pair("ShiftLock", S);
key_map[ EA::KEY_Meta_L ] = std::make_pair("Meta", L);
key_map[ EA::KEY_Meta_R ] = std::make_pair("Meta", R);
key_map[ EA::KEY_Alt_L ] = std::make_pair("Alt", L);
key_map[ EA::KEY_Alt_R ] = std::make_pair("Alt", R);
key_map[ EA::KEY_Super_L ] = std::make_pair("Super", L);
key_map[ EA::KEY_Super_R ] = std::make_pair("Super", R);
key_map[ EA::KEY_Hyper_L ] = std::make_pair("Hyper", L);
key_map[ EA::KEY_Hyper_R ] = std::make_pair("Hyper", R);
num_pad_keys.insert(EA::KEY_KP_Home );
num_pad_keys.insert(EA::KEY_KP_Left );
num_pad_keys.insert(EA::KEY_KP_Up );
num_pad_keys.insert(EA::KEY_KP_Right );
num_pad_keys.insert(EA::KEY_KP_Down );
num_pad_keys.insert(EA::KEY_KP_Page_Up );
num_pad_keys.insert(EA::KEY_KP_Page_Down);
num_pad_keys.insert(EA::KEY_KP_End );
num_pad_keys.insert(EA::KEY_KP_Begin );
num_pad_keys.insert(EA::KEY_KP_Insert );
num_pad_keys.insert(EA::KEY_KP_Delete );
}
_location = DOM_KEY_LOCATION_STANDARD;
InternalKeyMap::const_iterator it = key_map.find(_key);
if( it != key_map.end())
{
_name = it->second.first;
_location = it->second.second;
}
// Empty or no mapping -> convert UTF-32 key value to UTF-8
if( _name.empty() )
{
if( !utf8::internal::is_code_point_valid(_key) )
_name = "Unidentified";
else
utf8::unchecked::append(_key, std::back_inserter(_name));
}
// Keys on the numpad with NumLock enabled are reported just like their
// equivalent keys in the standard key block. Using the unmodified key value
// we can detect such keys and set the location accordingly.
if( num_pad_keys.find(_unmodified_key) != num_pad_keys.end() )
_location = DOM_KEY_LOCATION_NUMPAD;
return _name;
}
//----------------------------------------------------------------------------
KeyboardEvent::DOMKeyLocation KeyboardEvent::location() const
{
key(); // ensure location is up-to-date
return static_cast<DOMKeyLocation>(_location);
}
//----------------------------------------------------------------------------
bool KeyboardEvent::isModifier() const
{
return ( _key >= EA::KEY_Shift_L
&& _key <= EA::KEY_Hyper_R
)
|| _key == KEY_AltGraph;
}
//----------------------------------------------------------------------------
bool KeyboardEvent::isPrint() const
{
const std::string& key_name = key();
if( key_name.empty() )
return false;
std::string::const_iterator it = key_name.begin();
uint32_t cp = utf8::next(it, key_name.end());
// Check if _name contains exactly one (UTF-8 encoded) character.
if( it != key_name.end() )
return false;
// C0 and C1 control characters are not printable.
if( cp <= 0x1f || (0x7f <= cp && cp <= 0x9f) )
return false;
return true;
}
} // namespace canvas
} // namespace simgear

View File

@@ -1,81 +0,0 @@
///@file
/// Keyboard event
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef CANVAS_KEYBOARD_EVENT_HXX_
#define CANVAS_KEYBOARD_EVENT_HXX_
#include "DeviceEvent.hxx"
namespace simgear
{
namespace canvas
{
/**
* Keyboard (button up/down) event
*/
class KeyboardEvent:
public DeviceEvent
{
public:
enum DOMKeyLocation
{
DOM_KEY_LOCATION_STANDARD = 0,
DOM_KEY_LOCATION_LEFT,
DOM_KEY_LOCATION_RIGHT,
DOM_KEY_LOCATION_NUMPAD
};
KeyboardEvent();
KeyboardEvent(const osgGA::GUIEventAdapter& ea);
void setKey(uint32_t key);
void setUnmodifiedKey(uint32_t key);
void setRepeat(bool repeat);
std::string key() const;
DOMKeyLocation location() const;
bool repeat() const { return _repeat; }
uint32_t charCode() const { return _key; }
uint32_t keyCode() const { return _unmodified_key; }
/// Whether the key which has triggered this event is a modifier
bool isModifier() const;
/// Whether this events represents an input of a printable character
bool isPrint() const;
protected:
uint32_t _key, //!< Key identifier for this event
_unmodified_key; //!< Virtual key identifier without any
// modifiers applied
bool _repeat; //!< If key has been depressed long enough to
// generate key repetition
mutable std::string _name; //!< Printable representation/name
mutable uint8_t _location; //!< Location of the key on the keyboard
};
} // namespace canvas
} // namespace simgear
#endif /* CANVAS_KEYBOARD_EVENT_HXX_ */

View File

@@ -1,68 +0,0 @@
// Mouse event
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "MouseEvent.hxx"
#include <osgGA/GUIEventAdapter>
namespace simgear
{
namespace canvas
{
//----------------------------------------------------------------------------
MouseEvent::MouseEvent():
button(0),
buttons(0),
click_count(0)
{
}
//----------------------------------------------------------------------------
MouseEvent::MouseEvent(const osgGA::GUIEventAdapter& ea):
DeviceEvent(ea),
button(0),
buttons(ea.getButtonMask()),
click_count(0)
{
// Convert button mask to index
int button_mask = ea.getButton();
while( (button_mask >>= 1) > 0 )
button += 1;
}
//----------------------------------------------------------------------------
bool MouseEvent::canBubble() const
{
// Check if event supports bubbling
const Event::Type types_no_bubbling[] = {
Event::MOUSE_ENTER,
Event::MOUSE_LEAVE,
};
const size_t num_types_no_bubbling = sizeof(types_no_bubbling)
/ sizeof(types_no_bubbling[0]);
for( size_t i = 0; i < num_types_no_bubbling; ++i )
if( type == types_no_bubbling[i] )
return false;
return true;
}
} // namespace canvas
} // namespace simgear

View File

@@ -1,36 +0,0 @@
/// Unit tests for reference counting and smart pointer classes
#define BOOST_TEST_MODULE structure
#include <BoostTestTargetConfig.h>
#include "MouseEvent.hxx"
#include "CustomEvent.hxx"
namespace sc = simgear::canvas;
BOOST_AUTO_TEST_CASE( canvas_event_types )
{
// Register type
BOOST_REQUIRE_EQUAL( sc::Event::strToType("test"),
sc::Event::UNKNOWN );
BOOST_REQUIRE_EQUAL( sc::Event::getOrRegisterType("test"),
sc::Event::CUSTOM_EVENT );
BOOST_REQUIRE_EQUAL( sc::Event::strToType("test"),
sc::Event::CUSTOM_EVENT );
BOOST_REQUIRE_EQUAL( sc::Event::typeToStr(sc::Event::CUSTOM_EVENT),
"test" );
// Basic internal type
BOOST_REQUIRE_EQUAL( sc::Event::typeToStr(sc::Event::MOUSE_DOWN),
"mousedown" );
BOOST_REQUIRE_EQUAL( sc::Event::strToType("mousedown"),
sc::Event::MOUSE_DOWN );
// Unknown type
BOOST_REQUIRE_EQUAL( sc::Event::typeToStr(123),
"unknown" );
// Register type through custom event instance
sc::CustomEvent e("blub");
BOOST_REQUIRE_EQUAL( e.getTypeString(), "blub" );
BOOST_REQUIRE_NE( e.getType(), sc::Event::UNKNOWN );
}

View File

@@ -1,78 +0,0 @@
// Keyboard event demo. Press some keys and get some info...
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "KeyboardEvent.hxx"
#include <osgViewer/Viewer>
#include <iostream>
class DemoEventHandler:
public osgGA::GUIEventHandler
{
public:
bool handle( const osgGA::GUIEventAdapter& ea,
osgGA::GUIActionAdapter&,
osg::Object*,
osg::NodeVisitor* )
{
switch( ea.getEventType() )
{
case osgGA::GUIEventAdapter::PUSH:
case osgGA::GUIEventAdapter::RELEASE:
case osgGA::GUIEventAdapter::DRAG:
case osgGA::GUIEventAdapter::MOVE:
case osgGA::GUIEventAdapter::SCROLL:
return handleMouse(ea);
case osgGA::GUIEventAdapter::KEYDOWN:
case osgGA::GUIEventAdapter::KEYUP:
return handleKeyboard(ea);
default:
return false;
}
}
protected:
bool handleMouse(const osgGA::GUIEventAdapter&)
{
return false;
}
bool handleKeyboard(const osgGA::GUIEventAdapter& ea)
{
simgear::canvas::KeyboardEvent evt(ea);
std::cout << evt.getTypeString() << " '" << evt.key() << "'"
<< ", loc=" << evt.location()
<< ", char=" << evt.charCode()
<< ", key=" << evt.keyCode()
<< (evt.isPrint() ? ", printable" : "")
<< std::endl;
return true;
}
};
int main()
{
osgViewer::Viewer viewer;
osg::ref_ptr<DemoEventHandler> handler( new DemoEventHandler );
viewer.addEventHandler(handler);
viewer.setUpViewInWindow(100, 100, 200, 100, 0);
viewer.setRunMaxFrameRate(5);
return viewer.run();
}

View File

@@ -1,43 +0,0 @@
///@file
/// Enumeration of layout alignment flags.
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef ALIGN_ENUM_MAPPING
# error "Only include with ALIGN_ENUM_MAPPING defined!"
#endif
ALIGN_ENUM_MAPPING(AlignFill, 0, Use all available space)
ALIGN_ENUM_MAPPING(AlignLeft, 0x01, Align with left edge)
ALIGN_ENUM_MAPPING(AlignRight, 0x02, Align with right edge)
ALIGN_ENUM_MAPPING(AlignHCenter, 0x04, Center horizontally in available space)
ALIGN_ENUM_MAPPING(AlignTop, 0x20, Align with top edge)
ALIGN_ENUM_MAPPING(AlignBottom, 0x40, Align with bottom edge)
ALIGN_ENUM_MAPPING(AlignVCenter, 0x80, Center vertically in available space)
ALIGN_ENUM_MAPPING( AlignCenter,
AlignVCenter | AlignHCenter,
Center both vertically and horizontally )
ALIGN_ENUM_MAPPING( AlignHorizontal_Mask,
AlignLeft | AlignRight | AlignHCenter,
Mask of all horizontal alignment flags )
ALIGN_ENUM_MAPPING( AlignVertical_Mask,
AlignTop | AlignBottom | AlignVCenter,
Mask of all vertical alignment flags )

View File

@@ -1,522 +0,0 @@
// Align items horizontally or vertically in a box
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "BoxLayout.hxx"
#include "SpacerItem.hxx"
#include <simgear/canvas/Canvas.hxx>
namespace simgear
{
namespace canvas
{
//----------------------------------------------------------------------------
BoxLayout::BoxLayout(Direction dir):
_padding(5)
{
setDirection(dir);
}
//----------------------------------------------------------------------------
BoxLayout::~BoxLayout()
{
_parent.reset(); // No need to invalidate parent again...
clear();
}
//----------------------------------------------------------------------------
void BoxLayout::addItem(const LayoutItemRef& item)
{
return addItem(item, 0);
}
//----------------------------------------------------------------------------
void BoxLayout::addItem( const LayoutItemRef& item,
int stretch,
uint8_t alignment )
{
insertItem(-1, item, stretch, alignment);
}
//----------------------------------------------------------------------------
void BoxLayout::addStretch(int stretch)
{
insertStretch(-1, stretch);
}
//----------------------------------------------------------------------------
void BoxLayout::addSpacing(int size)
{
insertSpacing(-1, size);
}
//----------------------------------------------------------------------------
void BoxLayout::insertItem( int index,
const LayoutItemRef& item,
int stretch,
uint8_t alignment )
{
ItemData item_data = {0};
item_data.layout_item = item;
item_data.stretch = std::max(0, stretch);
if( alignment != AlignFill )
item->setAlignment(alignment);
if( SGWeakReferenced::count(this) )
item->setParent(this);
else
SG_LOG( SG_GUI,
SG_WARN,
"Adding item to expired or non-refcounted layout" );
if( index < 0 )
_layout_items.push_back(item_data);
else
_layout_items.insert(_layout_items.begin() + index, item_data);
invalidate();
}
//----------------------------------------------------------------------------
void BoxLayout::insertStretch(int index, int stretch)
{
insertItem(index, LayoutItemRef(new SpacerItem()), stretch);
}
//----------------------------------------------------------------------------
void BoxLayout::insertSpacing(int index, int size)
{
SGVec2i size_hint = horiz()
? SGVec2i(size, 0)
: SGVec2i(0, size),
max_size = size_hint;
insertItem(index, LayoutItemRef(new SpacerItem(size_hint, max_size)));
}
//----------------------------------------------------------------------------
size_t BoxLayout::count() const
{
return _layout_items.size();
}
//----------------------------------------------------------------------------
LayoutItemRef BoxLayout::itemAt(size_t index)
{
if( index >= _layout_items.size() )
return LayoutItemRef();
return _layout_items[index].layout_item;
}
//----------------------------------------------------------------------------
LayoutItemRef BoxLayout::takeAt(size_t index)
{
if( index >= _layout_items.size() )
return LayoutItemRef();
LayoutItems::iterator it = _layout_items.begin() + index;
LayoutItemRef item = it->layout_item;
item->onRemove();
item->setParent(LayoutItemWeakRef());
_layout_items.erase(it);
invalidate();
return item;
}
//----------------------------------------------------------------------------
void BoxLayout::clear()
{
for( LayoutItems::iterator it = _layout_items.begin();
it != _layout_items.end();
++it )
{
it->layout_item->onRemove();
it->layout_item->setParent(LayoutItemWeakRef());
}
_layout_items.clear();
invalidate();
}
//----------------------------------------------------------------------------
void BoxLayout::setStretch(size_t index, int stretch)
{
if( index >= _layout_items.size() )
return;
_layout_items.at(index).stretch = std::max(0, stretch);
invalidate();
}
//----------------------------------------------------------------------------
bool BoxLayout::setStretchFactor(const LayoutItemRef& item, int stretch)
{
for( LayoutItems::iterator it = _layout_items.begin();
it != _layout_items.end();
++it )
{
if( item == it->layout_item )
{
it->stretch = std::max(0, stretch);
invalidate();
return true;
}
}
return false;
}
//----------------------------------------------------------------------------
int BoxLayout::stretch(size_t index) const
{
if( index >= _layout_items.size() )
return 0;
return _layout_items.at(index).stretch;
}
//----------------------------------------------------------------------------
void BoxLayout::setSpacing(int spacing)
{
if( spacing == _padding )
return;
_padding = spacing;
invalidate();
}
//----------------------------------------------------------------------------
int BoxLayout::spacing() const
{
return _padding;
}
//----------------------------------------------------------------------------
void BoxLayout::setDirection(Direction dir)
{
_direction = dir;
_get_layout_coord = &SGVec2i::x;
_get_fixed_coord = &SGVec2i::y;
if( !horiz() )
std::swap(_get_layout_coord, _get_fixed_coord);
invalidate();
}
//----------------------------------------------------------------------------
BoxLayout::Direction BoxLayout::direction() const
{
return _direction;
}
//----------------------------------------------------------------------------
bool BoxLayout::hasHeightForWidth() const
{
if( _flags & SIZE_INFO_DIRTY )
updateSizeHints();
return _layout_data.has_hfw;
}
//----------------------------------------------------------------------------
void BoxLayout::setCanvas(const CanvasWeakPtr& canvas)
{
_canvas = canvas;
for(size_t i = 0; i < _layout_items.size(); ++i)
_layout_items[i].layout_item->setCanvas(canvas);
}
//----------------------------------------------------------------------------
bool BoxLayout::horiz() const
{
return (_direction == LeftToRight) || (_direction == RightToLeft);
}
//----------------------------------------------------------------------------
void BoxLayout::updateSizeHints() const
{
SGVec2i min_size(0, 0),
max_size(0, 0),
size_hint(0, 0);
_layout_data.reset();
_hfw_width = _hfw_height = _hfw_min_height = -1;
bool is_first = true;
for(size_t i = 0; i < _layout_items.size(); ++i)
{
ItemData& item_data = _layout_items[i];
LayoutItem const& item = *item_data.layout_item;
item_data.visible = item.isVisible();
if( !item_data.visible )
continue;
item_data.min_size = (item.minimumSize().*_get_layout_coord)();
item_data.max_size = (item.maximumSize().*_get_layout_coord)();
item_data.size_hint = (item.sizeHint().*_get_layout_coord)();
item_data.has_hfw = item.hasHeightForWidth();
uint8_t alignment_mask = horiz()
? AlignHorizontal_Mask
: AlignVertical_Mask;
item_data.has_align = (item.alignment() & alignment_mask) != 0;
if( !dynamic_cast<SpacerItem*>(item_data.layout_item.get()) )
{
if( is_first )
{
item_data.padding_orig = 0;
is_first = false;
}
else
{
item_data.padding_orig = _padding;
_layout_data.padding += item_data.padding_orig;
}
}
// Add sizes of all children in layout direction
SGMisc<int>::addClipOverflowInplace(min_size.x(), item_data.min_size);
SGMisc<int>::addClipOverflowInplace(max_size.x(), item_data.max_size);
SGMisc<int>::addClipOverflowInplace(size_hint.x(), item_data.size_hint);
// Take maximum in fixed (non-layouted) direction
min_size.y() = std::max( min_size.y(),
(item.minimumSize().*_get_fixed_coord)() );
max_size.y() = std::max( max_size.y(),
(item.maximumSize().*_get_fixed_coord)() );
size_hint.y() = std::max( size_hint.y(),
(item.sizeHint().*_get_fixed_coord)() );
_layout_data.has_hfw = _layout_data.has_hfw || item.hasHeightForWidth();
}
SGMisc<int>::addClipOverflowInplace(min_size.x(), _layout_data.padding);
SGMisc<int>::addClipOverflowInplace(max_size.x(), _layout_data.padding);
SGMisc<int>::addClipOverflowInplace(size_hint.x(), _layout_data.padding);
_layout_data.min_size = min_size.x();
_layout_data.max_size = max_size.x();
_layout_data.size_hint = size_hint.x();
_min_size.x() = (min_size.*_get_layout_coord)();
_max_size.x() = (max_size.*_get_layout_coord)();
_size_hint.x() = (size_hint.*_get_layout_coord)();
_min_size.y() = (min_size.*_get_fixed_coord)();
_max_size.y() = (max_size.*_get_fixed_coord)();
_size_hint.y() = (size_hint.*_get_fixed_coord)();
_flags &= ~SIZE_INFO_DIRTY;
}
//----------------------------------------------------------------------------
void BoxLayout::updateWFHCache(int w) const
{
if( w == _hfw_width )
return;
_hfw_height = 0;
_hfw_min_height = 0;
if( horiz() )
{
_layout_data.size = w;
const_cast<BoxLayout*>(this)->distribute(_layout_items, _layout_data);
for(size_t i = 0; i < _layout_items.size(); ++i)
{
ItemData const& data = _layout_items[i];
if( !data.visible )
continue;
_hfw_height = std::max(_hfw_height, data.hfw(data.size));
_hfw_min_height = std::max(_hfw_min_height, data.mhfw(data.size));
}
}
else
{
for(size_t i = 0; i < _layout_items.size(); ++i)
{
ItemData const& data = _layout_items[i];
if( !data.visible )
continue;
_hfw_height += data.hfw(w) + data.padding_orig;
_hfw_min_height += data.mhfw(w) + data.padding_orig;
}
}
_hfw_width = w;
}
//----------------------------------------------------------------------------
SGVec2i BoxLayout::sizeHintImpl() const
{
updateSizeHints();
return _size_hint;
}
//----------------------------------------------------------------------------
SGVec2i BoxLayout::minimumSizeImpl() const
{
updateSizeHints();
return _min_size;
}
//----------------------------------------------------------------------------
SGVec2i BoxLayout::maximumSizeImpl() const
{
updateSizeHints();
return _max_size;
}
//----------------------------------------------------------------------------
int BoxLayout::heightForWidthImpl(int w) const
{
if( !hasHeightForWidth() )
return -1;
updateWFHCache(w);
return _hfw_height;
}
//----------------------------------------------------------------------------
int BoxLayout::minimumHeightForWidthImpl(int w) const
{
if( !hasHeightForWidth() )
return -1;
updateWFHCache(w);
return _hfw_min_height;
}
//----------------------------------------------------------------------------
void BoxLayout::doLayout(const SGRecti& geom)
{
if( _flags & SIZE_INFO_DIRTY )
updateSizeHints();
// Store current size hints because vertical layouts containing
// height-for-width items the size hints are update for the actual width of
// the layout
int min_size_save = _layout_data.min_size,
size_hint_save = _layout_data.size_hint;
_layout_data.size = (geom.size().*_get_layout_coord)();
// update width dependent data for layouting of vertical layouts
if( _layout_data.has_hfw && !horiz() )
{
for(size_t i = 0; i < _layout_items.size(); ++i)
{
ItemData& data = _layout_items[i];
if( !data.visible )
continue;
if( data.has_hfw )
{
int w = SGMisc<int>::clip( geom.width(),
data.layout_item->minimumSize().x(),
data.layout_item->maximumSize().x() );
data.min_size = data.mhfw(w);
data.size_hint = data.hfw(w);
// Update size hints for layouting with difference to size hints
// calculated by using the size hints provided (without trading
// height for width)
_layout_data.min_size += data.min_size
- data.layout_item->minimumSize().y();
_layout_data.size_hint += data.size_hint
- data.layout_item->sizeHint().y();
}
}
}
// now do the actual layouting
distribute(_layout_items, _layout_data);
// Restore size hints possibly changed by vertical layouting
_layout_data.min_size = min_size_save;
_layout_data.size_hint = size_hint_save;
// and finally set the layouted geometry for each item
SGVec2i size( 0,
// Always assign all available space. Alignment handles final
// size.
(geom.size().*_get_fixed_coord)() );
SGVec2i cur_pos( (geom.pos().*_get_layout_coord)(),
(geom.pos().*_get_fixed_coord)() );
bool reverse = (_direction == RightToLeft) || (_direction == BottomToTop);
if( reverse )
cur_pos.x() += (geom.size().*_get_layout_coord)();
for(size_t i = 0; i < _layout_items.size(); ++i)
{
ItemData const& data = _layout_items[i];
if( !data.visible )
continue;
cur_pos.x() += reverse ? -data.padding - data.size : data.padding;
size.x() = data.size;
data.layout_item->setGeometry(SGRecti(
(cur_pos.*_get_layout_coord)(),
(cur_pos.*_get_fixed_coord)(),
(size.*_get_layout_coord)(),
(size.*_get_fixed_coord)()
));
if( !reverse )
cur_pos.x() += data.size;
}
}
//----------------------------------------------------------------------------
void BoxLayout::visibilityChanged(bool visible)
{
for(size_t i = 0; i < _layout_items.size(); ++i)
callSetVisibleInternal(_layout_items[i].layout_item.get(), visible);
}
//----------------------------------------------------------------------------
HBoxLayout::HBoxLayout():
BoxLayout(LeftToRight)
{
}
//----------------------------------------------------------------------------
VBoxLayout::VBoxLayout():
BoxLayout(TopToBottom)
{
}
} // namespace canvas
} // namespace simgear

View File

@@ -1,167 +0,0 @@
/// @file
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_CANVAS_BOX_LAYOUT_HXX_
#define SG_CANVAS_BOX_LAYOUT_HXX_
#include "Layout.hxx"
namespace simgear
{
namespace canvas
{
/**
* Align LayoutItems horizontally or vertically in a box.
*
* @see http://qt-project.org/doc/qt-4.8/qboxlayout.html#details
*/
class BoxLayout:
public Layout
{
public:
enum Direction
{
LeftToRight,
RightToLeft,
TopToBottom,
BottomToTop
};
BoxLayout(Direction dir);
~BoxLayout();
virtual void addItem(const LayoutItemRef& item);
void addItem( const LayoutItemRef& item,
int stretch,
uint8_t alignment = 0 );
void addStretch(int stretch = 0);
void addSpacing(int size);
void insertItem( int index,
const LayoutItemRef& item,
int stretch = 0,
uint8_t alignment = 0 );
void insertStretch(int index, int stretch = 0);
void insertSpacing(int index, int size);
virtual size_t count() const;
virtual LayoutItemRef itemAt(size_t index);
virtual LayoutItemRef takeAt(size_t index);
virtual void clear();
/**
* Set the stretch factor of the item at position @a index to @a stretch.
*/
void setStretch(size_t index, int stretch);
/**
* Set the stretch factor of the given @a item to @a stretch, if it exists
* in this layout.
*
* @return true, if the @a item was found in the layout
*/
bool setStretchFactor(const LayoutItemRef& item, int stretch);
/**
* Get the stretch factor of the item at position @a index
*/
int stretch(size_t index) const;
virtual void setSpacing(int spacing);
virtual int spacing() const;
void setDirection(Direction dir);
Direction direction() const;
virtual bool hasHeightForWidth() const;
virtual void setCanvas(const CanvasWeakPtr& canvas);
bool horiz() const;
protected:
typedef const int& (SGVec2i::*CoordGetter)() const;
CoordGetter _get_layout_coord, //!< getter for coordinate in layout
// direction
_get_fixed_coord; //!< getter for coordinate in secondary
// (fixed) direction
int _padding;
Direction _direction;
typedef std::vector<ItemData> LayoutItems;
mutable LayoutItems _layout_items;
mutable ItemData _layout_data;
// Cache for last height-for-width query
mutable int _hfw_width,
_hfw_height,
_hfw_min_height;
void updateSizeHints() const;
void updateWFHCache(int w) const;
virtual SGVec2i sizeHintImpl() const;
virtual SGVec2i minimumSizeImpl() const;
virtual SGVec2i maximumSizeImpl() const;
virtual int heightForWidthImpl(int w) const;
virtual int minimumHeightForWidthImpl(int w) const;
virtual void doLayout(const SGRecti& geom);
virtual void visibilityChanged(bool visible);
};
/**
* Shortcut for creating a horizontal box layout
*/
class HBoxLayout:
public BoxLayout
{
public:
HBoxLayout();
};
/**
* Shortcut for creating a vertical box layout
*/
class VBoxLayout:
public BoxLayout
{
public:
VBoxLayout();
};
typedef SGSharedPtr<BoxLayout> BoxLayoutRef;
} // namespace canvas
} // namespace simgear
#endif /* SG_CANVAS_BOX_LAYOUT_HXX_ */

View File

@@ -1,25 +0,0 @@
include (SimGearComponent)
set(HEADERS
AlignFlag_values.hxx
BoxLayout.hxx
Layout.hxx
LayoutItem.hxx
NasalWidget.hxx
SpacerItem.hxx
)
set(SOURCES
BoxLayout.cxx
Layout.cxx
LayoutItem.cxx
NasalWidget.cxx
SpacerItem.cxx
)
simgear_scene_component(canvas-layout canvas/layout "${SOURCES}" "${HEADERS}")
add_boost_test(canvas_layout
SOURCES canvas_layout_test.cxx
LIBRARIES ${TEST_LIBS}
)

View File

@@ -1,334 +0,0 @@
// Basic class for canvas layouts
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "Layout.hxx"
#include <simgear/debug/logstream.hxx>
namespace simgear
{
namespace canvas
{
//----------------------------------------------------------------------------
void Layout::removeItem(const LayoutItemRef& item)
{
size_t i = 0;
while( LayoutItemRef child = itemAt(i) )
{
if( item == child )
return (void)takeAt(i);
++i;
}
}
//----------------------------------------------------------------------------
void Layout::clear()
{
while( itemAt(0) )
takeAt(0);
}
//----------------------------------------------------------------------------
SGRecti Layout::alignmentRect(const SGRecti& geom) const
{
return alignment() == AlignFill
// Without explicit alignment (default == AlignFill) use the whole
// available space and let the layout and its items distribute the
// excess space.
? geom
// Otherwise align according to flags.
: LayoutItem::alignmentRect(geom);
}
//----------------------------------------------------------------------------
void Layout::ItemData::reset()
{
layout_item = 0;
size_hint = 0;
min_size = 0;
max_size = 0;
padding_orig= 0;
padding = 0;
size = 0;
stretch = 0;
visible = false;
has_align = false;
has_hfw = false;
done = false;
}
//----------------------------------------------------------------------------
int Layout::ItemData::hfw(int w) const
{
if( has_hfw )
return layout_item->heightForWidth(w);
else
return layout_item->sizeHint().y();
}
//----------------------------------------------------------------------------
int Layout::ItemData::mhfw(int w) const
{
if( has_hfw )
return layout_item->minimumHeightForWidth(w);
else
return layout_item->minimumSize().y();
}
//----------------------------------------------------------------------------
Layout::Layout():
_num_not_done(0),
_sum_stretch(0),
_space_stretch(0),
_space_left(0)
{
}
//----------------------------------------------------------------------------
void Layout::contentsRectChanged(const SGRecti& rect)
{
doLayout(rect);
_flags &= ~LAYOUT_DIRTY;
}
//----------------------------------------------------------------------------
void Layout::distribute(std::vector<ItemData>& items, const ItemData& space)
{
const int num_children = static_cast<int>(items.size());
_num_not_done = 0;
SG_LOG( SG_GUI,
SG_DEBUG,
"Layout::distribute(" << space.size << "px for "
<< num_children << " items, s.t."
<< " min=" << space.min_size
<< ", hint=" << space.size_hint
<< ", max=" << space.max_size << ")" );
if( space.size < space.min_size )
{
// TODO
SG_LOG( SG_GUI, SG_WARN, "Layout: not enough size (not implemented)");
}
else if( space.size < space.max_size )
{
_sum_stretch = 0;
_space_stretch = 0;
bool less_then_hint = space.size < space.size_hint;
// Give min_size/size_hint to all items
_space_left = space.size
- (less_then_hint ? space.min_size : space.size_hint);
for(int i = 0; i < num_children; ++i)
{
ItemData& d = items[i];
if( !d.visible )
continue;
d.size = less_then_hint ? d.min_size : d.size_hint;
d.padding = d.padding_orig;
d.done = d.size >= (less_then_hint ? d.size_hint : d.max_size);
SG_LOG(
SG_GUI,
SG_DEBUG,
i << ") initial=" << d.size
<< ", min=" << d.min_size
<< ", hint=" << d.size_hint
<< ", max=" << d.max_size
);
if( d.done )
continue;
_num_not_done += 1;
if( d.stretch > 0 )
{
_sum_stretch += d.stretch;
_space_stretch += d.size;
}
}
// Distribute remaining space to increase the size of each item up to its
// size_hint/max_size
while( _space_left > 0 )
{
if( _num_not_done <= 0 )
{
SG_LOG(SG_GUI, SG_WARN, "space left, but no more items?");
break;
}
int space_per_element = std::max(1, _space_left / _num_not_done);
SG_LOG(SG_GUI, SG_DEBUG, "space/element=" << space_per_element);
for(int i = 0; i < num_children; ++i)
{
ItemData& d = items[i];
if( !d.visible )
continue;
SG_LOG(
SG_GUI,
SG_DEBUG,
i << ") left=" << _space_left
<< ", not_done=" << _num_not_done
<< ", sum=" << _sum_stretch
<< ", stretch=" << _space_stretch
<< ", stretch/unit=" << _space_stretch / std::max(1, _sum_stretch)
);
if( d.done )
continue;
if( _sum_stretch > 0 && d.stretch <= 0 )
d.done = true;
else
{
int target_size = 0;
int max_size = less_then_hint ? d.size_hint : d.max_size;
if( _sum_stretch > 0 )
{
target_size = (d.stretch * (_space_left + _space_stretch))
/ _sum_stretch;
// Item would be smaller than minimum size or larger than maximum
// size, so just keep bounded size and ignore stretch factor
if( target_size <= d.size || target_size >= max_size )
{
d.done = true;
_sum_stretch -= d.stretch;
_space_stretch -= d.size;
if( target_size >= max_size )
target_size = max_size;
else
target_size = d.size;
}
else
_space_stretch += target_size - d.size;
}
else
{
// Give space evenly to all remaining elements in this round
target_size = d.size + std::min(_space_left, space_per_element);
if( target_size >= max_size )
{
d.done = true;
target_size = max_size;
}
}
int old_size = d.size;
d.size = target_size;
_space_left -= d.size - old_size;
}
if( d.done )
{
_num_not_done -= 1;
if( _sum_stretch <= 0 && d.stretch > 0 )
// Distribute remaining space evenly to all non-stretchable items
// in a new round
break;
}
}
}
}
else
{
_space_left = space.size - space.max_size;
int num_align = 0;
for(int i = 0; i < num_children; ++i)
{
if( !items[i].visible )
continue;
_num_not_done += 1;
if( items[i].has_align )
num_align += 1;
}
SG_LOG(
SG_GUI,
SG_DEBUG,
"Distributing excess space:"
" not_done=" << _num_not_done
<< ", num_align=" << num_align
<< ", space_left=" << _space_left
);
for(int i = 0; i < num_children; ++i)
{
ItemData& d = items[i];
if( !d.visible )
continue;
d.padding = d.padding_orig;
d.size = d.max_size;
int space_add = 0;
if( d.has_align )
{
// Equally distribute superfluous space and let each child items
// alignment handle the exact usage.
space_add = _space_left / num_align;
num_align -= 1;
d.size += space_add;
}
else if( num_align <= 0 )
{
// Add superfluous space as padding
space_add = _space_left
// Padding after last child...
/ (_num_not_done + 1);
_num_not_done -= 1;
d.padding += space_add;
}
_space_left -= space_add;
}
}
SG_LOG(SG_GUI, SG_DEBUG, "distribute:");
for(int i = 0; i < num_children; ++i)
{
ItemData const& d = items[i];
if( d.visible )
SG_LOG(SG_GUI, SG_DEBUG, i << ") pad=" << d.padding
<< ", size= " << d.size);
else
SG_LOG(SG_GUI, SG_DEBUG, i << ") [hidden]");
}
}
} // namespace canvas
} // namespace simgear

View File

@@ -1,143 +0,0 @@
/// @file
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_CANVAS_LAYOUT_HXX_
#define SG_CANVAS_LAYOUT_HXX_
#include "LayoutItem.hxx"
#include <vector>
namespace simgear
{
namespace canvas
{
/**
* Base class for all Canvas layouts.
*/
class Layout:
public LayoutItem
{
public:
virtual void addItem(const LayoutItemRef& item) = 0;
virtual void setSpacing(int spacing) = 0;
virtual int spacing() const = 0;
/**
* Get the number of items.
*/
virtual size_t count() const = 0;
/**
* Get the item at position @a index.
*
* If there is no such item the function must do nothing and return an
* empty reference.
*/
virtual LayoutItemRef itemAt(size_t index) = 0;
/**
* Remove and get the item at position @a index.
*
* If there is no such item the function must do nothing and return an
* empty reference.
*/
virtual LayoutItemRef takeAt(size_t index) = 0;
/**
* Remove the given @a item from the layout.
*/
void removeItem(const LayoutItemRef& item);
/**
* Remove all items.
*/
virtual void clear();
/**
* Get the actual geometry of this layout given the rectangle \a geom
* taking into account the alignment flags and size hints. For layouts,
* if no alignment (different to AlignFill) is set, the whole area is
* used. Excess space is distributed by the layouting algorithm and
* handled by the individual children.
*
* @param geom Area available to this layout.
* @return The resulting geometry for this layout.
*/
virtual SGRecti alignmentRect(const SGRecti& geom) const;
protected:
enum LayoutFlags
{
LAST_FLAG = LayoutItem::LAST_FLAG
};
struct ItemData
{
LayoutItemRef layout_item;
int size_hint,
min_size,
max_size,
padding_orig, //!< original padding as specified by the user
padding, //!< padding before element (layouted)
size, //!< layouted size
stretch; //!< stretch factor
bool visible : 1,
has_align: 1, //!< Has alignment factor set (!= AlignFill)
has_hfw : 1, //!< height for width
done : 1; //!< layouting done
/** Clear values (reset to default/empty state) */
void reset();
int hfw(int w) const;
int mhfw(int w) const;
};
Layout();
virtual void contentsRectChanged(const SGRecti& rect);
/**
* Override to implement the actual layouting
*/
virtual void doLayout(const SGRecti& geom) = 0;
/**
* Distribute the available @a space to all @a items
*/
void distribute(std::vector<ItemData>& items, const ItemData& space);
private:
int _num_not_done, //!< number of children not layouted yet
_sum_stretch, //!< sum of stretch factors of all not yet layouted
// children
_space_stretch,//!< space currently assigned to all not yet layouted
// stretchable children
_space_left; //!< remaining space not used by any child yet
};
typedef SGSharedPtr<Layout> LayoutRef;
} // namespace canvas
} // namespace simgear
#endif /* SG_CANVAS_LAYOUT_HXX_ */

View File

@@ -1,393 +0,0 @@
// Basic element for layouting canvas elements
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "LayoutItem.hxx"
#include <simgear/canvas/Canvas.hxx>
namespace simgear
{
namespace canvas
{
//----------------------------------------------------------------------------
Margins::Margins(int m):
l(m), t(m), r(m), b(m)
{
}
//----------------------------------------------------------------------------
Margins::Margins(int h, int v):
l(h), t(v),
r(h), b(v)
{
}
//----------------------------------------------------------------------------
Margins::Margins(int l, int t, int r, int b):
l(l), t(t), r(r), b(b)
{
}
//----------------------------------------------------------------------------
int Margins::horiz() const
{
return l + r;
}
//----------------------------------------------------------------------------
int Margins::vert() const
{
return t + b;
}
//----------------------------------------------------------------------------
SGVec2i Margins::size() const
{
return SGVec2i(horiz(), vert());
}
//----------------------------------------------------------------------------
bool Margins::isNull() const
{
return l == 0 && t == 0 && r == 0 && b == 0;
}
//----------------------------------------------------------------------------
const SGVec2i LayoutItem::MAX_SIZE( SGLimits<int>::max(),
SGLimits<int>::max() );
//----------------------------------------------------------------------------
LayoutItem::LayoutItem():
_alignment(AlignFill),
_flags(VISIBLE),
_size_hint(0, 0),
_min_size(0, 0),
_max_size(MAX_SIZE)
{
invalidate();
}
//----------------------------------------------------------------------------
LayoutItem::~LayoutItem()
{
}
//----------------------------------------------------------------------------
void LayoutItem::setContentsMargins(const Margins& margins)
{
_margins = margins;
}
//----------------------------------------------------------------------------
void LayoutItem::setContentsMargins(int left, int top, int right, int bottom)
{
_margins.l = left;
_margins.t = top;
_margins.r = right;
_margins.b = bottom;
}
//----------------------------------------------------------------------------
void LayoutItem::setContentsMargin(int margin)
{
setContentsMargins(margin, margin, margin, margin);
}
//----------------------------------------------------------------------------
Margins LayoutItem::getContentsMargins() const
{
return _margins;
}
//----------------------------------------------------------------------------
SGRecti LayoutItem::contentsRect() const
{
return SGRecti(
_geometry.x() + _margins.l,
_geometry.y() + _margins.t,
std::max(0, _geometry.width() - _margins.horiz()),
std::max(0, _geometry.height() - _margins.vert())
);
}
//----------------------------------------------------------------------------
SGVec2i LayoutItem::sizeHint() const
{
if( _flags & SIZE_HINT_DIRTY )
{
_size_hint = sizeHintImpl();
_flags &= ~SIZE_HINT_DIRTY;
}
return addClipOverflow(_size_hint, _margins.size());
}
//----------------------------------------------------------------------------
SGVec2i LayoutItem::minimumSize() const
{
if( _flags & MINIMUM_SIZE_DIRTY )
{
_min_size = minimumSizeImpl();
_flags &= ~MINIMUM_SIZE_DIRTY;
}
return addClipOverflow(_min_size, _margins.size());
}
//----------------------------------------------------------------------------
SGVec2i LayoutItem::maximumSize() const
{
if( _flags & MAXIMUM_SIZE_DIRTY )
{
_max_size = maximumSizeImpl();
_flags &= ~MAXIMUM_SIZE_DIRTY;
}
return addClipOverflow(_max_size, _margins.size());
}
//----------------------------------------------------------------------------
bool LayoutItem::hasHeightForWidth() const
{
return false;
}
//----------------------------------------------------------------------------
int LayoutItem::heightForWidth(int w) const
{
int h = heightForWidthImpl(w - _margins.horiz());
return h < 0 ? -1 : SGMisc<int>::addClipOverflow(h, _margins.vert());
}
//------------------------------------------------------------------------------
int LayoutItem::minimumHeightForWidth(int w) const
{
int h = minimumHeightForWidthImpl(w - _margins.horiz());
return h < 0 ? -1 : SGMisc<int>::addClipOverflow(h, _margins.vert());
}
//----------------------------------------------------------------------------
void LayoutItem::setAlignment(uint8_t align)
{
if( align == _alignment )
return;
_alignment = align;
invalidateParent();
}
//----------------------------------------------------------------------------
uint8_t LayoutItem::alignment() const
{
return _alignment;
}
//----------------------------------------------------------------------------
void LayoutItem::setVisible(bool visible)
{
if( visible )
_flags &= ~EXPLICITLY_HIDDEN;
else
_flags |= EXPLICITLY_HIDDEN;
setVisibleInternal(visible);
}
//----------------------------------------------------------------------------
bool LayoutItem::isVisible() const
{
return _flags & VISIBLE;
}
//----------------------------------------------------------------------------
bool LayoutItem::isExplicitlyHidden() const
{
return _flags & EXPLICITLY_HIDDEN;
}
//----------------------------------------------------------------------------
void LayoutItem::invalidate()
{
_flags |= SIZE_INFO_DIRTY | LAYOUT_DIRTY;
invalidateParent();
}
//----------------------------------------------------------------------------
void LayoutItem::invalidateParent()
{
LayoutItemRef parent = _parent.lock();
if( parent )
parent->invalidate();
}
//----------------------------------------------------------------------------
void LayoutItem::update()
{
if( (_flags & LAYOUT_DIRTY) && isVisible() )
contentsRectChanged( contentsRect() );
}
//----------------------------------------------------------------------------
void LayoutItem::setGeometry(const SGRecti& geom)
{
SGRecti ar = alignmentRect(geom);
if( ar != _geometry )
{
_geometry = ar;
_flags |= LAYOUT_DIRTY;
}
update();
}
//----------------------------------------------------------------------------
SGRecti LayoutItem::geometry() const
{
return _geometry;
}
//----------------------------------------------------------------------------
SGRecti LayoutItem::alignmentRect(const SGRecti& geom) const
{
uint8_t halign = alignment() & AlignHorizontal_Mask,
valign = alignment() & AlignVertical_Mask;
// Size
SGVec2i size = sizeHint();
if( halign == AlignFill )
size.x() = maximumSize().x();
size.x() = std::min(size.x(), geom.width());
if( valign == AlignFill )
size.y() = maximumSize().y();
else if( hasHeightForWidth() )
size.y() = heightForWidth(size.x());
size.y() = std::min(size.y(), geom.height());
// Position
SGVec2i pos = geom.pos();
if( halign & AlignRight )
pos.x() += geom.width() - size.x();
else if( !(halign & AlignLeft) )
pos.x() += (geom.width() - size.x()) / 2;
if( valign & AlignBottom )
pos.y() += geom.height() - size.y();
else if( !(valign & AlignTop) )
pos.y() += (geom.height() - size.y()) / 2;
return SGRecti(pos, pos + size);
}
//----------------------------------------------------------------------------
void LayoutItem::setCanvas(const CanvasWeakPtr& canvas)
{
_canvas = canvas;
}
//----------------------------------------------------------------------------
CanvasPtr LayoutItem::getCanvas() const
{
return _canvas.lock();
}
//----------------------------------------------------------------------------
void LayoutItem::setParent(const LayoutItemWeakRef& parent)
{
_parent = parent;
LayoutItemRef parent_ref = parent.lock();
if( parent_ref )
// Only change the canvas if there is a new parent. If the item is removed
// keep the old canvas, as it may be used for example during the call to
// onRemove.
setCanvas(parent_ref->_canvas);
setVisibleInternal(!parent_ref || parent_ref->isVisible());
}
//----------------------------------------------------------------------------
LayoutItemRef LayoutItem::getParent() const
{
return _parent.lock();
}
//----------------------------------------------------------------------------
SGVec2i LayoutItem::sizeHintImpl() const
{
return _size_hint;
}
//----------------------------------------------------------------------------
SGVec2i LayoutItem::minimumSizeImpl() const
{
return _min_size;
}
//----------------------------------------------------------------------------
SGVec2i LayoutItem::maximumSizeImpl() const
{
return _max_size;
}
//----------------------------------------------------------------------------
int LayoutItem::heightForWidthImpl(int w) const
{
return -1;
}
//------------------------------------------------------------------------------
int LayoutItem::minimumHeightForWidthImpl(int w) const
{
return heightForWidth(w);
}
//----------------------------------------------------------------------------
void LayoutItem::setVisibleInternal(bool visible)
{
LayoutItemRef parent = getParent();
if( isExplicitlyHidden() || (parent && !parent->isVisible()) )
visible = false;
if( isVisible() == visible )
return;
invalidateParent();
if( visible )
_flags |= VISIBLE;
else
_flags &= ~VISIBLE;
visibilityChanged(visible);
}
//----------------------------------------------------------------------------
void LayoutItem::callSetVisibleInternal(LayoutItem* item, bool visible)
{
item->setVisibleInternal(visible);
}
} // namespace canvas
} // namespace simgear

View File

@@ -1,371 +0,0 @@
///@file
/// Basic element for layouting canvas elements.
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_CANVAS_LAYOUT_ITEM_HXX_
#define SG_CANVAS_LAYOUT_ITEM_HXX_
#include <simgear/canvas/canvas_fwd.hxx>
#include <simgear/math/SGMath.hxx>
#include <simgear/math/SGRect.hxx>
#include <simgear/misc/stdint.hxx>
#include <simgear/structure/SGWeakReferenced.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
namespace simgear
{
namespace canvas
{
class LayoutItem;
typedef SGSharedPtr<LayoutItem> LayoutItemRef;
typedef SGWeakPtr<LayoutItem> LayoutItemWeakRef;
/**
* Holds the four margins for a rectangle.
*/
struct Margins
{
int l, t, r, b;
/**
* Set all margins to the same value @a m.
*/
explicit Margins(int m = 0);
/**
* Set horizontal and vertical margins to the same values @a h and @a v
* respectively.
*
* @param h Horizontal margins
* @param v Vertical margins
*/
Margins(int h, int v);
/**
* Set the margins to the given values.
*/
Margins(int left, int top, int right, int bottom);
/**
* Get the total horizontal margin (sum of left and right margin).
*/
int horiz() const;
/**
* Get the total vertical margin (sum of top and bottom margin).
*/
int vert() const;
/**
* Get total horizontal and vertical margin as vector.
*/
SGVec2i size() const;
/**
* Returns true if all margins are 0.
*/
bool isNull() const;
};
/**
* Flags for LayoutItem alignment inside {@link Layout Layouts}.
*
* @note You can only use one horizontal and one vertical flag at the same.
*/
enum AlignmentFlag
{
#define ALIGN_ENUM_MAPPING(key, val, comment) key = val, /*!< comment */
# include "AlignFlag_values.hxx"
#undef ALIGN_ENUM_MAPPING
};
/**
* Base class for all layouting elements. Specializations either implement a
* layouting algorithm or a widget.
*/
class LayoutItem:
public virtual SGVirtualWeakReferenced
{
public:
/** Maximum item size (indicating no limit) */
static const SGVec2i MAX_SIZE;
LayoutItem();
virtual ~LayoutItem();
/**
* Set the margins to use by the layout system around the item.
*
* The margins define the size of the clear area around an item. It
* increases the size hints and reduces the size of the geometry()
* available to child layouts and widgets.
*
* @see Margins
*/
void setContentsMargins(const Margins& margins);
/**
* Set the individual margins.
*
* @see setContentsMargins(const Margins&)
*/
void setContentsMargins(int left, int top, int right, int bottom);
/**
* Set all margins to the same value.
*
* @see setContentsMargins(const Margins&)
*/
void setContentsMargin(int margin);
/**
* Get the currently used margins.
*
* @see setContentsMargins(const Margins&)
* @see Margins
*/
Margins getContentsMargins() const;
/**
* Get the area available to the contents.
*
* This is equal to the geometry() reduced by the sizes of the margins.
*
* @see setContentsMargins(const Margins&)
* @see geometry()
*/
SGRecti contentsRect() const;
/**
* Get the preferred size of this item.
*/
SGVec2i sizeHint() const;
/**
* Get the minimum amount of the space this item requires.
*/
SGVec2i minimumSize() const;
/**
* Get the maximum amount of space this item can use.
*/
SGVec2i maximumSize() const;
/**
* Returns true if this items preferred and minimum height depend on its
* width.
*
* The default implementation returns false. Reimplement for items
* providing height for width.
*
* @see heightForWidth()
* @see minimumHeightForWidth()
*/
virtual bool hasHeightForWidth() const;
/**
* Returns the preferred height for the given width @a w.
*
* Reimplement heightForWidthImpl() for items providing height for width.
*
* @see hasHeightForWidth()
*/
int heightForWidth(int w) const;
/**
* Returns the minimum height for the given width @a w.
*
* Reimplement minimumHeightForWidthImpl() for items providing height for
* width.
*
* @see hasHeightForWidth()
*/
int minimumHeightForWidth(int w) const;
/**
* Set alignment of item within {@link Layout Layouts}.
*
* @param alignment Bitwise combination of vertical and horizontal
* alignment flags.
* @see AlignmentFlag
*/
void setAlignment(uint8_t alignment);
/**
* Get all alignment flags.
*
* @see AlignmentFlag
*/
uint8_t alignment() const;
virtual void setVisible(bool visible);
virtual bool isVisible() const;
bool isExplicitlyHidden() const;
void show() { setVisible(true); }
void hide() { setVisible(false); }
/**
* Mark all cached data as invalid and require it to be recalculated.
*/
virtual void invalidate();
/**
* Mark all cached data of parent item as invalid (if the parent is set).
*/
void invalidateParent();
/**
* Apply any changes not applied yet.
*/
void update();
/**
* Set position and size of this element. For layouts this triggers a
* recalculation of the layout.
*/
virtual void setGeometry(const SGRecti& geom);
/**
* Get position and size of this element.
*/
virtual SGRecti geometry() const;
/**
* Get the actual geometry of this item given the rectangle \a geom
* taking into account the alignment flags and size hints.
*
* @param geom Area available to this item.
* @return The resulting geometry for this item.
*
* @see setAlignment()
* @see minimumSize()
* @see maximumSize()
* @see sizeHint()
*/
virtual SGRecti alignmentRect(const SGRecti& geom) const;
/**
* Set the canvas this item is attached to.
*/
virtual void setCanvas(const CanvasWeakPtr& canvas);
/**
* Get the canvas this item is attached to.
*/
CanvasPtr getCanvas() const;
/**
* Set the parent layout item (usually this is a layout).
*/
void setParent(const LayoutItemWeakRef& parent);
/**
* Get the parent layout.
*/
LayoutItemRef getParent() const;
/// Called before item is removed from a layout
virtual void onRemove() {}
protected:
friend class Canvas;
enum Flags
{
SIZE_HINT_DIRTY = 1,
MINIMUM_SIZE_DIRTY = SIZE_HINT_DIRTY << 1,
MAXIMUM_SIZE_DIRTY = MINIMUM_SIZE_DIRTY << 1,
SIZE_INFO_DIRTY = SIZE_HINT_DIRTY
| MINIMUM_SIZE_DIRTY
| MAXIMUM_SIZE_DIRTY,
EXPLICITLY_HIDDEN = MAXIMUM_SIZE_DIRTY << 1,
VISIBLE = EXPLICITLY_HIDDEN << 1,
LAYOUT_DIRTY = VISIBLE << 1,
LAST_FLAG = LAYOUT_DIRTY
};
CanvasWeakPtr _canvas;
LayoutItemWeakRef _parent;
SGRecti _geometry;
Margins _margins;
uint8_t _alignment;
mutable uint32_t _flags;
mutable SGVec2i _size_hint,
_min_size,
_max_size;
virtual SGVec2i sizeHintImpl() const;
virtual SGVec2i minimumSizeImpl() const;
virtual SGVec2i maximumSizeImpl() const;
/**
* Returns the preferred height for the given width @a w.
*
* The default implementation returns -1, indicating that the preferred
* height is independent of the given width.
*
* Reimplement this function for items supporting height for width.
*
* @note Do not take margins into account, as this is already handled
* before calling this function.
*
* @see hasHeightForWidth()
*/
virtual int heightForWidthImpl(int w) const;
/**
* Returns the minimum height for the given width @a w.
*
* The default implementation returns -1, indicating that the minimum
* height is independent of the given width.
*
* Reimplement this function for items supporting height for width.
*
* @note Do not take margins into account, as this is already handled
* before calling this function.
*
* @see hasHeightForWidth()
*/
virtual int minimumHeightForWidthImpl(int w) const;
/**
* @return whether the visibility has changed.
*/
void setVisibleInternal(bool visible);
virtual void contentsRectChanged(const SGRecti& rect) {};
virtual void visibilityChanged(bool visible) {}
/**
* Allow calling the protected setVisibleImpl from derived classes
*/
static void callSetVisibleInternal(LayoutItem* item, bool visible);
};
} // namespace canvas
} // namespace simgear
#endif /* SG_CANVAS_LAYOUT_ITEM_HXX_ */

View File

@@ -1,295 +0,0 @@
// Glue for GUI layout items implemented in Nasal space
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "NasalWidget.hxx"
#include <simgear/canvas/Canvas.hxx>
#include <simgear/nasal/cppbind/NasalContext.hxx>
#include <simgear/nasal/cppbind/Ghost.hxx>
namespace simgear
{
namespace canvas
{
//----------------------------------------------------------------------------
NasalWidget::NasalWidget(naRef impl):
Object(impl),
_layout_size_hint(32, 32),
_layout_min_size(16, 16),
_layout_max_size(MAX_SIZE),
_user_size_hint(0, 0),
_user_min_size(0, 0),
_user_max_size(MAX_SIZE)
{
}
//----------------------------------------------------------------------------
NasalWidget::~NasalWidget()
{
onRemove();
}
//----------------------------------------------------------------------------
void NasalWidget::onRemove()
{
try
{
callMethod<void>("onRemove");
}
catch( std::exception const& ex )
{
SG_LOG(
SG_GUI,
SG_WARN,
"NasalWidget::onRemove: callback error: '" << ex.what() << "'"
);
}
}
//----------------------------------------------------------------------------
void NasalWidget::setSetGeometryFunc(const SetGeometryFunc& func)
{
_set_geometry = func;
}
//----------------------------------------------------------------------------
void NasalWidget::setHeightForWidthFunc(const HeightForWidthFunc& func)
{
_height_for_width = func;
invalidate();
}
//----------------------------------------------------------------------------
void NasalWidget::setMinimumHeightForWidthFunc(const HeightForWidthFunc& func)
{
_min_height_for_width = func;
invalidate();
}
//----------------------------------------------------------------------------
void NasalWidget::setSizeHint(const SGVec2i& s)
{
if( _user_size_hint == s )
return;
_user_size_hint = s;
// TODO just invalidate size_hint? Probably not a performance issue...
invalidate();
}
//----------------------------------------------------------------------------
void NasalWidget::setMinimumSize(const SGVec2i& s)
{
if( _user_min_size == s )
return;
_user_min_size = s;
invalidate();
}
//----------------------------------------------------------------------------
void NasalWidget::setMaximumSize(const SGVec2i& s)
{
if( _user_max_size == s )
return;
_user_max_size = s;
invalidate();
}
//----------------------------------------------------------------------------
void NasalWidget::setLayoutSizeHint(const SGVec2i& s)
{
if( _layout_size_hint == s )
return;
_layout_size_hint = s;
invalidate();
}
//----------------------------------------------------------------------------
void NasalWidget::setLayoutMinimumSize(const SGVec2i& s)
{
if( _layout_min_size == s )
return;
_layout_min_size = s;
invalidate();
}
//----------------------------------------------------------------------------
void NasalWidget::setLayoutMaximumSize(const SGVec2i& s)
{
if( _layout_max_size == s )
return;
_layout_max_size = s;
invalidate();
}
//----------------------------------------------------------------------------
bool NasalWidget::hasHeightForWidth() const
{
return !_height_for_width.empty() || !_min_height_for_width.empty();
}
//----------------------------------------------------------------------------
static naRef f_makeNasalWidget(const nasal::CallContext& ctx)
{
return ctx.to_nasal(NasalWidgetRef(
new NasalWidget( ctx.requireArg<naRef>(0) )
));
}
//----------------------------------------------------------------------------
void NasalWidget::setupGhost(nasal::Hash& ns)
{
nasal::Ghost<NasalWidgetRef>::init("canvas.Widget")
.bases<LayoutItemRef>()
.bases<nasal::ObjectRef>()
.method("setSetGeometryFunc", &NasalWidget::setSetGeometryFunc)
.method("setMinimumHeightForWidthFunc",
&NasalWidget::setMinimumHeightForWidthFunc)
.method("setHeightForWidthFunc", &NasalWidget::setHeightForWidthFunc)
.method("setSizeHint", &NasalWidget::setSizeHint)
.method("setMinimumSize", &NasalWidget::setMinimumSize)
.method("setMaximumSize", &NasalWidget::setMaximumSize)
.method("setLayoutSizeHint", &NasalWidget::setLayoutSizeHint)
.method("setLayoutMinimumSize", &NasalWidget::setLayoutMinimumSize)
.method("setLayoutMaximumSize", &NasalWidget::setLayoutMaximumSize);
nasal::Hash widget_hash = ns.createHash("Widget");
widget_hash.set("new", &f_makeNasalWidget);
}
//----------------------------------------------------------------------------
int NasalWidget::callHeightForWidthFunc( const HeightForWidthFunc& hfw,
int w ) const
{
if( hfw.empty() )
return -1;
naContext c = naNewContext();
try
{
return hfw(nasal::to_nasal(c, const_cast<NasalWidget*>(this)), w);
}
catch( std::exception const& ex )
{
SG_LOG(
SG_GUI,
SG_WARN,
"NasalWidget.heightForWidth: callback error: '" << ex.what() << "'"
);
}
naFreeContext(c);
return -1;
}
//----------------------------------------------------------------------------
SGVec2i NasalWidget::sizeHintImpl() const
{
return SGVec2i(
_user_size_hint.x() > 0 ? _user_size_hint.x() : _layout_size_hint.x(),
_user_size_hint.y() > 0 ? _user_size_hint.y() : _layout_size_hint.y()
);
}
//----------------------------------------------------------------------------
SGVec2i NasalWidget::minimumSizeImpl() const
{
return SGVec2i(
_user_min_size.x() > 0 ? _user_min_size.x() : _layout_min_size.x(),
_user_min_size.y() > 0 ? _user_min_size.y() : _layout_min_size.y()
);
}
//----------------------------------------------------------------------------
SGVec2i NasalWidget::maximumSizeImpl() const
{
return SGVec2i(
_user_max_size.x() < MAX_SIZE.x() ? _user_max_size.x()
: _layout_max_size.x(),
_user_max_size.y() < MAX_SIZE.y() ? _user_max_size.y()
: _layout_max_size.y()
);
}
//----------------------------------------------------------------------------
int NasalWidget::heightForWidthImpl(int w) const
{
return callHeightForWidthFunc( _height_for_width.empty()
? _min_height_for_width
: _height_for_width, w );
}
//----------------------------------------------------------------------------
int NasalWidget::minimumHeightForWidthImpl(int w) const
{
return callHeightForWidthFunc( _min_height_for_width.empty()
? _height_for_width
: _min_height_for_width, w );
}
//----------------------------------------------------------------------------
void NasalWidget::contentsRectChanged(const SGRect<int>& rect)
{
if( !_set_geometry )
return;
try
{
nasal::Context c;
_set_geometry(nasal::to_nasal(c, this), rect);
_flags &= ~LAYOUT_DIRTY;
}
catch( std::exception const& ex )
{
SG_LOG(
SG_GUI,
SG_WARN,
"NasalWidget::setGeometry: callback error: '" << ex.what() << "'"
);
}
}
//----------------------------------------------------------------------------
void NasalWidget::visibilityChanged(bool visible)
{
try
{
callMethod<void>("visibilityChanged", visible);
}
catch( std::exception const& ex )
{
SG_LOG(
SG_GUI,
SG_WARN,
"NasalWidget::visibilityChanged: callback error: '" << ex.what() << "'"
);
}
}
} // namespace canvas
} // namespace simgear

View File

@@ -1,133 +0,0 @@
///@file
/// Glue for GUI widgets implemented in Nasal space.
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_CANVAS_NASAL_WIDGET_HXX_
#define SG_CANVAS_NASAL_WIDGET_HXX_
#include "LayoutItem.hxx"
#include <simgear/nasal/cppbind/from_nasal.hxx>
#include <simgear/nasal/cppbind/NasalHash.hxx>
#include <simgear/nasal/cppbind/NasalObject.hxx>
namespace simgear
{
namespace canvas
{
/**
* Base class/ghost to implement gui widgets in Nasal space.
*/
class NasalWidget:
public LayoutItem,
public nasal::Object
{
public:
typedef boost::function<void (nasal::Me, const SGRecti&)> SetGeometryFunc;
typedef boost::function<int (nasal::Me, int)> HeightForWidthFunc;
/**
*
* @param impl Initial implementation hash (nasal part of
* implementation)
*/
NasalWidget(naRef impl);
~NasalWidget();
virtual void onRemove();
void setSetGeometryFunc(const SetGeometryFunc& func);
void setHeightForWidthFunc(const HeightForWidthFunc& func);
void setMinimumHeightForWidthFunc(const HeightForWidthFunc& func);
/** Set size hint.
*
* Overrides default size hint. Set to (0, 0) to fall back to default size
* hint.
*/
void setSizeHint(const SGVec2i& s);
/** Set minimum size.
*
* Overrides default minimum size. Set to (0, 0) to fall back to default
* minimum size.
*/
void setMinimumSize(const SGVec2i& s);
/** Set maximum size.
*
* Overrides default maximum size hint. Set to LayoutItem::MAX_SIZE to
* fall back to default maximum size.
*/
void setMaximumSize(const SGVec2i& s);
void setLayoutSizeHint(const SGVec2i& s);
void setLayoutMinimumSize(const SGVec2i& s);
void setLayoutMaximumSize(const SGVec2i& s);
virtual bool hasHeightForWidth() const;
/**
* @param ns Namespace to register the class interface
*/
static void setupGhost(nasal::Hash& ns);
protected:
enum WidgetFlags
{
LAYOUT_DIRTY = LayoutItem::LAST_FLAG << 1,
LAST_FLAG = LAYOUT_DIRTY
};
SetGeometryFunc _set_geometry;
HeightForWidthFunc _height_for_width,
_min_height_for_width;
SGVec2i _layout_size_hint,
_layout_min_size,
_layout_max_size,
_user_size_hint,
_user_min_size,
_user_max_size;
int callHeightForWidthFunc( const HeightForWidthFunc& hfw,
int w ) const;
virtual SGVec2i sizeHintImpl() const;
virtual SGVec2i minimumSizeImpl() const;
virtual SGVec2i maximumSizeImpl() const;
virtual int heightForWidthImpl(int w) const;
virtual int minimumHeightForWidthImpl(int w) const;
virtual void contentsRectChanged(const SGRecti& rect);
virtual void visibilityChanged(bool visible);
};
typedef SGSharedPtr<NasalWidget> NasalWidgetRef;
} // namespace canvas
} // namespace simgear
#endif /* SG_CANVAS_NASAL_WIDGET_HXX_ */

View File

@@ -1,36 +0,0 @@
// Element providing blank space in a layout.
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "SpacerItem.hxx"
namespace simgear
{
namespace canvas
{
//----------------------------------------------------------------------------
SpacerItem::SpacerItem( const SGVec2i& size,
const SGVec2i& max_size )
{
_size_hint = size;
_min_size = size;
_max_size = max_size;
}
} // namespace canvas
} // namespace simgear

View File

@@ -1,43 +0,0 @@
///@file
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_CANVAS_SPACER_ITEM_HXX_
#define SG_CANVAS_SPACER_ITEM_HXX_
#include "LayoutItem.hxx"
namespace simgear
{
namespace canvas
{
/**
* Element for providing blank space in a layout.
*/
class SpacerItem:
public LayoutItem
{
public:
SpacerItem( const SGVec2i& size = SGVec2i(0, 0),
const SGVec2i& max_size = MAX_SIZE );
};
} // namespace canvas
} // namespace simgear
#endif /* SG_CANVAS_SPACER_ITEM_HXX_ */

View File

@@ -1,735 +0,0 @@
// Testing canvas layouting system
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#define BOOST_TEST_MODULE canvas_layout
#include <BoostTestTargetConfig.h>
#include "BoxLayout.hxx"
#include "NasalWidget.hxx"
#include <simgear/debug/logstream.hxx>
#include <simgear/nasal/cppbind/NasalContext.hxx>
#include <cstdlib>
//------------------------------------------------------------------------------
struct SetLogLevelFixture
{
SetLogLevelFixture()
{
sglog().set_log_priority(SG_DEBUG);
}
};
BOOST_GLOBAL_FIXTURE(SetLogLevelFixture);
//------------------------------------------------------------------------------
namespace sc = simgear::canvas;
class TestWidget:
public sc::LayoutItem
{
public:
TestWidget( const SGVec2i& min_size,
const SGVec2i& size_hint,
const SGVec2i& max_size = MAX_SIZE )
{
_size_hint = size_hint;
_min_size = min_size;
_max_size = max_size;
}
TestWidget(const TestWidget& rhs)
{
_size_hint = rhs._size_hint;
_min_size = rhs._min_size;
_max_size = rhs._max_size;
}
void setMinSize(const SGVec2i& size) { _min_size = size; }
void setMaxSize(const SGVec2i& size) { _max_size = size; }
void setSizeHint(const SGVec2i& size) { _size_hint = size; }
protected:
virtual SGVec2i sizeHintImpl() const { return _size_hint; }
virtual SGVec2i minimumSizeImpl() const { return _min_size; }
virtual SGVec2i maximumSizeImpl() const { return _max_size; }
virtual void visibilityChanged(bool visible)
{
if( !visible )
_geometry.set(0, 0, 0, 0);
}
};
class TestWidgetHFW:
public TestWidget
{
public:
TestWidgetHFW( const SGVec2i& min_size,
const SGVec2i& size_hint,
const SGVec2i& max_size = MAX_SIZE ):
TestWidget(min_size, size_hint, max_size)
{
}
virtual bool hasHeightForWidth() const
{
return true;
}
virtual int heightForWidthImpl(int w) const
{
return _size_hint.x() * _size_hint.y() / w;
}
virtual int minimumHeightForWidthImpl(int w) const
{
return _min_size.x() * _min_size.y() / w;
}
};
typedef SGSharedPtr<TestWidget> TestWidgetRef;
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( horizontal_layout )
{
sc::BoxLayoutRef box_layout(new sc::BoxLayout(sc::BoxLayout::BottomToTop));
box_layout->setSpacing(5);
BOOST_CHECK_EQUAL(box_layout->direction(), sc::BoxLayout::BottomToTop);
BOOST_CHECK_EQUAL(box_layout->spacing(), 5);
box_layout->setDirection(sc::BoxLayout::LeftToRight);
box_layout->setSpacing(9);
BOOST_CHECK_EQUAL(box_layout->direction(), sc::BoxLayout::LeftToRight);
BOOST_CHECK_EQUAL(box_layout->spacing(), 9);
TestWidgetRef fixed_size_widget( new TestWidget( SGVec2i(16, 16),
SGVec2i(16, 16),
SGVec2i(16, 16) ) );
box_layout->addItem(fixed_size_widget);
BOOST_CHECK_EQUAL(box_layout->minimumSize(), SGVec2i(16, 16));
BOOST_CHECK_EQUAL(box_layout->sizeHint(), SGVec2i(16, 16));
BOOST_CHECK_EQUAL(box_layout->maximumSize(), SGVec2i(16, 16));
TestWidgetRef limited_resize_widget( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32),
SGVec2i(256, 64) ) );
box_layout->addItem(limited_resize_widget);
// Combined sizes of both widget plus the padding between them
BOOST_CHECK_EQUAL(box_layout->minimumSize(), SGVec2i(41, 16));
BOOST_CHECK_EQUAL(box_layout->sizeHint(), SGVec2i(57, 32));
BOOST_CHECK_EQUAL(box_layout->maximumSize(), SGVec2i(281, 64));
// Test with different spacing/padding
box_layout->setSpacing(5);
BOOST_CHECK_EQUAL(box_layout->minimumSize(), SGVec2i(37, 16));
BOOST_CHECK_EQUAL(box_layout->sizeHint(), SGVec2i(53, 32));
BOOST_CHECK_EQUAL(box_layout->maximumSize(), SGVec2i(277, 64));
box_layout->setGeometry(SGRecti(0, 0, 128, 32));
// Fixed size for first widget and remaining space goes to second widget
BOOST_CHECK_EQUAL(fixed_size_widget->geometry(), SGRecti(0, 8, 16, 16));
BOOST_CHECK_EQUAL(limited_resize_widget->geometry(), SGRecti(21, 0, 107, 32));
TestWidgetRef stretch_widget( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32),
SGVec2i(128, 32) ) );
box_layout->addItem(stretch_widget, 1);
box_layout->update();
BOOST_CHECK_EQUAL(box_layout->minimumSize(), SGVec2i(58, 16));
BOOST_CHECK_EQUAL(box_layout->sizeHint(), SGVec2i(90, 32));
BOOST_CHECK_EQUAL(box_layout->maximumSize(), SGVec2i(410, 64));
// Due to the stretch factor only the last widget gets additional space. All
// other widgets get the preferred size.
BOOST_CHECK_EQUAL(fixed_size_widget->geometry(), SGRecti(0, 8, 16, 16));
BOOST_CHECK_EQUAL(limited_resize_widget->geometry(), SGRecti(21, 0, 32, 32));
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(58, 0, 70, 32));
// Test stretch factor
TestWidgetRef fast_stretch( new TestWidget(*stretch_widget) );
sc::BoxLayoutRef box_layout_stretch(
new sc::BoxLayout(sc::BoxLayout::LeftToRight)
);
box_layout_stretch->addItem(stretch_widget, 1);
box_layout_stretch->addItem(fast_stretch, 2);
box_layout_stretch->setGeometry(SGRecti(0,0,128,32));
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(0, 0, 41, 32));
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(46, 0, 82, 32));
box_layout_stretch->setGeometry(SGRecti(0,0,256,32));
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(0, 0, 123, 32));
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(128, 0, 128, 32));
// Test superflous space to padding
box_layout_stretch->setGeometry(SGRecti(0,0,512,32));
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(83, 0, 128, 32));
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(300, 0, 128, 32));
// ...and now with alignment
//
// All widgets without alignment get their maximum space and the remaining
// space is equally distributed to the remaining items. All items with
// alignment are set to their size hint and positioned according to their
// alignment.
// Left widget: size hint and positioned on the left
// Right widget: maximum size and positioned on the right
stretch_widget->setAlignment(sc::AlignLeft);
box_layout_stretch->update();
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(0, 0, 32, 32));
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(384, 0, 128, 32));
// Left widget: align right
stretch_widget->setAlignment(sc::AlignRight);
box_layout_stretch->update();
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(347, 0, 32, 32));
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(384, 0, 128, 32));
// Left widget: size hint and positioned on the right
// Right widget: size hint and positioned on the left of the right half
fast_stretch->setAlignment(sc::AlignLeft);
box_layout_stretch->update();
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(221, 0, 32, 32));
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(258, 0, 32, 32));
// Also check vertical alignment
stretch_widget->setAlignment(sc::AlignLeft | sc::AlignTop);
fast_stretch->setAlignment(sc::AlignLeft | sc::AlignBottom);
box_layout_stretch->setGeometry(SGRecti(0,0,512,64));
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(0, 0, 32, 32));
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(258, 32, 32, 32));
}
//------------------------------------------------------------------------------
// Test more space then preferred, but less than maximum
BOOST_AUTO_TEST_CASE( hbox_pref_to_max )
{
sc::BoxLayoutRef hbox(new sc::HBoxLayout());
TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32),
SGVec2i(9999, 32) ) ),
w2( new TestWidget(*w1) );
hbox->addItem(w1);
hbox->addItem(w2);
hbox->setGeometry( SGRecti(0, 0, 256, 32) );
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 126, 32));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(131, 0, 125, 32));
hbox->setStretch(0, 1);
hbox->setStretch(1, 1);
BOOST_CHECK_EQUAL(hbox->stretch(0), 1);
BOOST_CHECK_EQUAL(hbox->stretch(1), 1);
hbox->update();
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 125, 32));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(130, 0, 126, 32));
BOOST_REQUIRE( hbox->setStretchFactor(w1, 2) );
BOOST_REQUIRE( hbox->setStretchFactor(w2, 3) );
BOOST_CHECK_EQUAL(hbox->stretch(0), 2);
BOOST_CHECK_EQUAL(hbox->stretch(1), 3);
hbox->removeItem(w1);
BOOST_CHECK( !hbox->setStretchFactor(w1, 0) );
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( spacer_layouting )
{
sc::HBoxLayout hbox;
TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32),
SGVec2i(9999, 9999) ) ),
w2( new TestWidget(*w1) );
hbox.addItem(w1);
hbox.addItem(w2);
hbox.addStretch(1);
BOOST_CHECK_EQUAL(hbox.minimumSize(), SGVec2i(37, 16));
BOOST_CHECK_EQUAL(hbox.sizeHint(), SGVec2i(69, 32));
BOOST_CHECK_EQUAL(hbox.maximumSize(), sc::LayoutItem::MAX_SIZE);
hbox.setGeometry(SGRecti(0, 0, 256, 40));
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 32, 40));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(37, 0, 32, 40));
// now center with increased spacing between both widgets
hbox.insertStretch(0, 1);
hbox.insertSpacing(2, 10);
BOOST_CHECK_EQUAL(hbox.minimumSize(), SGVec2i(47, 16));
BOOST_CHECK_EQUAL(hbox.sizeHint(), SGVec2i(79, 32));
BOOST_CHECK_EQUAL(hbox.maximumSize(), sc::LayoutItem::MAX_SIZE);
hbox.update();
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(88, 0, 32, 40));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(135, 0, 32, 40));
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( vertical_layout)
{
sc::BoxLayout vbox(sc::BoxLayout::TopToBottom);
vbox.setSpacing(7);
TestWidgetRef fixed_size_widget( new TestWidget( SGVec2i(16, 16),
SGVec2i(16, 16),
SGVec2i(16, 16) ) );
TestWidgetRef limited_resize_widget( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32),
SGVec2i(256, 64) ) );
vbox.addItem(fixed_size_widget);
vbox.addItem(limited_resize_widget);
BOOST_CHECK_EQUAL(vbox.minimumSize(), SGVec2i(16, 39));
BOOST_CHECK_EQUAL(vbox.sizeHint(), SGVec2i(32, 55));
BOOST_CHECK_EQUAL(vbox.maximumSize(), SGVec2i(256, 87));
vbox.setGeometry(SGRecti(10, 20, 16, 55));
BOOST_CHECK_EQUAL(fixed_size_widget->geometry(), SGRecti(10, 20, 16, 16));
BOOST_CHECK_EQUAL(limited_resize_widget->geometry(), SGRecti(10, 43, 16, 32));
vbox.setDirection(sc::BoxLayout::BottomToTop);
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( boxlayout_insert_remove )
{
sc::BoxLayoutRef hbox( new sc::HBoxLayout );
BOOST_CHECK_EQUAL(hbox->count(), 0);
BOOST_CHECK(!hbox->itemAt(0));
BOOST_CHECK(!hbox->takeAt(0));
TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32),
SGVec2i(9999, 32) ) ),
w2( new TestWidget(*w1) );
hbox->addItem(w1);
BOOST_CHECK_EQUAL(hbox->count(), 1);
BOOST_CHECK_EQUAL(hbox->itemAt(0), w1);
BOOST_CHECK_EQUAL(w1->getParent(), hbox);
hbox->insertItem(0, w2);
BOOST_CHECK_EQUAL(hbox->count(), 2);
BOOST_CHECK_EQUAL(hbox->itemAt(0), w2);
BOOST_CHECK_EQUAL(hbox->itemAt(1), w1);
BOOST_CHECK_EQUAL(w2->getParent(), hbox);
hbox->removeItem(w2);
BOOST_CHECK_EQUAL(hbox->count(), 1);
BOOST_CHECK_EQUAL(hbox->itemAt(0), w1);
BOOST_CHECK( !w2->getParent() );
hbox->addItem(w2);
BOOST_CHECK_EQUAL(hbox->count(), 2);
BOOST_CHECK_EQUAL(w2->getParent(), hbox);
hbox->clear();
BOOST_CHECK_EQUAL(hbox->count(), 0);
BOOST_CHECK( !w1->getParent() );
BOOST_CHECK( !w2->getParent() );
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( boxlayout_visibility )
{
sc::BoxLayoutRef hbox( new sc::HBoxLayout );
TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32) ) ),
w2( new TestWidget(*w1) ),
w3( new TestWidget(*w1) );
hbox->addItem(w1);
hbox->addItem(w2);
hbox->addItem(w3);
BOOST_REQUIRE_EQUAL(hbox->sizeHint().x(), 3 * 32 + 2 * hbox->spacing());
hbox->setGeometry(SGRecti(0, 0, 69, 32));
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 20, 32));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(25, 0, 20, 32));
BOOST_CHECK_EQUAL(w3->geometry(), SGRecti(50, 0, 19, 32));
w2->setVisible(false);
BOOST_REQUIRE(hbox->isVisible());
BOOST_REQUIRE(w1->isVisible());
BOOST_REQUIRE(!w2->isVisible());
BOOST_REQUIRE(w2->isExplicitlyHidden());
BOOST_REQUIRE(w3->isVisible());
BOOST_CHECK_EQUAL(hbox->sizeHint().x(), 2 * 32 + 1 * hbox->spacing());
hbox->update();
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 32, 32));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(0, 0, 0, 0));
BOOST_CHECK_EQUAL(w3->geometry(), SGRecti(37, 0, 32, 32));
hbox->setVisible(false);
BOOST_REQUIRE(!hbox->isVisible());
BOOST_REQUIRE(hbox->isExplicitlyHidden());
BOOST_REQUIRE(!w1->isVisible());
BOOST_REQUIRE(!w1->isExplicitlyHidden());
BOOST_REQUIRE(!w2->isVisible());
BOOST_REQUIRE(w2->isExplicitlyHidden());
BOOST_REQUIRE(!w3->isVisible());
BOOST_REQUIRE(!w3->isExplicitlyHidden());
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 0, 0));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(0, 0, 0, 0));
BOOST_CHECK_EQUAL(w3->geometry(), SGRecti(0, 0, 0, 0));
w2->setVisible(true);
BOOST_REQUIRE(!w2->isVisible());
BOOST_REQUIRE(!w2->isExplicitlyHidden());
hbox->setVisible(true);
BOOST_REQUIRE(hbox->isVisible());
BOOST_REQUIRE(w1->isVisible());
BOOST_REQUIRE(w2->isVisible());
BOOST_REQUIRE(w3->isVisible());
hbox->update();
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 20, 32));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(25, 0, 20, 32));
BOOST_CHECK_EQUAL(w3->geometry(), SGRecti(50, 0, 19, 32));
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( boxlayout_contents_margins )
{
sc::Margins m;
BOOST_REQUIRE(m.isNull());
m = sc::Margins(5);
BOOST_REQUIRE_EQUAL(m.l, 5);
BOOST_REQUIRE_EQUAL(m.t, 5);
BOOST_REQUIRE_EQUAL(m.r, 5);
BOOST_REQUIRE_EQUAL(m.b, 5);
m = sc::Margins(6, 7);
BOOST_REQUIRE_EQUAL(m.l, 6);
BOOST_REQUIRE_EQUAL(m.t, 7);
BOOST_REQUIRE_EQUAL(m.r, 6);
BOOST_REQUIRE_EQUAL(m.b, 7);
BOOST_REQUIRE_EQUAL(m.horiz(), 12);
BOOST_REQUIRE_EQUAL(m.vert(), 14);
BOOST_REQUIRE(!m.isNull());
m = sc::Margins(1, 2, 3, 4);
BOOST_REQUIRE_EQUAL(m.l, 1);
BOOST_REQUIRE_EQUAL(m.t, 2);
BOOST_REQUIRE_EQUAL(m.r, 3);
BOOST_REQUIRE_EQUAL(m.b, 4);
BOOST_REQUIRE_EQUAL(m.horiz(), 4);
BOOST_REQUIRE_EQUAL(m.vert(), 6);
BOOST_REQUIRE_EQUAL(m.size(), SGVec2i(4, 6));
sc::BoxLayoutRef hbox( new sc::HBoxLayout );
hbox->setContentsMargins(5, 10, 15, 20);
BOOST_CHECK_EQUAL(hbox->minimumSize(), SGVec2i(20, 30));
BOOST_CHECK_EQUAL(hbox->sizeHint(), SGVec2i(20, 30));
BOOST_CHECK_EQUAL(hbox->maximumSize(), SGVec2i(20, 30));
hbox->setGeometry(SGRecti(0, 0, 30, 40));
BOOST_CHECK_EQUAL(hbox->geometry(), SGRecti(0, 0, 30, 40));
BOOST_CHECK_EQUAL(hbox->contentsRect(), SGRecti(5, 10, 10, 10));
TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32) ) ),
w2( new TestWidget(*w1) ),
w3( new TestWidget(*w1) );
w1->setContentsMargin(5);
w2->setContentsMargin(6);
w3->setContentsMargin(7);
BOOST_CHECK_EQUAL(w1->minimumSize(), SGVec2i(26, 26));
BOOST_CHECK_EQUAL(w1->sizeHint(), SGVec2i(42, 42));
BOOST_CHECK_EQUAL(w1->maximumSize(), sc::LayoutItem::MAX_SIZE);
BOOST_CHECK_EQUAL(w2->minimumSize(), SGVec2i(28, 28));
BOOST_CHECK_EQUAL(w2->sizeHint(), SGVec2i(44, 44));
BOOST_CHECK_EQUAL(w2->maximumSize(), sc::LayoutItem::MAX_SIZE);
BOOST_CHECK_EQUAL(w3->minimumSize(), SGVec2i(30, 30));
BOOST_CHECK_EQUAL(w3->sizeHint(), SGVec2i(46, 46));
BOOST_CHECK_EQUAL(w3->maximumSize(), sc::LayoutItem::MAX_SIZE);
hbox->addItem(w1);
hbox->addItem(w2);
hbox->addItem(w3);
BOOST_CHECK_EQUAL(hbox->minimumSize(), SGVec2i(114, 60));
BOOST_CHECK_EQUAL(hbox->sizeHint(), SGVec2i(162, 76));
BOOST_CHECK_EQUAL(hbox->maximumSize(), sc::LayoutItem::MAX_SIZE);
hbox->setGeometry(SGRecti(0, 0, hbox->sizeHint().x(), hbox->sizeHint().y()));
BOOST_CHECK_EQUAL(hbox->contentsRect(), SGRecti(5, 10, 142, 46));
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(5, 10, 42, 46));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(52, 10, 44, 46));
BOOST_CHECK_EQUAL(w3->geometry(), SGRecti(101, 10, 46, 46));
BOOST_CHECK_EQUAL(w1->contentsRect(), SGRecti(10, 15, 32, 36));
BOOST_CHECK_EQUAL(w2->contentsRect(), SGRecti(58, 16, 32, 34));
BOOST_CHECK_EQUAL(w3->contentsRect(), SGRecti(108, 17, 32, 32));
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( boxlayout_hfw )
{
TestWidgetRef w1( new TestWidgetHFW( SGVec2i(16, 16),
SGVec2i(32, 32) ) ),
w2( new TestWidgetHFW( SGVec2i(24, 24),
SGVec2i(48, 48) ) );
BOOST_CHECK_EQUAL(w1->heightForWidth(16), 64);
BOOST_CHECK_EQUAL(w1->minimumHeightForWidth(16), 16);
BOOST_CHECK_EQUAL(w2->heightForWidth(24), 96);
BOOST_CHECK_EQUAL(w2->minimumHeightForWidth(24), 24);
TestWidgetRef w_no_hfw( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32) ) );
BOOST_CHECK(!w_no_hfw->hasHeightForWidth());
BOOST_CHECK_EQUAL(w_no_hfw->heightForWidth(16), -1);
BOOST_CHECK_EQUAL(w_no_hfw->minimumHeightForWidth(16), -1);
// horizontal
sc::HBoxLayout hbox;
hbox.setSpacing(5);
hbox.addItem(w1);
hbox.addItem(w2);
BOOST_CHECK_EQUAL(hbox.heightForWidth(45), w2->heightForWidth(24));
BOOST_CHECK_EQUAL(hbox.heightForWidth(85), w2->heightForWidth(48));
hbox.addItem(w_no_hfw);
BOOST_CHECK_EQUAL(hbox.heightForWidth(66), 96);
BOOST_CHECK_EQUAL(hbox.heightForWidth(122), 48);
BOOST_CHECK_EQUAL(hbox.minimumHeightForWidth(66), 24);
BOOST_CHECK_EQUAL(hbox.minimumHeightForWidth(122), 16);
hbox.setGeometry(SGRecti(0, 0, 66, 24));
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 16, 24));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(21, 0, 24, 24));
BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(50, 0, 16, 24));
// vertical
sc::VBoxLayout vbox;
vbox.setSpacing(5);
vbox.addItem(w1);
vbox.addItem(w2);
BOOST_CHECK_EQUAL(vbox.heightForWidth(24), 143);
BOOST_CHECK_EQUAL(vbox.heightForWidth(48), 74);
BOOST_CHECK_EQUAL(vbox.minimumHeightForWidth(24), 39);
BOOST_CHECK_EQUAL(vbox.minimumHeightForWidth(48), 22);
vbox.addItem(w_no_hfw);
BOOST_CHECK_EQUAL(vbox.heightForWidth(24), 180);
BOOST_CHECK_EQUAL(vbox.heightForWidth(48), 111);
BOOST_CHECK_EQUAL(vbox.minimumHeightForWidth(24), 60);
BOOST_CHECK_EQUAL(vbox.minimumHeightForWidth(48), 43);
SGVec2i min_size = vbox.minimumSize(),
size_hint = vbox.sizeHint();
BOOST_CHECK_EQUAL(min_size, SGVec2i(24, 66));
BOOST_CHECK_EQUAL(size_hint, SGVec2i(48, 122));
vbox.setGeometry(SGRecti(0, 0, 24, 122));
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 24, 33));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(0, 38, 24, 47));
BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(0, 90, 24, 32));
// Vertical layouting modifies size hints, so check if they are correctly
// restored
BOOST_CHECK_EQUAL(min_size, vbox.minimumSize());
BOOST_CHECK_EQUAL(size_hint, vbox.sizeHint());
vbox.setGeometry(SGRecti(0, 0, 50, 122));
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 50, 25));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(0, 30, 50, 51));
BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(0, 86, 50, 36));
// Same geometry as before -> should get same widget geometry
// (check internal size hint cache updates correctly)
vbox.setGeometry(SGRecti(0, 0, 24, 122));
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 24, 33));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(0, 38, 24, 47));
BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(0, 90, 24, 32));
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( item_alignment_rect )
{
TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32) ) );
const SGRecti r(10, 10, 64, 64);
// Default: AlignFill -> fill up to maximum size
BOOST_CHECK_EQUAL(w1->alignmentRect(r), r);
// Horizontal
// AlignLeft -> width from size hint, positioned on the left
w1->setAlignment(sc::AlignLeft);
BOOST_CHECK_EQUAL(w1->alignmentRect(r), SGRecti(10, 10, 32, 64));
// AlignRight -> width from size hint, positioned on the left
w1->setAlignment(sc::AlignRight);
BOOST_CHECK_EQUAL(w1->alignmentRect(r), SGRecti(42, 10, 32, 64));
// AlignHCenter -> width from size hint, positioned in the center
w1->setAlignment(sc::AlignHCenter);
BOOST_CHECK_EQUAL(w1->alignmentRect(r), SGRecti(26, 10, 32, 64));
// Vertical
// AlignTop -> height from size hint, positioned on the top
w1->setAlignment(sc::AlignTop);
BOOST_CHECK_EQUAL(w1->alignmentRect(r), SGRecti(10, 10, 64, 32));
// AlignBottom -> height from size hint, positioned on the bottom
w1->setAlignment(sc::AlignBottom);
BOOST_CHECK_EQUAL(w1->alignmentRect(r), SGRecti(10, 42, 64, 32));
// AlignVCenter -> height from size hint, positioned in the center
w1->setAlignment(sc::AlignVCenter);
BOOST_CHECK_EQUAL(w1->alignmentRect(r), SGRecti(10, 26, 64, 32));
// Vertical + Horizontal
w1->setAlignment(sc::AlignCenter);
BOOST_CHECK_EQUAL(w1->alignmentRect(r), SGRecti(26, 26, 32, 32));
}
//------------------------------------------------------------------------------
// TODO extend to_nasal_helper for automatic argument conversion
static naRef f_Widget_visibilityChanged(nasal::CallContext ctx)
{
sc::NasalWidget* w = ctx.from_nasal<sc::NasalWidget*>(ctx.me);
if( !ctx.requireArg<bool>(0) )
w->setGeometry(SGRecti(0, 0, -1, -1));
return naNil();
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( nasal_widget )
{
nasal::Context c;
nasal::Hash globals = c.newHash();
nasal::Object::setupGhost();
nasal::Ghost<sc::LayoutItemRef>::init("LayoutItem");
sc::NasalWidget::setupGhost(globals);
nasal::Hash me = c.newHash();
me.set("visibilityChanged", &f_Widget_visibilityChanged);
sc::NasalWidgetRef w( new sc::NasalWidget(me.get_naRef()) );
// Default layout sizes (no user set values)
BOOST_CHECK_EQUAL(w->minimumSize(), SGVec2i(16, 16));
BOOST_CHECK_EQUAL(w->sizeHint(), SGVec2i(32, 32));
BOOST_CHECK_EQUAL(w->maximumSize(), sc::LayoutItem::MAX_SIZE);
// Changed layout sizes
w->setLayoutMinimumSize( SGVec2i(2, 12) );
w->setLayoutSizeHint( SGVec2i(3, 13) );
w->setLayoutMaximumSize( SGVec2i(4, 14) );
BOOST_CHECK_EQUAL(w->minimumSize(), SGVec2i(2, 12));
BOOST_CHECK_EQUAL(w->sizeHint(), SGVec2i(3, 13));
BOOST_CHECK_EQUAL(w->maximumSize(), SGVec2i(4, 14));
// User set values (overwrite layout sizes)
w->setMinimumSize( SGVec2i(15, 16) );
w->setSizeHint( SGVec2i(17, 18) );
w->setMaximumSize( SGVec2i(19, 20) );
BOOST_CHECK_EQUAL(w->minimumSize(), SGVec2i(15, 16));
BOOST_CHECK_EQUAL(w->sizeHint(), SGVec2i(17, 18));
BOOST_CHECK_EQUAL(w->maximumSize(), SGVec2i(19, 20));
// Only vertical user set values (layout/default for horizontal hints)
w->setMinimumSize( SGVec2i(0, 21) );
w->setSizeHint( SGVec2i(0, 22) );
w->setMaximumSize( SGVec2i(SGLimits<int>::max(), 23) );
BOOST_CHECK_EQUAL(w->minimumSize(), SGVec2i(2, 21));
BOOST_CHECK_EQUAL(w->sizeHint(), SGVec2i(3, 22));
BOOST_CHECK_EQUAL(w->maximumSize(), SGVec2i(4, 23));
w->setVisible(false);
BOOST_CHECK_EQUAL(w->geometry(), SGRecti(0, 0, -1, -1));
}

View File

@@ -190,12 +190,6 @@ inline int (isnan)(double r) { return !(r <= 0 || r >= 0); }
# define DEPRECATED
#endif
#if defined(__clang__)
# define SG_NO_RETURN [[noreturn]]
#else
# define SG_NO_RETURN
#endif
//
// No user modifiable definitions beyond here.
//

View File

@@ -26,8 +26,6 @@
#include <simgear/sg_inlines.h>
#include <simgear/threads/SGThread.hxx>
#include <simgear/threads/SGGuard.hxx>
#include <cstdlib> // for malloc
namespace simgear
{

View File

@@ -31,9 +31,7 @@ typedef enum {
SG_ENVIRONMENT = 0x00100000,
SG_SOUND = 0x00200000,
SG_NAVAID = 0x00400000,
SG_GUI = 0x00800000,
SG_TERRASYNC = 0x01000000,
SG_UNDEFD = 0x02000000, // For range checking
SG_UNDEFD = 0x00800000, // For range checking
SG_ALL = 0xFFFFFFFF
} sgDebugClass;

View File

@@ -70,8 +70,6 @@ const char* debugClassToString(sgDebugClass c)
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";
default: return "unknown";
}
}
@@ -440,4 +438,4 @@ void requestConsole()
global_privateLogstream->requestConsole();
}
} // of namespace simgear
} // of namespace simgear

View File

@@ -37,7 +37,7 @@
* Build a new OSG object from osgParticle.
*/
SGPrecipitation::SGPrecipitation() :
_freeze(false), _enabled(true), _droplet_external(false), _snow_intensity(0.0), _rain_intensity(0.0), _clip_distance(5.0), _rain_droplet_size(0.0), _snow_flake_size(0.0), _illumination(1.0)
_freeze(false), _enabled(true), _snow_intensity(0.0), _rain_intensity(0.0), _clip_distance(5.0)
{
_precipitationEffect = new osgParticle::PrecipitationEffect;
}
@@ -47,11 +47,6 @@ void SGPrecipitation::setEnabled( bool value )
_enabled = value;
}
void SGPrecipitation::setDropletExternal( bool value )
{
_droplet_external = value;
}
bool SGPrecipitation::getEnabled() const
{
return _enabled;
@@ -71,7 +66,7 @@ osg::Group* SGPrecipitation::build(void)
_precipitationEffect->rain(0);
if (_clip_distance!=0.0)
{
{
osg::ref_ptr<osg::ClipNode> clipNode = new osg::ClipNode;
clipNode->addClipPlane( new osg::ClipPlane( 0 ) );
clipNode->getClipPlane(0)->setClipPlane( 0.0, 0.0, -1.0, -_clip_distance );
@@ -124,72 +119,17 @@ void SGPrecipitation::setRainIntensity(float intensity)
this->_rain_intensity = intensity;
}
/**
* @brief Define the rain droplet size
*
* This function permits you to define and change the rain droplet size
* which is used if external droplet size control is enabled
*/
void SGPrecipitation::setRainDropletSize(float size)
{
_rain_droplet_size = size;
}
/**
* @brief Define the illumination multiplier
*
* This function permits you to define and change the rain droplet size
* which is used if external droplet size control is enabled
*/
void SGPrecipitation::setIllumination(float illumination)
{
_illumination = illumination;
}
/**
* @brief Define the snow flake size
*
* This function permits you to define and change the snow flake size
* which is used if external droplet size control is enabled
*/
void SGPrecipitation::setSnowFlakeSize(float size)
{
_snow_flake_size = size;
}
/**
* @brief Define the rain droplet size
*
* This function permits you to define and change the rain droplet size
* which is used if external droplet size control is enabled
*/
void SGPrecipitation::setClipDistance(float distance)
{
_clip_distance = distance;
}
/**
/**
* @brief Freeze the rain to snow
*
* @param freeze Boolean
*
*
* @param freeze Boolean
*
* This function permits you to turn off the rain to snow.
*/
void SGPrecipitation::setFreezing(bool freeze)
{
if ((this->_freeze)&&(!freeze)) // rain freezes suddenly, so we need to unfreeze
{
this->_rain_intensity = this->_snow_intensity;
this->_snow_intensity = 0.0;
}
this->_freeze = freeze;
}
@@ -197,7 +137,7 @@ void SGPrecipitation::setFreezing(bool freeze)
* @brief Define the wind direction and speed
*
* This function permits you to define and change the wind direction
*
*
* After apply the MatrixTransform to the osg::Precipitation object,
* x points full south... From wind heading and speed, we can calculate
* the wind vector.
@@ -229,74 +169,34 @@ void SGPrecipitation::setWindProperty(double heading, double speed)
bool SGPrecipitation::update(void)
{
if (this->_freeze) {
if (this->_rain_intensity > 0) {
this->_snow_intensity = this->_rain_intensity;
}
if (this->_rain_intensity > 0)
this->_snow_intensity = this->_rain_intensity;
}
if (_enabled && this->_snow_intensity > 0) {
_precipitationEffect->setWind(_wind_vec);
_precipitationEffect->setParticleSpeed( -0.75f - 0.25f*_snow_intensity);
if(_droplet_external)
{
if ((_freeze) && (_rain_droplet_size > 0.03)) // this is hail or sleet
{
_precipitationEffect->setParticleSize(_rain_droplet_size*1.5f);
_precipitationEffect->setParticleSpeed( -1.0f - 22.36f*sqrtf(_rain_droplet_size));
_precipitationEffect->setMaximumParticleDensity(_snow_intensity * 4.8f);
}
else if (_freeze) // this is snow from frozen small rain droplets
{
_precipitationEffect->setParticleSize(_rain_droplet_size*1.3f);
_precipitationEffect->setParticleSpeed( -0.75f - 0.25f*_snow_intensity);
_precipitationEffect->setMaximumParticleDensity(_snow_intensity * 10.0f);
}
else // this was snow in the first place
{
_precipitationEffect->setParticleSize(_snow_flake_size);
_precipitationEffect->setParticleSpeed( -0.75f - 0.25f*_snow_intensity);
_precipitationEffect->setMaximumParticleDensity(_snow_intensity * 7.2f);
}
}
else
{
_precipitationEffect->setMaximumParticleDensity(_snow_intensity * 7.2f);
_precipitationEffect->setParticleSize(0.02f + 0.03f*_snow_intensity);
_precipitationEffect->setParticleSpeed( -0.75f - 0.25f*_snow_intensity);
}
_precipitationEffect->setParticleSize(0.02f + 0.03f*_snow_intensity);
_precipitationEffect->setMaximumParticleDensity(_snow_intensity * 7.2f);
_precipitationEffect->setCellSize(osg::Vec3(5.0f / (0.25f+_snow_intensity), 5.0f / (0.25f+_snow_intensity), 5.0f));
_precipitationEffect->setNearTransition(25.f);
_precipitationEffect->setFarTransition(100.0f - 60.0f*sqrtf(_snow_intensity));
_precipitationEffect->setParticleColor(osg::Vec4(0.85 * _illumination, 0.85 * _illumination, 0.85 * _illumination, 1.0) - osg::Vec4(0.1, 0.1, 0.1, 1.0) * _snow_intensity);
_precipitationEffect->setParticleColor(osg::Vec4(0.85, 0.85, 0.85, 1.0) - osg::Vec4(0.1, 0.1, 0.1, 1.0) * _snow_intensity);
} else if (_enabled && this->_rain_intensity > 0) {
_precipitationEffect->setWind(_wind_vec);
if(_droplet_external)
{
_precipitationEffect->setParticleSize(_rain_droplet_size);
_precipitationEffect->setParticleSpeed( -1.0f - 22.36f*sqrtf(_rain_droplet_size));
}
else
{
_precipitationEffect->setParticleSize(0.01 + 0.02*_rain_intensity);
_precipitationEffect->setParticleSpeed( -2.0f + -5.0f*_rain_intensity);
}
_precipitationEffect->setParticleSpeed( -2.0f + -5.0f*_rain_intensity);
_precipitationEffect->setParticleSize(0.01 + 0.02*_rain_intensity);
_precipitationEffect->setMaximumParticleDensity(_rain_intensity * 7.5f);
_precipitationEffect->setCellSize(osg::Vec3(5.0f / (0.25f+_rain_intensity), 5.0f / (0.25f+_rain_intensity), 5.0f));
_precipitationEffect->setNearTransition(25.f);
_precipitationEffect->setFarTransition(100.0f - 60.0f*sqrtf(_rain_intensity));
_precipitationEffect->setParticleColor( osg::Vec4(0.64 * _illumination, 0.64 * _illumination, 0.64 * _illumination, 0.5));
_precipitationEffect->setParticleColor( osg::Vec4(0x7A, 0xCE, 0xFF, 0x80));
} else {
_precipitationEffect->snow(0);
_precipitationEffect->rain(0);

View File

@@ -37,14 +37,10 @@ class SGPrecipitation : public osg::Referenced
private:
bool _freeze;
bool _enabled;
bool _droplet_external;
float _snow_intensity;
float _rain_intensity;
float _clip_distance;
float _rain_droplet_size;
float _snow_flake_size;
float _illumination;
osg::Vec3 _wind_vec;
@@ -58,13 +54,8 @@ public:
void setWindProperty(double, double);
void setFreezing(bool);
void setDropletExternal(bool);
void setRainIntensity(float);
void setSnowIntensity(float);
void setRainDropletSize(float);
void setSnowFlakeSize(float);
void setIllumination(float);
void setClipDistance(float);
void setEnabled( bool );
bool getEnabled() const;

View File

@@ -22,7 +22,7 @@
* $Id$
**************************************************************************/
#include <cmath>
#include <math.h>
#include "jupiter.hxx"

View File

@@ -22,7 +22,7 @@
* $Id$
**************************************************************************/
#include <cmath>
#include <math.h>
#include "mars.hxx"

View File

@@ -22,7 +22,7 @@
* $Id$
**************************************************************************/
#include <cmath>
#include <math.h>
#include "mercury.hxx"

View File

@@ -25,7 +25,7 @@
#ifdef __BORLANDC__
# define exception c_exception
#endif
#include <cmath>
#include <math.h>
#include "neptune.hxx"

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