Compare commits

..

67 Commits

Author SHA1 Message Date
FlightGear Flight Simulator
2d00653f6e Bump version number to 3.4.0 2015-01-17 19:26:46 +01:00
Stuart Buchanan
4647ce2da5 Protect against divide-by-zero error.
Patch from Emilian Huminiuc.
2015-01-15 21:47:22 +00:00
Stuart Buchanan
51d43f809e Fix directional lighting 2015-01-05 20:50:34 +00:00
Peter Sadrozinski
370a115ab9 - remove the test of the test.. 2014-12-27 13:19:29 -05:00
Peter Sadrozinski
32b60f9b80 - fix binobj unit test. Simgear now rejects zero area triangles in .btg files, and the unit test could generate them. 2014-12-27 13:08:53 -05:00
Peter Sadrozinski
beeaef3868 - fix for index overrun when building TexturedTriangleArray
- fix for ws2.0 zero area triangles - drop them when loading
2014-12-26 13:07:33 -05:00
James Turner
ddd9691439 Explicitly track not-found responses from SVN.
When SVN reports a path is not found (ocean tile), track
this data explicitly and cache the result. Reduces backend
hits for missing tiles.
2014-12-01 17:49:03 +00:00
Stuart Buchanan
9da0031039 Fix PagedLOD for random objects.
- Only generate the object set and flights from the data once.
- Only expire the PagedLOD node after a suitable period of time.
2014-11-28 21:57:37 +00:00
Thomas Geymayer
9537876bba Nasal: add an naRef to ghosts to allow for proper gc of dependent objects/ghosts.
This allows for binding the lifetime of any nasal object to
the lifetime of a ghost. Otherwise circular references from
objects saved within the ghost would prevent the ghost from
being garbage collected.
2014-11-23 23:45:22 +01:00
Stuart Buchanan
958ae9bdf0 Fix two bugs in random object placement 2014-11-21 22:14:03 +00:00
Torsten Dreyer
023b5a09f7 Make expiry time for paged models settable from props
Our paged models used the default minimumExpiryTime of zero seconds
which caused frequent stutter with AI traffic loaded.
This patch sets the minimumExpiryTime to 180 seconds, more than enough
to hold the models in memory during a full standard rate turn.

The property to set the expiry time is
/sim/rendering/plod-minimum-expiry-time-secs
2014-11-19 17:23:29 +01:00
Clément de l'Hamaide
46fe57fb8d UniformFactory: fix Rembrandt light 2014-11-16 20:22:29 +01:00
Stuart Buchanan
07e09bb88c Support alpha for cloud definitions Thorsten RENK 2014-11-13 20:38:28 +00:00
James Turner
1b7f310ea6 Merge FreeBSD patches.
Taken from:
http://svnweb.freebsd.org/ports/head/devel/simgear/files/
2014-11-11 22:29:24 +00:00
Stuart Buchanan
365ddb84a7 Limit number of random objects per triangle 2014-11-07 22:22:55 +00:00
Stuart Buchanan
5fef44b11c Change parameter name to avoid clash with cull-face 2014-10-27 22:40:27 +00:00
Stuart Buchanan
1480aa9bb8 Pass whether light is directional to shader.
Patch from Thorsten RENK.
2014-10-21 20:27:46 +01:00
Torsten Dreyer
75271c44a8 Precipitation updates from ThorstenR
Since the consensus seems to be that the precipitation clipping issue is with the panel code, attached is  my proposed update for the precipitation system in SG and FG

* without corresponding control structures in FGData it falls back to default, except I have fixed an inconsistency in freezing behavior - previously rain changed suddenly to snow when the temperature dropped below zero, but the reverse transition was dragged out and gave odd visible motion with the wind as snow gradually changed back to rain with the particle speed not well defined. Now both transitions are sudden. And I see no more particles flow against the wind

* with

 <!-- definitions for the detailed precipitation manager -->
 <precipitation-control>
     <rain-droplet-size type="float" userarchive="n">0.015</rain-droplet-size>
     <snow-flake-size type="float" userarchive="n">0.03</snow-flake-size>
     <detailed-precipitation type="bool" userarchive="n">false</detailed-precipitation>
     <illumination type="float" userarchive="n">1.0</illumination>
     <clip-distance type="float" userarchive="n">5.0</clip-distance>
  </precipitation-control>

added to Environment/environment.xml, the new system allows to switch more detailed management on. This provides

* explicit setting of rain droplet size and snow flake size by the weather system

* automatic sqrt(r) scaling of the vertical speed of raindrops

* automatic transition to snow when freezing for small droplets but hail for large droplet sizes (looks like snow, but has different particle dynamics)

* an illumination scaling factor to dim the precipitating based on the light we have in the scene (I still need to devise a property rule to set this automatically)

The clip distance is also exposed now and considered at startup of the system - might be useful for e.g. airships when the gas bag provides rain cover (?)   or to be simply off for open airplanes
2014-10-21 10:46:48 +02:00
Torsten Dreyer
543f1b7902 Set sun color below horizon
Thorsten Renk:
The following patch sets the sun color to alpha=0 when
the sun is below the local horizon, removing the oddity
that the sun is seen 'through' the terrain when the terrain
at large distance is rendered by the skydome.
2014-10-08 12:45:01 +02:00
Stuart Buchanan
93a63a0678 Further work to clean up UniformCache for Effects 2014-09-27 22:05:30 +01:00
Torsten Dreyer
2bf79a2fa1 Merge branch 'UniformFactory' into next
Merging in Stuart's fix for the Effect system
2014-09-27 21:50:44 +02:00
Torsten Dreyer
ab956f15c3 A better fix for crash in the Effect System
Stuart has improved the UniformCache approach, here are his
changes:
- We have a UniformCache so that each unique Uniform is only created once
- As part of the UniformFactory we also have a queue of listeners that are still to be added
- When the main thread sends an Update node visitor across the Effects,
  all queued listeners are de-queued and added.
2014-09-27 21:48:36 +02:00
Thomas Geymayer
deceee8997 Fix compiler warnings. 2014-09-22 18:24:13 +02:00
Thomas Geymayer
b3c7d63809 Let ENABLE_TESTS enable/disable also the new Boost.Tests. 2014-09-22 18:21:52 +02:00
Clément de l'Hamaide
2026c665b2 Improve the <usage> tag feature
Do not load the <model> if <usage> tag is found as child.
Instead the load is triggered later by FlightGear
2014-09-21 22:05:45 +02:00
Thomas Geymayer
36fd005bb9 cppbind: check if ghost is of wrong type.
- Throw an exception if converting an object
   from Nasal fails due to a wrong type (nil
   does not throw).
 - Update cppbind test cases accordingly (and
   refactor another test suite to use Boost.
   Test).
2014-09-15 23:42:12 +02:00
Torsten Dreyer
46bed67cce first stab at UniformFactory 2014-09-05 11:28:58 +02:00
Torsten Dreyer
5b9af0c0aa Revert "Partial fix for crash in SGPropertyNode::fireValueChanged"
This reverts commit f33ad357e9.
2014-09-05 11:28:28 +02:00
Thomas Geymayer
85090180d0 canvas::Text: fix global StateSet and line start cursor pos with empty line. 2014-08-31 19:24:58 +02:00
Torsten Dreyer
f33ad357e9 Partial fix for crash in SGPropertyNode::fireValueChanged
The effect system used Listeners on property nodes to get the values
for shader uniforms. These listeners get deleted by an osg thread
causing access to freed memory when this happens while the main thread
calls fireValueChanged.

This patch changes the update method to polling for scalar properties.
This isn't 100% threadsafe, too. But at least it does not crash anymore.
2014-08-29 15:30:25 +02:00
Clément de l'Hamaide
c30ce67e16 Remove Textures.high logic 2014-08-27 22:08:20 +02:00
Torsten Dreyer
3ca7369fb2 Canvas: add stroke-linejoin handling for path elements 2014-08-26 00:08:35 +02:00
Thomas Geymayer
088ce31f7c canvas::KeyboardEvent: C0/C1 control characters are not printable. 2014-08-25 21:49:44 +02:00
Thomas Geymayer
4f94c22241 Canvas: add method clearFocusElement. 2014-08-25 21:49:21 +02:00
Thomas Geymayer
e1791b3006 canvas::Text: clean up and expose character/cursor positions. 2014-08-23 15:54:07 +02:00
Stuart Buchanan
e608ed5a01 Use surface-lights effect for random tile lights. 2014-08-10 20:20:42 +01:00
Thomas Geymayer
35ebb16215 Canvas: prepare for keypress events. 2014-08-10 19:39:18 +02:00
Thomas Geymayer
68d0891665 Canvas: fix element mouse hit detection with OSG 3.3.2. 2014-08-10 15:37:43 +02:00
Stuart Buchanan
b11ff19a0f Fix VS2010 lack of fminf 2014-08-10 12:54:58 +01:00
Stuart Buchanan
e7e616e07c New materials.xml format 2014-08-09 20:34:08 +01:00
bcoconni
14f2f9e917 Added some OSG headers for the correct evaluation of the OSG_VERSION_LESS_THAN macro. 2014-08-09 19:53:19 +02:00
Thomas Geymayer
791273c61d Update for OpenSceneGraph 3.3.2 API changes. 2014-08-09 18:13:45 +02:00
Thomas Geymayer
519326f751 Add simple keyboard event demo application. 2014-08-08 00:59:06 +02:00
Thomas Geymayer
d1f5d92a7b Move canvas::AlignmentFlag to separate file.
Mapping in a separate file allows easier exposing of
values and string representation to Nasal.
2014-08-03 16:39:26 +02:00
Christian Schmitt
f448898531 Fix lights appearing three times.
The 3 lights showing up are fixed by my patch. It's the old way of
calculating a normal (PLIB-style) that makes them show up.
2014-08-03 13:13:59 +02:00
Thomas Geymayer
d9b66fc0ef canvas::Layout: support for alignment.
Set alignment inside layouts, taking care of where
excess space is distributed.
2014-08-03 12:02:39 +02:00
Thomas Geymayer
3bcd0bafd5 Lots of (mostly) doxygen fixes/cleanup. 2014-08-01 00:13:25 +02:00
Thomas Geymayer
942181c8ae Canvas: Support for preventDefault() on Events. 2014-07-30 17:16:26 +02:00
Thomas Geymayer
7df39b9fc8 Fallback for old Boost (also with UTF8-CPP library). 2014-07-30 12:44:21 +02:00
Thomas Geymayer
f41b18f064 Improve (mostly Canvas event related) documentation. 2014-07-29 23:57:59 +02:00
Thomas Geymayer
c5d649aa0b Canvas: basic Keyboard event support (with input focus). 2014-07-29 22:22:20 +02:00
Thomas Geymayer
64ac22f50c Canvas: warn for missing tff and png plugins. 2014-07-27 12:11:09 +02:00
Thomas Geymayer
719ee36f5c Canvas: backdrop/stroke option for text (based on Gijs patch). 2014-07-27 11:05:49 +02:00
Thomas Geymayer
92851135d4 Doxygen: disable (not working) latex output and update version. 2014-07-22 12:51:01 +02:00
Thomas Geymayer
76fc47b24b canvas::Layout: support for contents margins. 2014-07-22 12:00:24 +02:00
Thomas Geymayer
f25abad9d7 Move overflow protected add helpers to math. 2014-07-22 11:45:28 +02:00
Thomas Geymayer
23413b4781 canvas::Layout: clear parent/canvas after calling onRemove. 2014-07-21 23:56:41 +02:00
Thomas Geymayer
e036dfc5bf Hopefully final fix for old gcc... 2014-07-21 13:26:09 +02:00
Thomas Geymayer
9260bea850 One more fix for old gcc. 2014-07-21 12:22:33 +02:00
Thomas Geymayer
c1f09034e0 Trying to make old gcc on Jenkins happy. 2014-07-21 10:54:31 +02:00
Thomas Geymayer
557e4f75b5 canvas::Layout: support for hiding items. 2014-07-21 00:25:59 +02:00
Thomas Geymayer
e71d6b24d7 cppbind: helper to call Nasal methods on NasalWidget. 2014-07-21 00:22:15 +02:00
Thomas Geymayer
726a4c6d10 canvas::NasalWidget: check for empty setGeometry callback. 2014-07-20 23:31:32 +02:00
Thomas Geymayer
6af8d32078 cppbind: fix Ghost casting/storage in polymorphic class hierarchies. 2014-07-20 23:17:33 +02:00
Thomas Geymayer
f6b16e2ba8 canvas::Element: floating point scissor coordinates.
GL_SCISSOR itself only supports integer coordinates, but
with reference frames different from GLOBAL transforms
influence the position of the clipping frame, possibly
resulting in wrong positions due to too low precision.
2014-07-19 20:52:17 +02:00
Thomas Geymayer
e415b08da4 canvas::BoxLayout: fix parent ref on add/remove. 2014-07-17 15:10:35 +02:00
Torsten Dreyer
1d93e8d61e New Version: 3.3.0 2014-07-17 14:54:35 +02:00
157 changed files with 7465 additions and 1671 deletions

View File

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

17
3rdparty/utf8/CMakeLists.txt vendored Normal file
View File

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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

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

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

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

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

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

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

View File

@@ -181,7 +181,7 @@ else()
message(STATUS "Sound support: ENABLED")
endif(ENABLE_SOUND)
find_package(OpenSceneGraph 3.2.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgUtil)
find_package(OpenSceneGraph 3.2.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgViewer osgUtil)
endif(SIMGEAR_HEADLESS)
find_package(ZLIB REQUIRED)
@@ -276,12 +276,12 @@ 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>
void f() { isnan(0.0);} "
int main() { return isnan(0.0);} "
HAVE_ISNAN)
check_cxx_source_compiles(
"#include <cmath>
void f() { std::isnan(0.0);} "
int main() { return std::isnan(0.0);} "
HAVE_STD_ISNAN)
if(CMAKE_COMPILER_IS_GNUCXX)
@@ -385,6 +385,8 @@ endif()
install (FILES ${PROJECT_BINARY_DIR}/simgear/simgear_config.h DESTINATION include/simgear/)
include_directories(3rdparty/utf8/source)
add_subdirectory(3rdparty)
add_subdirectory(simgear)

View File

@@ -51,15 +51,15 @@ if("${Boost_VERSION}0" LESS "1034000")
"NOTE: boost::test-based targets and tests cannot "
"be added: boost >= 1.34.0 required but not found. "
"(found: '${Boost_VERSION}'; want >=103400) ")
if(BUILD_TESTING)
if(ENABLE_TESTS)
message(FATAL_ERROR
${_shared_msg}
"You may disable BUILD_TESTING to continue without the "
"You may disable ENABLE_TESTS to continue without the "
"tests.")
else()
message(STATUS
${_shared_msg}
"BUILD_TESTING disabled, so continuing anyway.")
"ENABLE_TESTS disabled, so continuing anyway.")
endif()
endif()
@@ -88,7 +88,7 @@ if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000")
endif()
function(add_boost_test _name)
if(NOT BUILD_TESTING)
if(NOT ENABLE_TESTS)
return()
endif()

View File

@@ -38,7 +38,7 @@ PROJECT_NAME = SimGear
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 2.11.0
PROJECT_NUMBER = 3.3.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
@@ -744,30 +744,7 @@ WARN_LOGFILE =
# Note: If this tag is empty the current directory is searched.
INPUT = DoxygenMain.cxx \
simgear/bucket \
simgear/canvas \
simgear/compiler.h \
simgear/constants.h \
simgear/debug \
simgear/environment \
simgear/ephemeris \
simgear/io \
simgear/magvar \
simgear/math \
simgear/misc \
simgear/nasal \
simgear/props \
simgear/route \
simgear/scene \
simgear/screen \
simgear/serial \
simgear/structure \
simgear/sg_inlines.h \
simgear/sg_traits.hxx \
simgear/sound \
simgear/threads \
simgear/timing \
simgear/xml
simgear
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -1568,7 +1545,7 @@ EXTRA_SEARCH_MAPPINGS =
# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
# The default value is: YES.
GENERATE_LATEX = YES
GENERATE_LATEX = NO
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
@@ -1923,7 +1900,7 @@ ENABLE_PREPROCESSING = YES
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
MACRO_EXPANSION = NO
MACRO_EXPANSION = YES
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
# the macro expansion is limited to the macros specified with the PREDEFINED and

View File

@@ -127,21 +127,22 @@ public:
/**
* Construct a bucket given a specific location.
* @param dlon longitude specified in degrees
* @param dlat latitude specified in degrees
*
* @param geod Geodetic location
*/
SGBucket(const SGGeod& geod);
/** 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 lat and lon
* @param dlon longitude specified in degrees
* @param dlat latitude specified in degrees
* Reset a bucket to represent a new location.
*
* @param geod New geodetic location
*/
void set_bucket(const SGGeod& geod);

View File

@@ -20,6 +20,7 @@
#include "CanvasEventManager.hxx"
#include "CanvasEventVisitor.hxx"
#include "CanvasPlacement.hxx"
#include <simgear/canvas/events/KeyboardEvent.hxx>
#include <simgear/canvas/events/MouseEvent.hxx>
#include <simgear/scene/util/parse_color.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
@@ -196,7 +197,25 @@ namespace canvas
{
_layout = layout;
_layout->setCanvas(this);
_status |= LAYOUT_DIRTY;
}
//----------------------------------------------------------------------------
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();
}
//----------------------------------------------------------------------------
@@ -256,15 +275,7 @@ namespace canvas
}
if( _layout )
{
if( (_status & LAYOUT_DIRTY) )
{
_layout->setGeometry(SGRecti(0, 0, _view_width, _view_height));
_status &= ~LAYOUT_DIRTY;
}
else
_layout->update();
}
_layout->setGeometry(SGRecti(0, 0, _view_width, _view_height));
if( _visible || _render_always )
{
@@ -410,7 +421,6 @@ namespace canvas
if( _view_width == w )
return;
_view_width = w;
_status |= LAYOUT_DIRTY;
_texture.setViewSize(_view_width, _view_height);
}
@@ -421,7 +431,6 @@ namespace canvas
if( _view_height == h )
return;
_view_height = h;
_status |= LAYOUT_DIRTY;
_texture.setViewSize(_view_width, _view_height);
}
@@ -459,6 +468,18 @@ 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 )

View File

@@ -1,4 +1,5 @@
///@file The canvas for rendering with the 2d API
///@file
/// The canvas for rendering with the 2d API
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -43,6 +44,9 @@ namespace canvas
class CanvasMgr;
class MouseEvent;
/**
* Canvas to draw onto (to an off-screen render target).
*/
class Canvas:
public PropertyBasedElement,
public nasal::Object
@@ -53,8 +57,7 @@ namespace canvas
{
STATUS_OK,
STATUS_DIRTY = 1,
LAYOUT_DIRTY = STATUS_DIRTY << 1,
MISSING_SIZE_X = LAYOUT_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
@@ -133,6 +136,22 @@ namespace 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
*
@@ -160,6 +179,8 @@ namespace canvas
SGRect<int> getViewport() const;
bool handleMouseEvent(const MouseEventPtr& event);
bool handleKeyboardEvent(const KeyboardEventPtr& event);
bool propagateEvent( EventPtr const& event,
EventPropagationPath const& path );
@@ -211,13 +232,15 @@ namespace canvas
GroupPtr _root_group;
LayoutRef _layout;
ElementWeakPtr _focus_element;
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;

View File

@@ -27,7 +27,8 @@ namespace canvas
Event::Event():
type(UNKNOWN),
time(-1),
propagation_stopped(false)
propagation_stopped(false),
default_prevented(false)
{
}
@@ -80,6 +81,18 @@ namespace canvas
propagation_stopped = true;
}
//----------------------------------------------------------------------------
void Event::preventDefault()
{
default_prevented = true;
}
//----------------------------------------------------------------------------
bool Event::defaultPrevented() const
{
return default_prevented;
}
//----------------------------------------------------------------------------
int Event::getOrRegisterType(const std::string& type_str)
{
@@ -125,7 +138,7 @@ namespace canvas
if( type_map.empty() )
{
# define ENUM_MAPPING(type, str)\
# define ENUM_MAPPING(type, str, class_name)\
type_map.insert(TypeMap::value_type(str, type));
# include "CanvasEventTypes.hxx"
# undef ENUM_MAPPING

View File

@@ -1,4 +1,5 @@
// Canvas Event for event model similar to DOM Level 3 Event Model
/// @file
/// Canvas Event for event model similar to DOM Level 3 Event Model
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -27,26 +28,36 @@ 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
{
public:
/// Event type identifier
enum Type
{
UNKNOWN,
# define ENUM_MAPPING(name, str) name,
# define ENUM_MAPPING(name, str, class_name)\
name, /*!< class_name (type=str) */
# include "CanvasEventTypes.hxx"
# undef ENUM_MAPPING
CUSTOM_EVENT ///< all user defined event types share the same id. They
/// are just differentiated by using the type string.
CUSTOM_EVENT ///< First event type id available for user defined event
/// type.
/// @see CustomEvent
};
int type;
ElementWeakPtr target,
current_target;
double time;
bool propagation_stopped;
bool propagation_stopped,
default_prevented;
Event();
@@ -72,10 +83,33 @@ namespace canvas
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);

View File

@@ -20,14 +20,17 @@
# error "Don't include this file directly!"
#endif
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")
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)

View File

@@ -1,7 +1,8 @@
///@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.
///@file
/// Placement for putting a canvas texture onto OpenSceneGraph objects.
///
/// It also provides a SGPickCallback for passing mouse events to the canvas and
/// manages emissive lighting of the placed canvas.
//
// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
//

View File

@@ -172,6 +172,19 @@ namespace canvas
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()
{
@@ -267,6 +280,9 @@ namespace canvas
->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);
}

View File

@@ -84,6 +84,9 @@ namespace canvas
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.
*

View File

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

View File

@@ -156,7 +156,7 @@ SHfloat getMaxFloat();
/* OpenGL headers */
#if defined(VG_API_LINUX)
#if defined(VG_API_LINUX) || defined(VG_API_FREEBSD)
#include <GL/gl.h>
#include <GL/glx.h>
#elif defined(VG_API_MACOSX)

View File

@@ -53,6 +53,8 @@ namespace canvas
SG_FWD_DECL(Event)
SG_FWD_DECL(CustomEvent)
SG_FWD_DECL(DeviceEvent)
SG_FWD_DECL(KeyboardEvent)
SG_FWD_DECL(MouseEvent)
#undef SG_FWD_DECL

View File

@@ -26,7 +26,8 @@
#include <osg/Drawable>
#include <osg/Geode>
#include <osg/Scissor>
#include <osg/StateAttribute>
#include <osg/Version>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/foreach.hpp>
@@ -46,27 +47,34 @@ namespace canvas
* glScissor with coordinates relative to different reference frames.
*/
class Element::RelativeScissor:
public osg::Scissor
public osg::StateAttribute
{
public:
ReferenceFrame _coord_reference;
osg::observer_ptr<osg::Node> _node;
RelativeScissor(osg::Node* node = NULL):
explicit RelativeScissor(osg::Node* node = NULL):
_coord_reference(GLOBAL),
_node(node)
_node(node),
_x(0),
_y(0),
_width(0),
_height(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 ):
Scissor(vp, copyop),
StateAttribute(vp, copyop),
_coord_reference(vp._coord_reference),
_node(vp._node)
_node(vp._node),
_x(vp._x),
_y(vp._y),
_width(vp._width),
_height(vp._height)
{}
META_StateAttribute(simgear, RelativeScissor, SCISSOR);
@@ -89,6 +97,24 @@ namespace canvas
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 )
@@ -163,6 +189,12 @@ namespace canvas
return false;
}
protected:
float _x,
_y,
_width,
_height;
};
//----------------------------------------------------------------------------
@@ -272,6 +304,14 @@ namespace canvas
_listener.clear();
}
//----------------------------------------------------------------------------
void Element::setFocus()
{
CanvasPtr canvas = _canvas.lock();
if( canvas )
canvas->setFocusElement(this);
}
//----------------------------------------------------------------------------
bool Element::accept(EventVisitor& visitor)
{
@@ -295,6 +335,15 @@ namespace canvas
return true;
}
//----------------------------------------------------------------------------
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)
{
@@ -349,7 +398,13 @@ namespace canvas
// Drawables have a bounding box...
if( _drawable )
return _drawable->getBound().contains(osg::Vec3f(local_pos, 0));
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));
@@ -576,10 +631,10 @@ namespace canvas
_scissor = new RelativeScissor(_transform.get());
// <top>, <right>, <bottom>, <left>
_scissor->x() = SGMiscf::roundToInt(values[3]);
_scissor->y() = SGMiscf::roundToInt(values[0]);
_scissor->width() = SGMiscf::roundToInt(width);
_scissor->height() = SGMiscf::roundToInt(height);
_scissor->x() = values[3];
_scissor->y() = values[0];
_scissor->width() = width;
_scissor->height() = height;
SGPropertyNode* clip_frame = _node->getChild("clip-frame", 0);
if( clip_frame )
@@ -601,7 +656,11 @@ namespace canvas
osg::BoundingBox Element::getBoundingBox() const
{
if( _drawable )
#if OSG_VERSION_LESS_THAN(3,3,2)
return _drawable->getBound();
#else
return _drawable->getBoundingBox();
#endif
osg::BoundingBox bb;
@@ -624,7 +683,13 @@ namespace canvas
return osg::BoundingBox();
osg::BoundingBox transformed;
const osg::BoundingBox& bb = _drawable->getBound();
const osg::BoundingBox& bb =
#if OSG_VERSION_LESS_THAN(3,3,2)
_drawable->getBound();
#else
_drawable->getBoundingBox();
#endif
for(int i = 0; i < 4; ++i)
transformed.expandBy( bb.corner(i) * m );

View File

@@ -1,4 +1,5 @@
///@file Interface for 2D Canvas elements
///@file
/// Interface for 2D Canvas elements
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -42,7 +43,7 @@ namespace canvas
{
/**
* Baseclass for Elements displayed inside a Canvas.
* Base class for Elements displayed inside a Canvas.
*/
class Element:
public PropertyBasedElement
@@ -108,10 +109,16 @@ 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(const EventPtr& event);
bool dispatchEvent(const EventPtr& event);
@@ -128,12 +135,12 @@ namespace canvas
/**
* Set visibility of the element.
*/
void setVisible(bool visible);
virtual void setVisible(bool visible);
/**
* Get whether the element is visible or hidden.
*/
bool isVisible() const;
virtual bool isVisible() const;
osg::MatrixTransform* getMatrixTransform();
osg::MatrixTransform const* getMatrixTransform() const;
@@ -272,9 +279,10 @@ 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 name Property name
* @param type Interpolation type
* @param setter Setter function
* @param inheritable If this style propagates to child elements
*
* @tparam T1 Type of value used to retrieve value from property
* node
@@ -556,7 +564,7 @@ namespace canvas
/**
* Get stateset of drawable if available or use transform otherwise
*/
osg::StateSet* getOrCreateStateSet();
virtual osg::StateSet* getOrCreateStateSet();
void setupStyle();

View File

@@ -21,6 +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/scene/util/OsgMath.hxx>
#include <simgear/scene/util/parse_color.hxx>
@@ -30,6 +31,7 @@
#include <osg/Geometry>
#include <osg/PrimitiveSet>
#include <osgDB/Registry>
#include <osg/Version>
namespace simgear
{
@@ -100,6 +102,10 @@ namespace canvas
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");
}
//----------------------------------------------------------------------------
@@ -408,7 +414,14 @@ namespace canvas
&& child->getNameString() == "visible"
&& child->getBoolValue() )
{
CullCallback* cb = static_cast<CullCallback*>(_geom->getCullCallback());
CullCallback* cb =
#if OSG_VERSION_LESS_THAN(3,3,2)
static_cast<CullCallback*>
#else
dynamic_cast<CullCallback*>
#endif
( _geom->getCullCallback() );
if( cb )
cb->cullNextFrame();
}
@@ -521,8 +534,7 @@ namespace canvas
if( !src_canvas )
return handled;
MouseEventPtr mouse_event = dynamic_cast<MouseEvent*>(event.get());
if( mouse_event )
if( MouseEventPtr mouse_event = dynamic_cast<MouseEvent*>(event.get()) )
{
mouse_event.reset( new MouseEvent(*mouse_event) );
@@ -547,6 +559,11 @@ namespace canvas
handled |= src_canvas->handleMouseEvent(mouse_event);
}
else if( KeyboardEventPtr keyboard_event =
dynamic_cast<KeyboardEvent*>(event.get()) )
{
handled |= src_canvas->handleKeyboardEvent(keyboard_event);
}
return handled;
}

View File

@@ -20,6 +20,7 @@
#include <simgear/scene/util/parse_color.hxx>
#include <osg/Drawable>
#include <osg/Version>
#include <vg/openvg.h>
#include <cassert>
@@ -69,7 +70,8 @@ namespace canvas
_mode(0),
_fill_rule(VG_EVEN_ODD),
_stroke_width(1),
_stroke_linecap(VG_CAP_BUTT)
_stroke_linecap(VG_CAP_BUTT),
_stroke_linejoin(VG_JOIN_MITER)
{
setSupportsDisplayList(false);
setDataVariance(Object::DYNAMIC);
@@ -202,6 +204,21 @@ 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
*/
@@ -251,6 +268,7 @@ 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] );
@@ -372,7 +390,13 @@ namespace canvas
/**
* Compute the bounding box
*/
virtual osg::BoundingBox computeBound() const
virtual osg::BoundingBox
#if OSG_VERSION_LESS_THAN(3,3,2)
computeBound()
#else
computeBoundingBox()
#endif
const
{
if( _path == VG_INVALID_HANDLE || (_attributes_dirty & PATH) )
return osg::BoundingBox();
@@ -419,6 +443,7 @@ 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
@@ -499,6 +524,7 @@ 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);
}
//----------------------------------------------------------------------------

View File

@@ -21,6 +21,7 @@
#include <simgear/canvas/CanvasSystemAdapter.hxx>
#include <simgear/scene/util/parse_color.hxx>
#include <osg/Version>
#include <osgDB/Registry>
#include <osgText/Text>
namespace simgear
@@ -31,27 +32,190 @@ 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);
SGVec2i sizeForWidth(int w) const;
osg::Vec2 handleHit(const osg::Vec2f& pos);
float lineHeight() const;
virtual osg::BoundingBox computeBound() const;
/// Get the number of lines
size_t lineCount() const;
/// Get line @a i
TextLine lineAt(size_t i) const;
/// Get nearest line to given y-coordinate
TextLine nearestLine(float pos_y) const;
SGVec2i sizeForWidth(int w) const;
virtual osg::BoundingBox
#if OSG_VERSION_LESS_THAN(3,3,2)
computeBound()
#else
computeBoundingBox()
#endif
const;
protected:
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;
GlyphQuads::Coords2 const& coords = _quads->_coords;
size_t global_i = _begin + i;
if( global_i == _begin )
// before first character of line
pos.x() = coords[_begin * 4].x();
else if( global_i == _end )
// After Last character of line
pos.x() = coords[(_end - 1) * 4 + 2].x();
else
{
float prev_l = coords[(global_i - 1) * 4].x(),
prev_r = coords[(global_i - 1) * 4 + 2].x(),
cur_l = coords[global_i * 4].x();
if( prev_l == prev_r )
// If previous character width is zero set to begin of next character
// (Happens eg. with spaces)
pos.x() = cur_l;
else
// position at center between characters
pos.x() = 0.5 * (prev_r + cur_l);
}
return pos;
}
//----------------------------------------------------------------------------
osg::Vec2 TextLine::nearestCursor(float x) const
{
if( empty() )
return cursorPos(0);
GlyphQuads::Glyphs const& glyphs = _quads->_glyphs;
GlyphQuads::Coords2 const& coords = _quads->_coords;
float const HIT_FRACTION = 0.6;
float const character_width = _text->getCharacterHeight()
* _text->getCharacterAspectRatio();
size_t i = _begin;
for(; i < _end; ++i)
{
// Get threshold for mouse x position for setting cursor before or after
// current character
float threshold = coords[i * 4].x()
+ HIT_FRACTION * glyphs[i]->getHorizontalAdvance()
* character_width;
if( x <= threshold )
break;
}
return cursorPos(i - _begin);
}
//----------------------------------------------------------------------------
Text::TextOSG::TextOSG(canvas::Text* text):
_text_element(text)
@@ -88,6 +252,19 @@ 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)
{
@@ -96,6 +273,45 @@ namespace canvas
setBoundingBoxColor( color );
}
//----------------------------------------------------------------------------
float Text::TextOSG::lineHeight() const
{
return (1 + _lineSpacing) * _characterHeight;
}
//----------------------------------------------------------------------------
size_t Text::TextOSG::lineCount() const
{
return _lineCount;
}
//----------------------------------------------------------------------------
TextLine Text::TextOSG::lineAt(size_t i) const
{
return TextLine(i, this);
}
//----------------------------------------------------------------------------
TextLine Text::TextOSG::nearestLine(float pos_y) const
{
osgText::Font const* font = getActiveFont();
if( !font || lineCount() <= 0 )
return TextLine(0, this);
float asc = .9f, desc = -.2f;
font->getVerticalSize(asc, desc);
float first_line_y = _offset.y()
- (1 + _lineSpacing / 2 + desc) * _characterHeight;
size_t line_num = std::min<size_t>(
std::max<size_t>(0, (pos_y - first_line_y) / lineHeight()),
lineCount() - 1
);
return TextLine(line_num, this);
}
//----------------------------------------------------------------------------
// simplified version of osgText::Text::computeGlyphRepresentation() to
// just calculate the size for a given weight. Glpyh calculations/creating
@@ -372,89 +588,20 @@ namespace canvas
}
//----------------------------------------------------------------------------
osg::Vec2 Text::TextOSG::handleHit(const osg::Vec2f& pos)
osg::BoundingBox
#if OSG_VERSION_LESS_THAN(3,3,2)
Text::TextOSG::computeBound()
#else
Text::TextOSG::computeBoundingBox()
#endif
const
{
float line_height = _characterHeight + _lineSpacing;
// 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);
if( _textureGlyphQuadMap.empty() )
return osg::Vec2(-1, -1);
// TODO check when it can be larger
assert( _textureGlyphQuadMap.size() == 1 );
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;
const float HIT_FRACTION = 0.6;
const float character_width = getCharacterHeight()
* getCharacterAspectRatio();
float y = (line + 0.5) * line_height;
bool line_found = false;
for(size_t i = 0; i < line_numbers.size(); ++i)
{
if( line_numbers[i] != line )
{
if( !line_found )
{
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);
}
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());
return hit;
}
}
// Nothing found -> return position after last character
return osg::Vec2
(
coords.back().x(),
(_lineCount - 0.5) * line_height
);
}
//----------------------------------------------------------------------------
osg::BoundingBox Text::TextOSG::computeBound() const
{
osg::BoundingBox bb = osgText::Text::computeBound();
osg::BoundingBox bb =
#if OSG_VERSION_LESS_THAN(3,3,2)
osgText::Text::computeBound();
#else
osgText::Text::computeBoundingBox();
#endif
#if OSG_VERSION_LESS_THAN(3,1,0)
if( bb.valid() )
@@ -546,6 +693,7 @@ 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<
@@ -567,6 +715,10 @@ 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");
}
//----------------------------------------------------------------------------
@@ -657,10 +809,39 @@ namespace canvas
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->handleHit(pos);
return _text->nearestLine(pos.y()).nearestCursor(pos.x());
}
//----------------------------------------------------------------------------
osg::Vec2 Text::getCursorPos(size_t line, size_t character) const
{
return _text->lineAt(line).cursorPos(character);
}
//----------------------------------------------------------------------------
osg::StateSet* Text::getOrCreateStateSet()
{
if( !_transform.valid() )
return 0;
// Only check for StateSet on Transform, as the text stateset is shared
// between all text instances using the same font (texture).
return _transform->getOrCreateStateSet();
}
} // namespace canvas

View File

@@ -29,6 +29,7 @@ namespace simgear
namespace canvas
{
class TextLine;
class Text:
public Element
{
@@ -48,13 +49,24 @@ namespace canvas
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

@@ -2,11 +2,15 @@ include (SimGearComponent)
set(HEADERS
CustomEvent.hxx
DeviceEvent.hxx
KeyboardEvent.hxx
MouseEvent.hxx
)
set(SOURCES
CustomEvent.cxx
DeviceEvent.cxx
KeyboardEvent.cxx
MouseEvent.cxx
)
@@ -15,4 +19,10 @@ 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,4 +1,5 @@
///@file Canvas user defined event
///@file
/// Canvas user defined event
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
@@ -27,6 +28,10 @@ namespace simgear
namespace canvas
{
/**
* User defined event (optionally carrying additional context information or
* data).
*/
class CustomEvent:
public Event
{
@@ -36,6 +41,7 @@ namespace canvas
*
* @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,
@@ -45,6 +51,7 @@ namespace canvas
/**
*
* @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,
@@ -61,10 +68,16 @@ namespace canvas
*/
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;
StringMap detail; //!< User data map
bool bubbles; //!< Whether the event supports bubbling
};
} // namespace canvas

View File

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

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

View File

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

View File

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

View File

@@ -17,6 +17,7 @@
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "MouseEvent.hxx"
#include <osgGA/GUIEventAdapter>
namespace simgear
{
@@ -27,7 +28,6 @@ namespace canvas
MouseEvent::MouseEvent():
button(0),
buttons(0),
modifiers(0),
click_count(0)
{
@@ -35,13 +35,11 @@ namespace canvas
//----------------------------------------------------------------------------
MouseEvent::MouseEvent(const osgGA::GUIEventAdapter& ea):
DeviceEvent(ea),
button(0),
buttons(ea.getButtonMask()),
modifiers(ea.getModKeyMask()),
click_count(0)
{
time = ea.getTime();
// Convert button mask to index
int button_mask = ea.getButton();
while( (button_mask >>= 1) > 0 )

View File

@@ -1,4 +1,5 @@
///@file Mouse event
///@file
/// Mouse event
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -19,16 +20,18 @@
#ifndef CANVAS_MOUSE_EVENT_HXX_
#define CANVAS_MOUSE_EVENT_HXX_
#include <simgear/canvas/CanvasEvent.hxx>
#include <osgGA/GUIEventAdapter>
#include "DeviceEvent.hxx"
namespace simgear
{
namespace canvas
{
/**
* Mouse (button/move/wheel) event
*/
class MouseEvent:
public Event
public DeviceEvent
{
public:
MouseEvent();
@@ -55,18 +58,16 @@ namespace canvas
int getButton() const { return button; }
int getButtonMask() const { return buttons; }
int getModifiers() const { return modifiers; }
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
modifiers, //<! Keyboard modifier state
click_count; //<! Current click count
int button, //!< Button for this event
buttons, //!< Current button state
click_count; //!< Current click count
};
} // namespace canvas

View File

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

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

@@ -46,9 +46,11 @@ namespace canvas
}
//----------------------------------------------------------------------------
void BoxLayout::addItem(const LayoutItemRef& item, int stretch)
void BoxLayout::addItem( const LayoutItemRef& item,
int stretch,
uint8_t alignment )
{
insertItem(-1, item, stretch);
insertItem(-1, item, stretch, alignment);
}
//----------------------------------------------------------------------------
@@ -64,14 +66,24 @@ namespace canvas
}
//----------------------------------------------------------------------------
void BoxLayout::insertItem(int index, const LayoutItemRef& item, int stretch)
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);
item->setCanvas(_canvas);
item->setParent(this);
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);
@@ -122,6 +134,7 @@ namespace canvas
LayoutItems::iterator it = _layout_items.begin() + index;
LayoutItemRef item = it->layout_item;
item->onRemove();
item->setParent(LayoutItemWeakRef());
_layout_items.erase(it);
invalidate();
@@ -137,6 +150,7 @@ namespace canvas
++it )
{
it->layout_item->onRemove();
it->layout_item->setParent(LayoutItemWeakRef());
}
_layout_items.clear();
invalidate();
@@ -223,26 +237,6 @@ namespace canvas
return _layout_data.has_hfw;
}
//----------------------------------------------------------------------------
int BoxLayout::heightForWidth(int w) const
{
if( !hasHeightForWidth() )
return -1;
updateWFHCache(w);
return _hfw_height;
}
//----------------------------------------------------------------------------
int BoxLayout::minimumHeightForWidth(int w) const
{
if( !hasHeightForWidth() )
return -1;
updateWFHCache(w);
return _hfw_min_height;
}
//----------------------------------------------------------------------------
void BoxLayout::setCanvas(const CanvasWeakPtr& canvas)
{
@@ -272,16 +266,23 @@ namespace canvas
for(size_t i = 0; i < _layout_items.size(); ++i)
{
// TODO check visible
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 )
@@ -297,9 +298,9 @@ namespace canvas
}
// Add sizes of all children in layout direction
safeAdd(min_size.x(), item_data.min_size);
safeAdd(max_size.x(), item_data.max_size);
safeAdd(size_hint.x(), item_data.size_hint);
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(),
@@ -312,9 +313,9 @@ namespace canvas
_layout_data.has_hfw = _layout_data.has_hfw || item.hasHeightForWidth();
}
safeAdd(min_size.x(), _layout_data.padding);
safeAdd(max_size.x(), _layout_data.padding);
safeAdd(size_hint.x(), _layout_data.padding);
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();
@@ -348,6 +349,9 @@ namespace canvas
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));
}
@@ -357,6 +361,9 @@ namespace canvas
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;
}
@@ -386,6 +393,27 @@ namespace canvas
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)
{
@@ -406,6 +434,9 @@ namespace canvas
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(),
@@ -434,7 +465,10 @@ namespace canvas
_layout_data.size_hint = size_hint_save;
// and finally set the layouted geometry for each item
int fixed_size = (geom.size().*_get_fixed_coord)();
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)() );
@@ -445,17 +479,11 @@ namespace canvas
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;
SGVec2i size(
data.size,
std::min( (data.layout_item->maximumSize().*_get_fixed_coord)(),
fixed_size )
);
// Center in fixed direction (TODO allow specifying alignment)
int offset_fixed = (fixed_size - size.y()) / 2;
cur_pos.y() += offset_fixed;
size.x() = data.size;
data.layout_item->setGeometry(SGRecti(
(cur_pos.*_get_layout_coord)(),
@@ -466,10 +494,16 @@ namespace canvas
if( !reverse )
cur_pos.x() += data.size;
cur_pos.y() -= offset_fixed;
}
}
//----------------------------------------------------------------------------
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)

View File

@@ -1,4 +1,4 @@
// Align items horizontally or vertically in a box
/// @file
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
@@ -26,6 +26,11 @@ 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
{
@@ -44,13 +49,18 @@ namespace canvas
virtual void addItem(const LayoutItemRef& item);
void addItem(const LayoutItemRef& item, int stretch);
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);
void insertItem( int index,
const LayoutItemRef& item,
int stretch = 0,
uint8_t alignment = 0 );
void insertStretch(int index, int stretch = 0);
@@ -86,8 +96,6 @@ namespace canvas
Direction direction() const;
virtual bool hasHeightForWidth() const;
virtual int heightForWidth(int w) const;
virtual int minimumHeightForWidth(int w) const;
virtual void setCanvas(const CanvasWeakPtr& canvas);
@@ -96,9 +104,9 @@ namespace canvas
protected:
typedef const int& (SGVec2i::*CoordGetter)() const;
CoordGetter _get_layout_coord, //<! getter for coordinate in layout
CoordGetter _get_layout_coord, //!< getter for coordinate in layout
// direction
_get_fixed_coord; //<! getter for coordinate in secondary
_get_fixed_coord; //!< getter for coordinate in secondary
// (fixed) direction
int _padding;
@@ -121,7 +129,12 @@ namespace canvas
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);
};
/**

View File

@@ -1,6 +1,7 @@
include (SimGearComponent)
set(HEADERS
AlignFlag_values.hxx
BoxLayout.hxx
Layout.hxx
LayoutItem.hxx

View File

@@ -24,36 +24,6 @@ namespace simgear
namespace canvas
{
//----------------------------------------------------------------------------
void Layout::update()
{
if( !(_flags & (LAYOUT_DIRTY | SIZE_INFO_DIRTY)) )
return;
doLayout(_geometry);
_flags &= ~LAYOUT_DIRTY;
}
//----------------------------------------------------------------------------
void Layout::invalidate()
{
LayoutItem::invalidate();
_flags |= LAYOUT_DIRTY;
}
//----------------------------------------------------------------------------
void Layout::setGeometry(const SGRecti& geom)
{
if( geom != _geometry )
{
_geometry = geom;
_flags |= LAYOUT_DIRTY;
}
update();
}
//----------------------------------------------------------------------------
void Layout::removeItem(const LayoutItemRef& item)
{
@@ -74,6 +44,19 @@ namespace canvas
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()
{
@@ -85,6 +68,8 @@ namespace canvas
padding = 0;
size = 0;
stretch = 0;
visible = false;
has_align = false;
has_hfw = false;
done = false;
}
@@ -108,19 +93,28 @@ namespace canvas
}
//----------------------------------------------------------------------------
void Layout::safeAdd(int& a, int b)
Layout::Layout():
_num_not_done(0),
_sum_stretch(0),
_space_stretch(0),
_space_left(0)
{
if( SGLimits<int>::max() - b < a )
a = SGLimits<int>::max();
else
a += b;
}
//----------------------------------------------------------------------------
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 = num_children;
_num_not_done = 0;
SG_LOG( SG_GUI,
SG_DEBUG,
@@ -148,6 +142,9 @@ namespace canvas
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);
@@ -162,10 +159,8 @@ namespace canvas
);
if( d.done )
{
_num_not_done -= 1;
continue;
}
_num_not_done += 1;
if( d.stretch > 0 )
{
@@ -191,6 +186,8 @@ namespace canvas
for(int i = 0; i < num_children; ++i)
{
ItemData& d = items[i];
if( !d.visible )
continue;
SG_LOG(
SG_GUI,
@@ -265,18 +262,59 @@ namespace canvas
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;
// Add superfluous space as padding
d.padding = d.padding_orig + _space_left
// Padding after last child...
/ (_num_not_done + 1);
int space_add = 0;
_space_left -= d.padding - d.padding_orig;
_num_not_done -= 1;
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;
}
}
@@ -284,10 +322,11 @@ namespace canvas
for(int i = 0; i < num_children; ++i)
{
ItemData const& d = items[i];
SG_LOG( SG_GUI,
SG_DEBUG,
i << ") pad=" << d.padding
<< ", size = " << d.size );
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]");
}
}

View File

@@ -1,4 +1,4 @@
// Basic class for canvas layouts
/// @file
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
@@ -27,15 +27,13 @@ namespace simgear
namespace canvas
{
/**
* Base class for all Canvas layouts.
*/
class Layout:
public LayoutItem
{
public:
void update();
virtual void invalidate();
virtual void setGeometry(const SGRecti& geom);
virtual void addItem(const LayoutItemRef& item) = 0;
virtual void setSpacing(int spacing) = 0;
virtual int spacing() const = 0;
@@ -71,11 +69,22 @@ namespace canvas
*/
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
{
LAYOUT_DIRTY = LayoutItem::LAST_FLAG << 1,
LAST_FLAG = LAYOUT_DIRTY
LAST_FLAG = LayoutItem::LAST_FLAG
};
struct ItemData
@@ -84,12 +93,14 @@ namespace canvas
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 has_hfw : 1, //<! height for width
done : 1; //<! layouting done
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();
@@ -98,16 +109,15 @@ namespace canvas
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;
/**
* Add two integers taking care of overflow (limit to INT_MAX)
*/
static void safeAdd(int& a, int b);
/**
* Distribute the available @a space to all @a items
*/
@@ -115,12 +125,12 @@ namespace canvas
private:
int _num_not_done, //<! number of children not layouted yet
_sum_stretch, //<! sum of stretch factors of all not yet layouted
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
_space_stretch,//!< space currently assigned to all not yet layouted
// stretchable children
_space_left; //<! remaining space not used by any child yet
_space_left; //!< remaining space not used by any child yet
};

View File

@@ -23,12 +23,61 @@ 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():
_flags(0),
_alignment(AlignFill),
_flags(VISIBLE),
_size_hint(0, 0),
_min_size(0, 0),
_max_size(MAX_SIZE)
@@ -42,6 +91,44 @@ namespace canvas
}
//----------------------------------------------------------------------------
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
{
@@ -51,7 +138,7 @@ namespace canvas
_flags &= ~SIZE_HINT_DIRTY;
}
return _size_hint;
return addClipOverflow(_size_hint, _margins.size());
}
//----------------------------------------------------------------------------
@@ -63,7 +150,7 @@ namespace canvas
_flags &= ~MINIMUM_SIZE_DIRTY;
}
return _min_size;
return addClipOverflow(_min_size, _margins.size());
}
//----------------------------------------------------------------------------
@@ -75,7 +162,7 @@ namespace canvas
_flags &= ~MAXIMUM_SIZE_DIRTY;
}
return _max_size;
return addClipOverflow(_max_size, _margins.size());
}
//----------------------------------------------------------------------------
@@ -87,19 +174,60 @@ namespace canvas
//----------------------------------------------------------------------------
int LayoutItem::heightForWidth(int w) const
{
return -1;
int h = heightForWidthImpl(w - _margins.horiz());
return h < 0 ? -1 : SGMisc<int>::addClipOverflow(h, _margins.vert());
}
//------------------------------------------------------------------------------
int LayoutItem::minimumHeightForWidth(int w) const
{
return heightForWidth(w);
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;
_flags |= SIZE_INFO_DIRTY | LAYOUT_DIRTY;
invalidateParent();
}
@@ -111,10 +239,24 @@ namespace canvas
parent->invalidate();
}
//----------------------------------------------------------------------------
void LayoutItem::update()
{
if( (_flags & LAYOUT_DIRTY) && isVisible() )
contentsRectChanged( contentsRect() );
}
//----------------------------------------------------------------------------
void LayoutItem::setGeometry(const SGRecti& geom)
{
_geometry = geom;
SGRecti ar = alignmentRect(geom);
if( ar != _geometry )
{
_geometry = ar;
_flags |= LAYOUT_DIRTY;
}
update();
}
//----------------------------------------------------------------------------
@@ -123,6 +265,41 @@ namespace canvas
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)
{
@@ -140,7 +317,14 @@ namespace canvas
{
_parent = parent;
LayoutItemRef parent_ref = parent.lock();
setCanvas(parent_ref ? parent_ref->_canvas : CanvasWeakPtr());
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());
}
//----------------------------------------------------------------------------
@@ -167,5 +351,43 @@ namespace canvas
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,4 +1,5 @@
///@file Basic element for layouting canvas elements
///@file
/// Basic element for layouting canvas elements.
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
@@ -34,6 +35,65 @@ namespace canvas
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.
@@ -49,6 +109,49 @@ namespace canvas
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.
*/
@@ -64,9 +167,60 @@ namespace canvas
*/
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;
virtual int heightForWidth(int w) const;
virtual int minimumHeightForWidth(int w) 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.
@@ -74,10 +228,15 @@ namespace canvas
virtual void invalidate();
/**
* Mark all cached data of parent item as invalid (if it is known)
* 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.
@@ -89,6 +248,20 @@ namespace canvas
*/
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.
*/
@@ -124,13 +297,18 @@ namespace canvas
SIZE_INFO_DIRTY = SIZE_HINT_DIRTY
| MINIMUM_SIZE_DIRTY
| MAXIMUM_SIZE_DIRTY,
LAST_FLAG = 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,
@@ -141,6 +319,50 @@ namespace canvas
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

View File

@@ -19,6 +19,7 @@
#include "NasalWidget.hxx"
#include <simgear/canvas/Canvas.hxx>
#include <simgear/nasal/cppbind/NasalContext.hxx>
#include <simgear/nasal/cppbind/Ghost.hxx>
namespace simgear
@@ -45,53 +46,12 @@ namespace canvas
onRemove();
}
//----------------------------------------------------------------------------
void NasalWidget::invalidate()
{
LayoutItem::invalidate();
_flags |= LAYOUT_DIRTY;
}
//----------------------------------------------------------------------------
void NasalWidget::setGeometry(const SGRect<int>& geom)
{
if( _geometry != geom )
_geometry = geom;
else if( !(_flags & LAYOUT_DIRTY) || !_set_geometry )
return;
naContext c = naNewContext();
try
{
_set_geometry(nasal::to_nasal(c, this), geom);
_flags &= ~LAYOUT_DIRTY;
}
catch( std::exception const& ex )
{
SG_LOG(
SG_GUI,
SG_WARN,
"NasalWidget::setGeometry: callback error: '" << ex.what() << "'"
);
}
naFreeContext(c);
}
//----------------------------------------------------------------------------
void NasalWidget::onRemove()
{
if( !_nasal_impl.valid() )
return;
typedef boost::function<void(nasal::Me)> Deleter;
naContext c = naNewContext();
try
{
Deleter del =
nasal::get_member<Deleter>(c, _nasal_impl.get_naRef(), "onRemove");
if( del )
del(nasal::to_nasal(c, this));
callMethod<void>("onRemove");
}
catch( std::exception const& ex )
{
@@ -101,7 +61,6 @@ namespace canvas
"NasalWidget::onRemove: callback error: '" << ex.what() << "'"
);
}
naFreeContext(c);
}
//----------------------------------------------------------------------------
@@ -192,22 +151,6 @@ namespace canvas
return !_height_for_width.empty() || !_min_height_for_width.empty();
}
//----------------------------------------------------------------------------
int NasalWidget::heightForWidth(int w) const
{
return callHeightForWidthFunc( _height_for_width.empty()
? _min_height_for_width
: _height_for_width, w );
}
//----------------------------------------------------------------------------
int NasalWidget::minimumHeightForWidth(int w) const
{
return callHeightForWidthFunc( _min_height_for_width.empty()
? _height_for_width
: _min_height_for_width, w );
}
//----------------------------------------------------------------------------
static naRef f_makeNasalWidget(const nasal::CallContext& ctx)
{
@@ -291,5 +234,62 @@ namespace canvas
);
}
//----------------------------------------------------------------------------
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,4 +1,5 @@
///@file Glue for GUI widgets implemented in Nasal space
///@file
/// Glue for GUI widgets implemented in Nasal space.
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
@@ -31,7 +32,7 @@ namespace canvas
{
/**
* Baseclass/ghost to create widgets with Nasal.
* Base class/ghost to implement gui widgets in Nasal space.
*/
class NasalWidget:
public LayoutItem,
@@ -51,8 +52,6 @@ namespace canvas
~NasalWidget();
virtual void invalidate();
virtual void setGeometry(const SGRecti& geom);
virtual void onRemove();
void setSetGeometryFunc(const SetGeometryFunc& func);
@@ -85,8 +84,6 @@ namespace canvas
void setLayoutMaximumSize(const SGVec2i& s);
virtual bool hasHeightForWidth() const;
virtual int heightForWidth(int w) const;
virtual int minimumHeightForWidth(int w) const;
/**
* @param ns Namespace to register the class interface
@@ -118,6 +115,13 @@ namespace canvas
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;

View File

@@ -1,4 +1,4 @@
///@file Element providing blank space in a layout.
///@file
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//

View File

@@ -23,6 +23,8 @@
#include "NasalWidget.hxx"
#include <simgear/debug/logstream.hxx>
#include <simgear/nasal/cppbind/NasalContext.hxx>
#include <cstdlib>
//------------------------------------------------------------------------------
@@ -62,16 +64,17 @@ class TestWidget:
void setMaxSize(const SGVec2i& size) { _max_size = size; }
void setSizeHint(const SGVec2i& size) { _size_hint = size; }
virtual void setGeometry(const SGRecti& geom) { _geom = geom; }
virtual SGRecti geometry() const { return _geom; }
protected:
SGRecti _geom;
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:
@@ -91,12 +94,12 @@ class TestWidgetHFW:
return true;
}
virtual int heightForWidth(int w) const
virtual int heightForWidthImpl(int w) const
{
return _size_hint.x() * _size_hint.y() / w;
}
virtual int minimumHeightForWidth(int w) const
virtual int minimumHeightForWidthImpl(int w) const
{
return _min_size.x() * _min_size.y() / w;
}
@@ -107,45 +110,45 @@ typedef SGSharedPtr<TestWidget> TestWidgetRef;
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( horizontal_layout )
{
sc::BoxLayout box_layout(sc::BoxLayout::BottomToTop);
box_layout.setSpacing(5);
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);
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);
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);
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);
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));
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);
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));
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);
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));
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));
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));
@@ -154,12 +157,12 @@ BOOST_AUTO_TEST_CASE( horizontal_layout )
TestWidgetRef stretch_widget( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32),
SGVec2i(128, 32) ) );
box_layout.addItem(stretch_widget, 1);
box_layout.update();
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));
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.
@@ -169,63 +172,101 @@ BOOST_AUTO_TEST_CASE( horizontal_layout )
// Test stretch factor
TestWidgetRef fast_stretch( new TestWidget(*stretch_widget) );
sc::BoxLayout box_layout_stretch(sc::BoxLayout::LeftToRight);
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->addItem(stretch_widget, 1);
box_layout_stretch->addItem(fast_stretch, 2);
box_layout_stretch.setGeometry(SGRecti(0,0,128,32));
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));
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));
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));
// Test more space then preferred, but less than maximum
{
sc::HBoxLayout hbox;
TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32),
SGVec2i(9999, 32) ) ),
w2( new TestWidget(*w1) );
// ...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.
hbox.addItem(w1);
hbox.addItem(w2);
// 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));
hbox.setGeometry( SGRecti(0, 0, 256, 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));
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 126, 32));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(131, 0, 125, 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));
hbox.setStretch(0, 1);
hbox.setStretch(1, 1);
// 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));
}
BOOST_CHECK_EQUAL(hbox.stretch(0), 1);
BOOST_CHECK_EQUAL(hbox.stretch(1), 1);
//------------------------------------------------------------------------------
// 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.update();
hbox->addItem(w1);
hbox->addItem(w2);
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 125, 32));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(130, 0, 126, 32));
hbox->setGeometry( SGRecti(0, 0, 256, 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);
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 126, 32));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(131, 0, 125, 32));
hbox.removeItem(w1);
hbox->setStretch(0, 1);
hbox->setStretch(1, 1);
BOOST_CHECK( !hbox.setStretchFactor(w1, 0) );
}
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) );
}
//------------------------------------------------------------------------------
@@ -295,35 +336,203 @@ BOOST_AUTO_TEST_CASE( vertical_layout)
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( boxlayout_insert_remove )
{
sc::HBoxLayout hbox;
sc::BoxLayoutRef hbox( new sc::HBoxLayout );
BOOST_CHECK_EQUAL(hbox.count(), 0);
BOOST_CHECK(!hbox.itemAt(0));
BOOST_CHECK(!hbox.takeAt(0));
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);
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);
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);
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);
hbox->addItem(w2);
BOOST_CHECK_EQUAL(hbox->count(), 2);
BOOST_CHECK_EQUAL(w2->getParent(), hbox);
hbox.clear();
BOOST_CHECK_EQUAL(hbox.count(), 0);
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));
}
//------------------------------------------------------------------------------
@@ -417,13 +626,77 @@ BOOST_AUTO_TEST_CASE( boxlayout_hfw )
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 )
{
naContext c = naNewContext();
naRef me = naNewHash(c);
nasal::Context c;
nasal::Hash globals = c.newHash();
sc::NasalWidgetRef w( new sc::NasalWidget(me) );
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));
@@ -457,5 +730,6 @@ BOOST_AUTO_TEST_CASE( nasal_widget )
BOOST_CHECK_EQUAL(w->sizeHint(), SGVec2i(3, 22));
BOOST_CHECK_EQUAL(w->maximumSize(), SGVec2i(4, 23));
naFreeContext(c);
w->setVisible(false);
BOOST_CHECK_EQUAL(w->geometry(), SGRecti(0, 0, -1, -1));
}

View File

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

View File

@@ -37,7 +37,7 @@
* Build a new OSG object from osgParticle.
*/
SGPrecipitation::SGPrecipitation() :
_freeze(false), _enabled(true), _snow_intensity(0.0), _rain_intensity(0.0), _clip_distance(5.0)
_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)
{
_precipitationEffect = new osgParticle::PrecipitationEffect;
}
@@ -47,6 +47,11 @@ void SGPrecipitation::setEnabled( bool value )
_enabled = value;
}
void SGPrecipitation::setDropletExternal( bool value )
{
_droplet_external = value;
}
bool SGPrecipitation::getEnabled() const
{
return _enabled;
@@ -66,7 +71,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 );
@@ -119,17 +124,72 @@ 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;
}
@@ -137,7 +197,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.
@@ -169,34 +229,74 @@ 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);
_precipitationEffect->setParticleSize(0.02f + 0.03f*_snow_intensity);
_precipitationEffect->setMaximumParticleDensity(_snow_intensity * 7.2f);
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->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, 0.85, 0.85, 1.0) - osg::Vec4(0.1, 0.1, 0.1, 1.0) * _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);
} else if (_enabled && this->_rain_intensity > 0) {
_precipitationEffect->setWind(_wind_vec);
_precipitationEffect->setParticleSpeed( -2.0f + -5.0f*_rain_intensity);
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->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(0x7A, 0xCE, 0xFF, 0x80));
_precipitationEffect->setParticleColor( osg::Vec4(0.64 * _illumination, 0.64 * _illumination, 0.64 * _illumination, 0.5));
} else {
_precipitationEffect->snow(0);
_precipitationEffect->rain(0);

View File

@@ -37,10 +37,14 @@ 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;
@@ -54,8 +58,13 @@ 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

@@ -1,4 +1,4 @@
///@file HTTP request writing response to a file.
///@file
//
// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
//
@@ -27,6 +27,9 @@ namespace simgear
namespace HTTP
{
/**
* HTTP request writing response to a file.
*/
class FileRequest:
public Request
{

View File

@@ -1,4 +1,4 @@
///@file HTTP request keeping response in memory.
///@file
//
// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
//
@@ -27,6 +27,9 @@ namespace simgear
namespace HTTP
{
/**
* HTTP request keeping response in memory.
*/
class MemoryRequest:
public Request
{

View File

@@ -1,3 +1,20 @@
// Copyright (C) 2011 James Turner <zakalawe@mac.com>
// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "HTTPRequest.hxx"
#include <simgear/compiler.h>

View File

@@ -1,3 +1,22 @@
///@file
//
// Copyright (C) 2011 James Turner <zakalawe@mac.com>
// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_HTTP_REQUEST_HXX
#define SG_HTTP_REQUEST_HXX
@@ -18,6 +37,9 @@ namespace simgear
namespace HTTP
{
/**
* Base class for HTTP request (and answer).
*/
class Request:
public SGReferenced
{

View File

@@ -274,6 +274,15 @@ static void read_indices(char* buffer,
if (vaMask & SG_VA_FLOAT_3) vas[7].push_back(*src++);
}
} // of elements in the index
// WS2.0 fix : toss zero area triangles
if ( ( count == 3 ) && (indexMask & SG_IDX_VERTICES) ) {
if ( (vertices[0] == vertices[1]) ||
(vertices[1] == vertices[2]) ||
(vertices[2] == vertices[0]) ) {
vertices.clear();
}
}
}
template <class T>
@@ -468,12 +477,15 @@ void SGBinObject::read_object( gzFile fp,
read_indices<uint16_t>(ptr, nbytes, idx_mask, vertex_attrib_mask, vs, ns, cs, tcs, vas );
}
vertices.push_back( vs );
normals.push_back( ns );
colors.push_back( cs );
texCoords.push_back( tcs );
vertexAttribs.push_back( vas );
materials.push_back( material );
// Fix for WS2.0 - ignore zero area triangles
if ( !vs.empty() ) {
vertices.push_back( vs );
normals.push_back( ns );
colors.push_back( cs );
texCoords.push_back( tcs );
vertexAttribs.push_back( vas );
materials.push_back( material );
}
} // of element iteration
}

View File

@@ -44,9 +44,8 @@
#include "sg_file.hxx"
using std::string;
SGFile::SGFile(const string &file, int repeat_)
SGFile::SGFile(const std::string &file, int repeat_)
: file_name(file), fp(-1), eof_flag(true), repeat(repeat_), iteration(0)
{
set_type( sgFileType );

View File

@@ -1,37 +1,31 @@
/** \file sg_file.hxx
* File I/O routines.
*/
///@file
/// File I/O routines.
//
// Written by Curtis Olson, started November 1999.
//
// Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
// 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 program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// 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
// General Public License for more details.
// 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$
// 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_FILE_HXX
#define _SG_FILE_HXX
#include <simgear/compiler.h>
#include <string>
#include "iochannel.hxx"
#include <string>
/**
* A file I/O class based on SGIOChannel.
@@ -91,7 +85,4 @@ public:
virtual bool eof() const { return eof_flag; };
};
#endif // _SG_FILE_HXX

View File

@@ -93,13 +93,32 @@ void compareTexCoords(const SGBinObject& rd, const std::vector<SGVec2f>& b)
VERIFY(equivalent(pos, b[i], 0.001f));
}
}
int_list make_tri(int maxIndex)
{
int_list r;
r.push_back(random() % maxIndex);
r.push_back(random() % maxIndex);
r.push_back(random() % maxIndex);
int a, b, c;
bool valid = false;
int retry = 10;
while(!valid && retry--) {
a = (random() % maxIndex);
b = (random() % maxIndex);
c = (random() % maxIndex);
valid = ( (a!=b) && (b!=c) && (c!=a) );
}
if (!valid) {
cerr << "can't generate valid triangle" << endl;
exit(1);
}
r.push_back(a);
r.push_back(b);
r.push_back(c);
return r;
}

View File

@@ -41,6 +41,30 @@ public:
static T clip(const T& a, const T& _min, const T& _max)
{ return max(_min, min(_max, a)); }
/// Add two (integer) values taking care of overflows.
static T addClipOverflow(T a, T b)
{
if( b > 0 )
{
if( SGLimits<T>::max() - b < a )
return SGLimits<T>::max();
}
else
{
if( SGLimits<T>::min() - b > a )
return SGLimits<T>::min();
}
return a + b;
}
/// Add two (integer) values in place, taking care of overflows.
static T& addClipOverflowInplace(T& a, T b)
{
return a = addClipOverflow(a, b);
}
/**
* Seek a variable towards a target value with given rate and timestep
*

View File

@@ -196,6 +196,17 @@ SGVec2<T>
max(S s, const SGVec2<T>& v)
{ return SGVec2<T>(SGMisc<T>::max(s, v(0)), SGMisc<T>::max(s, v(1))); }
/// Add two vectors taking care of (integer) overflows. The values are limited
/// to the respective minimum and maximum values.
template<class T>
SGVec2<T> addClipOverflow(SGVec2<T> const& lhs, SGVec2<T> const& rhs)
{
return SGVec2<T>(
SGMisc<T>::addClipOverflow(lhs.x(), rhs.x()),
SGMisc<T>::addClipOverflow(lhs.y(), rhs.y())
);
}
/// Scalar dot product
template<typename T>
inline

View File

@@ -288,6 +288,18 @@ max(S s, const SGVec3<T>& v)
SGMisc<T>::max(s, v(2)));
}
/// Add two vectors taking care of (integer) overflows. The values are limited
/// to the respective minimum and maximum values.
template<class T>
SGVec3<T> addClipOverflow(SGVec3<T> const& lhs, SGVec3<T> const& rhs)
{
return SGVec3<T>(
SGMisc<T>::addClipOverflow(lhs.x(), rhs.x()),
SGMisc<T>::addClipOverflow(lhs.y(), rhs.y()),
SGMisc<T>::addClipOverflow(lhs.z(), rhs.z())
);
}
/// Scalar dot product
template<typename T>
inline

View File

@@ -244,6 +244,19 @@ max(S s, const SGVec4<T>& v)
SGMisc<T>::max(s, v(3)));
}
/// Add two vectors taking care of (integer) overflows. The values are limited
/// to the respective minimum and maximum values.
template<class T>
SGVec4<T> addClipOverflow(SGVec4<T> const& lhs, SGVec4<T> const& rhs)
{
return SGVec4<T>(
SGMisc<T>::addClipOverflow(lhs.x(), rhs.x()),
SGMisc<T>::addClipOverflow(lhs.y(), rhs.y()),
SGMisc<T>::addClipOverflow(lhs.z(), rhs.z()),
SGMisc<T>::addClipOverflow(lhs.w(), rhs.w())
);
}
/// Scalar dot product
template<typename T>
inline

View File

@@ -36,8 +36,6 @@
#include "interpolater.hxx"
using std::string;
// Constructor -- starts with an empty table.
SGInterpTable::SGInterpTable()
{
@@ -55,7 +53,7 @@ SGInterpTable::SGInterpTable(const SGPropertyNode* interpolation)
// Constructor -- loads the interpolation table from the specified
// file
SGInterpTable::SGInterpTable( const string& file )
SGInterpTable::SGInterpTable( const std::string& file )
{
SG_LOG( SG_MATH, SG_INFO, "Initializing Interpolator for " << file );

View File

@@ -1,4 +1,5 @@
///@file Parse and represent SVG preserveAspectRatio attribute
///@file
/// Parse and represent SVG preserveAspectRatio attribute.
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
@@ -53,6 +54,7 @@ namespace simgear
bool operator==(const SVGpreserveAspectRatio& rhs) const;
/// Parse preserveAspectRatio from string.
static SVGpreserveAspectRatio parse(const std::string& str);
private:

View File

@@ -1,4 +1,5 @@
///@file Really simple markdown parser
///@file
/// Really simple markdown parser.
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//

View File

@@ -27,7 +27,6 @@
#include "sgstream.hxx"
using std::string;
using std::istream;
using std::ostream;
@@ -40,7 +39,7 @@ sg_gzifstream::sg_gzifstream()
//
// Open a possibly gzipped file for reading.
//
sg_gzifstream::sg_gzifstream( const string& name, ios_openmode io_mode )
sg_gzifstream::sg_gzifstream( const std::string& name, ios_openmode io_mode )
: istream(&gzbuf)
{
this->open( name, io_mode );
@@ -65,12 +64,12 @@ sg_gzifstream::sg_gzifstream( int fd, ios_openmode io_mode )
// then append ".gz" and try again.
//
void
sg_gzifstream::open( const string& name, ios_openmode io_mode )
sg_gzifstream::open( const std::string& name, ios_openmode io_mode )
{
gzbuf.open( name.c_str(), io_mode );
if ( ! gzbuf.is_open() )
{
string s = name;
std::string s = name;
if ( s.substr( s.length() - 3, 3 ) == ".gz" )
{
// remove ".gz" suffix
@@ -160,7 +159,7 @@ sg_gzofstream::sg_gzofstream()
//
// Open a file for gzipped writing.
//
sg_gzofstream::sg_gzofstream( const string& name, ios_openmode io_mode )
sg_gzofstream::sg_gzofstream( const std::string& name, ios_openmode io_mode )
: ostream(&gzbuf)
{
this->open( name, io_mode );
@@ -181,7 +180,7 @@ sg_gzofstream::sg_gzofstream( int fd, ios_openmode io_mode )
// Open a file for gzipped writing.
//
void
sg_gzofstream::open( const string& name, ios_openmode io_mode )
sg_gzofstream::open( const std::string& name, ios_openmode io_mode )
{
gzbuf.open( name.c_str(), io_mode );
}

View File

@@ -3,6 +3,7 @@ include (SimGearComponent)
set(HEADERS
Ghost.hxx
NasalCallContext.hxx
NasalContext.hxx
NasalHash.hxx
NasalObject.hxx
NasalObjectHolder.hxx
@@ -16,11 +17,13 @@ set(DETAIL_HEADERS
detail/from_nasal_helper.hxx
detail/functor_templates.hxx
detail/nasal_traits.hxx
detail/NasalObject_callMethod_templates.hxx
detail/to_nasal_helper.hxx
)
set(SOURCES
Ghost.cxx
NasalContext.cxx
NasalHash.cxx
NasalString.cxx
NasalObject.cxx
@@ -31,18 +34,22 @@ set(SOURCES
simgear_component(nasal/cppbind nasal/cppbind "${SOURCES}" "${HEADERS}")
simgear_component(nasal/cppbind/detail nasal/cppbind/detail "" "${DETAIL_HEADERS}")
if(ENABLE_TESTS)
add_executable(test_cppbind cppbind_test.cxx)
add_test(cppbind ${EXECUTABLE_OUTPUT_PATH}/test_cppbind)
target_link_libraries(test_cppbind ${TEST_LIBS})
endif(ENABLE_TESTS)
add_boost_test(cppbind_ghost
SOURCES cppbind_test_ghost.cxx
SOURCES test/cppbind_test_ghost.cxx
LIBRARIES ${TEST_LIBS}
)
add_boost_test(cppbind_misc
SOURCES test/cppbind_test.cxx
LIBRARIES ${TEST_LIBS}
)
add_boost_test(nasal_gc_test
SOURCES test/nasal_gc_test.cxx
LIBRARIES ${TEST_LIBS}
)
add_boost_test(nasal_num
SOURCES nasal_num_test.cxx
SOURCES test/nasal_num_test.cxx
LIBRARIES ${TEST_LIBS}
)

View File

@@ -33,7 +33,7 @@ namespace nasal
}
//--------------------------------------------------------------------------
bool GhostMetadata::isBaseOf(naGhostType* ghost_type, bool& is_weak) const
bool GhostMetadata::isInstance(naGhostType* ghost_type, bool& is_weak) const
{
if( ghost_type == _ghost_type_strong_ptr )
{
@@ -47,14 +47,6 @@ namespace nasal
return true;
}
for( DerivedList::const_iterator derived = _derived_classes.begin();
derived != _derived_classes.end();
++derived )
{
if( (*derived)->isBaseOf(ghost_type, is_weak) )
return true;
}
return false;
}
@@ -74,7 +66,6 @@ namespace nasal
void GhostMetadata::addDerived(const GhostMetadata* derived)
{
assert(derived);
_derived_classes.push_back(derived);
SG_LOG
(

View File

@@ -78,6 +78,11 @@ get_pointer(T ptr)
// The old version of g++ used on Jenkins (16.11.2012) only compiles
// this version.
# define SG_GET_TEMPLATE_MEMBER(type, member) &member
// With old g++ on Jenkins (21.07.2014), ADL for static_pointer_cast does not
// work.
using boost::static_pointer_cast;
using osg::static_pointer_cast;
#else
// VS (2008, 2010, ... ?) only allow this version.
# define SG_GET_TEMPLATE_MEMBER(type, member) &type::template member
@@ -107,17 +112,14 @@ namespace nasal
*/
void addNasalBase(const naRef& parent);
bool isBaseOf(naGhostType* ghost_type, bool& is_weak) const;
bool isInstance(naGhostType* ghost_type, bool& is_weak) const;
protected:
typedef std::vector<const GhostMetadata*> DerivedList;
const std::string _name_strong,
_name_weak;
const naGhostType *_ghost_type_strong_ptr,
*_ghost_type_weak_ptr;
DerivedList _derived_classes;
std::vector<naRef> _parents;
GhostMetadata( const std::string& name,
@@ -307,7 +309,7 @@ namespace nasal
// Keep reference for duration of call to prevent expiring
// TODO not needed for strong referenced ghost
strong_ref ref = fromNasal<strong_ref>(c, me);
strong_ref ref = fromNasal(c, me);
if( !ref )
{
naGhostType* ghost_type = naGhost_type(me);
@@ -325,7 +327,7 @@ namespace nasal
return holder->_method
(
*get_pointer(ref),
CallContext(c, argc, args)
CallContext(c, me, argc, args)
);
}
catch(const std::exception& ex)
@@ -416,10 +418,13 @@ namespace nasal
boost::is_base_of<typename BaseGhost::raw_type, raw_type>::value
));
typedef typename BaseGhost::strong_ref base_ref;
BaseGhost* base = BaseGhost::getSingletonPtr();
base->addDerived(
this,
SG_GET_TEMPLATE_MEMBER(Ghost, getTypeFor<BaseGhost>)
SG_GET_TEMPLATE_MEMBER(Ghost, toNasal<BaseGhost>),
SG_GET_TEMPLATE_MEMBER(Ghost, fromNasalWithCast<base_ref>)
);
// Replace any getter that is not available in the current class.
@@ -809,7 +814,7 @@ namespace nasal
makeGhost(naContext c, RefType const& ref_ptr)
{
strong_ref ref(ref_ptr);
raw_type* ptr = get_pointer(ref_ptr);
raw_type* ptr = get_pointer(ref);
if( !ptr )
return naNil();
@@ -817,15 +822,11 @@ namespace nasal
// will then be hold be a new shared pointer. We therefore have to
// check for the dynamic type of the object as it might differ from
// the passed static type.
naGhostType* ghost_type =
getTypeFor<Ghost>(ptr, shared_ptr_traits<RefType>::is_strong::value);
if( !ghost_type )
return naNil();
return naNewGhost2( c,
ghost_type,
shared_ptr_storage<RefType>::ref(ref_ptr) );
return toNasal<Ghost>(
c,
ref,
shared_ptr_traits<RefType>::is_strong::value
);
}
/**
@@ -833,25 +834,36 @@ namespace nasal
* Nasal objects has to be derived class of the target class (Either
* derived in C++ or in Nasal using a 'parents' vector)
*/
template<class Type>
static Type fromNasal(naContext c, naRef me)
static strong_ref fromNasal(naContext c, naRef me)
{
bool is_weak = false;
// Check if it's a ghost and if it can be converted
if( isBaseOf(naGhost_type(me), is_weak) )
naGhostType* ghost_type = naGhost_type(me);
if( ghost_type )
{
void* ghost = naGhost_ptr(me);
return is_weak ? getPtr<Type, true>(ghost)
: getPtr<Type, false>(ghost);
}
// Check if we got an instance of this class
bool is_weak = false;
if( isInstance(ghost_type, is_weak) )
{
return is_weak ? getPtr<strong_ref, true>(naGhost_ptr(me))
: getPtr<strong_ref, false>(naGhost_ptr(me));
}
// Now if it is derived from a ghost (hash with ghost in parent vector)
// otherwise try the derived classes
for( typename DerivedList::reverse_iterator
derived = getSingletonPtr()->_derived_types.rbegin();
derived != getSingletonPtr()->_derived_types.rend();
++derived )
{
strong_ref ref = (derived->from_nasal)(c, me);
if( get_pointer(ref) )
return ref;
}
}
else if( naIsHash(me) )
{
naRef na_parents = naHash_cget(me, const_cast<char*>("parents"));
if( !naIsVector(na_parents) )
return Type();
return strong_ref();
typedef std::vector<naRef> naRefs;
naRefs parents = from_nasal<naRefs>(c, na_parents);
@@ -859,19 +871,48 @@ namespace nasal
parent != parents.end();
++parent )
{
Type ptr = fromNasal<Type>(c, *parent);
if( get_pointer(ptr) )
return ptr;
strong_ref ref = fromNasal(c, *parent);
if( get_pointer(ref) )
return ref;
}
}
return Type();
return strong_ref();
}
static bool isBaseOf(naRef obj)
/**
* Convert Nasal object to C++ object and check if it has the correct
* type.
*
* @see fromNasal
*/
static strong_ref fromNasalChecked(naContext c, naRef me)
{
bool is_weak;
return isBaseOf(naGhost_type(obj), is_weak);
strong_ref obj = fromNasal(c, me);
if( obj )
return obj;
if( naIsNil(me) )
return strong_ref();
std::string msg = "Can not convert to '"
+ getSingletonPtr()->_name_strong
+ "': ";
naGhostType* ghost_type = naGhost_type(me);
if( ghost_type )
msg += "not a derived class (or expired weak ref): "
"'" + std::string(ghost_type->name) + "'";
else if( naIsHash(me) )
{
if( !naIsVector(naHash_cget(me, const_cast<char*>("parents"))) )
msg += "missing parents vector";
else
msg += "not a derived hash";
}
else
msg += "not an object";
throw bad_nasal_cast(msg);
}
private:
@@ -882,21 +923,30 @@ namespace nasal
static naGhostType _ghost_type_strong, //!< Stored as shared pointer
_ghost_type_weak; //!< Stored as weak shared pointer
typedef naGhostType* (*type_checker_t)(const raw_type*, bool);
typedef std::vector<type_checker_t> DerivedList;
typedef naRef (*to_nasal_t)(naContext, const strong_ref&, bool);
typedef strong_ref (*from_nasal_t)(naContext, naRef);
struct DerivedInfo
{
to_nasal_t to_nasal;
from_nasal_t from_nasal;
DerivedInfo( to_nasal_t to_nasal_func,
from_nasal_t from_nasal_func ):
to_nasal(to_nasal_func),
from_nasal(from_nasal_func)
{}
};
typedef std::vector<DerivedInfo> DerivedList;
DerivedList _derived_types;
static bool isBaseOf(naGhostType* ghost_type, bool& is_weak)
static bool isInstance(naGhostType* ghost_type, bool& is_weak)
{
if( !ghost_type || !getSingletonPtr() )
return false;
return getSingletonPtr()->GhostMetadata::isBaseOf(ghost_type, is_weak);
}
static bool isBaseOf(naRef obj, bool& is_weak)
{
return isBaseOf(naGhost_type(obj), is_weak);
return getSingletonPtr()->GhostMetadata::isInstance( ghost_type,
is_weak );
}
template<class RefPtr, bool is_weak>
@@ -945,28 +995,33 @@ namespace nasal
}
void addDerived( const internal::GhostMetadata* derived_meta,
type_checker_t derived_type_checker )
to_nasal_t to_nasal_func,
from_nasal_t from_nasal_func )
{
GhostMetadata::addDerived(derived_meta);
_derived_types.push_back(derived_type_checker);
_derived_types.push_back(DerivedInfo(to_nasal_func, from_nasal_func));
}
template<class BaseGhost>
static
typename boost::enable_if
< boost::is_polymorphic<typename BaseGhost::raw_type>,
naGhostType*
naRef
>::type
getTypeFor(const typename BaseGhost::raw_type* base, bool strong)
toNasal( naContext c,
const typename BaseGhost::strong_ref& base_ref,
bool strong )
{
typename BaseGhost::raw_type* ptr = get_pointer(base_ref);
// Check first if passed pointer can by converted to instance of class
// this ghost wraps.
if( !boost::is_same
< typename BaseGhost::raw_type,
typename Ghost::raw_type
>::value
&& dynamic_cast<const typename Ghost::raw_type*>(base) != base )
return 0;
&& dynamic_cast<const typename Ghost::raw_type*>(ptr) != ptr )
return naNil();
if( !getSingletonPtr() )
{
@@ -976,45 +1031,52 @@ namespace nasal
SG_INFO,
"Ghost::getTypeFor: can not get type for unregistered ghost"
);
return 0;
return naNil();
}
strong_ref ref =
static_pointer_cast<typename Ghost::raw_type>(base_ref);
// Now check if we can further downcast to one of our derived classes.
for( typename DerivedList::reverse_iterator
derived = getSingletonPtr()->_derived_types.rbegin();
derived != getSingletonPtr()->_derived_types.rend();
++derived )
{
naGhostType* ghost_type =
(*derived)(
static_cast<const typename Ghost::raw_type*>(base),
strong
);
naRef ghost = (derived->to_nasal)(c, ref, strong);
if( ghost_type )
return ghost_type;
if( !naIsNil(ghost) )
return ghost;
}
// If base is not an instance of any derived class, this class has to
// be the dynamic type.
return strong
? &_ghost_type_strong
: &_ghost_type_weak;
? create<false>(c, ref)
: create<true>(c, ref);
}
template<class BaseGhost>
static
typename boost::disable_if
< boost::is_polymorphic<typename BaseGhost::raw_type>,
naGhostType*
naRef
>::type
getTypeFor(const typename BaseGhost::raw_type* base, bool strong)
toNasal( naContext c,
const typename BaseGhost::strong_ref& ref,
bool strong )
{
// For non polymorphic classes there is no possibility to get the actual
// dynamic type, therefore we can only use its static type.
return strong
? &BaseGhost::_ghost_type_strong
: &BaseGhost::_ghost_type_weak;
? create<false>(c, ref)
: create<true>(c, ref);
}
template<class Type>
static Type fromNasalWithCast(naContext c, naRef me)
{
return Type(fromNasal(c, me));
}
static Ghost* getSingletonPtr()
@@ -1176,6 +1238,45 @@ namespace nasal
return instance;
}
template<bool is_weak>
static
typename boost::enable_if_c<
!is_weak,
naRef
>::type
create(naContext c, const strong_ref& ref_ptr)
{
typedef shared_ptr_storage<strong_ref> storage_type;
return naNewGhost2( c,
&Ghost::_ghost_type_strong,
storage_type::ref(ref_ptr) );
}
template<bool is_weak>
static
typename boost::enable_if_c<
is_weak && supports_weak_ref<T>::value,
naRef
>::type
create(naContext c, const strong_ref& ref_ptr)
{
typedef shared_ptr_storage<weak_ref> storage_type;
return naNewGhost2( c,
&Ghost::_ghost_type_weak,
storage_type::ref(ref_ptr) );
}
template<bool is_weak>
static
typename boost::enable_if_c<
is_weak && !supports_weak_ref<T>::value,
naRef
>::type
create(naContext, const strong_ref&)
{
return naNil();
}
template<class Type>
static void destroy(void *ptr)
{
@@ -1354,7 +1455,7 @@ typename boost::enable_if<
from_nasal_helper(naContext c, naRef ref, const T*)
{
typedef typename nasal::shared_ptr_traits<T>::strong_ref strong_ref;
return nasal::Ghost<strong_ref>::template fromNasal<T>(c, ref);
return T(nasal::Ghost<strong_ref>::fromNasalChecked(c, ref));
}
/**
@@ -1389,7 +1490,7 @@ typename boost::enable_if_c<
from_nasal_helper(naContext c, naRef ref, const T*)
{
typedef SGSharedPtr<typename boost::remove_pointer<T>::type> TypeRef;
return nasal::Ghost<TypeRef>::template fromNasal<T>(c, ref);
return T(nasal::Ghost<TypeRef>::fromNasalChecked(c, ref));
}
/**
@@ -1416,7 +1517,7 @@ typename boost::enable_if<
from_nasal_helper(naContext c, naRef ref, const T*)
{
typedef osg::ref_ptr<typename boost::remove_pointer<T>::type> TypeRef;
return nasal::Ghost<TypeRef>::template fromNasal<T>(c, ref);
return T(nasal::Ghost<TypeRef>::fromNasalChecked(c, ref));
}
#endif /* SG_NASAL_GHOST_HXX_ */

View File

@@ -32,8 +32,9 @@ namespace nasal
class CallContext
{
public:
CallContext(naContext c, size_t argc, naRef* args):
CallContext(naContext c, naRef me, size_t argc, naRef* args):
c(c),
me(me),
argc(argc),
args(args)
{}
@@ -120,6 +121,7 @@ namespace nasal
}
naContext c;
naRef me;
size_t argc;
naRef *args;
};

View File

@@ -0,0 +1,49 @@
// Manage lifetime and encapsulate a Nasal context
//
// 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 "NasalContext.hxx"
namespace nasal
{
//----------------------------------------------------------------------------
Context::Context():
_ctx(naNewContext())
{
}
//----------------------------------------------------------------------------
Context::~Context()
{
naFreeContext(_ctx);
}
//----------------------------------------------------------------------------
Context::operator naContext()
{
return _ctx;
}
//----------------------------------------------------------------------------
Hash Context::newHash()
{
return Hash(_ctx);
}
} // namespace nasal

View File

@@ -0,0 +1,46 @@
///@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_NASAL_CONTEXT_HXX_
#define SG_NASAL_CONTEXT_HXX_
#include "NasalHash.hxx"
namespace nasal
{
/**
* Manage lifetime and encapsulate a Nasal context.
*/
class Context
{
public:
Context();
~Context();
operator naContext();
Hash newHash();
protected:
naContext _ctx;
};
} // namespace nasal
#endif /* SG_NASAL_CONTEXT_HXX_ */

View File

@@ -1,4 +1,4 @@
///@file Wrapper class for Nasal hashes
///@file
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -29,7 +29,7 @@ namespace nasal
{
/**
* A Nasal Hash
* Wrapper class for Nasal hashes.
*/
class Hash
{
@@ -118,7 +118,6 @@ namespace nasal
*
* @tparam Sig Function signature
* @param name Member name
* @param key Member key
*/
template<class Sig, class Key>
typename boost::enable_if< boost::is_function<Sig>,

View File

@@ -1,4 +1,4 @@
///@file Object exposed to Nasal including a Nasal hash for data storage.
///@file
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
@@ -19,11 +19,18 @@
#ifndef SG_NASAL_OBJECT_HXX_
#define SG_NASAL_OBJECT_HXX_
#include "NasalContext.hxx"
#include "NasalObjectHolder.hxx"
#include "Ghost.hxx"
#include <boost/preprocessor/iteration/iterate.hpp>
#include <boost/preprocessor/repetition/enum_trailing_binary_params.hpp>
namespace nasal
{
/**
* Object exposed to Nasal including a Nasal hash for data storage.
*/
class Object:
public virtual SGVirtualWeakReferenced
{
@@ -41,6 +48,15 @@ namespace nasal
bool valid() const;
// Build dependency for CMake, gcc, etc.
#define SG_DONT_DO_ANYTHING
# include <simgear/nasal/cppbind/detail/NasalObject_callMethod_templates.hxx>
#undef SG_DONT_DO_ANYTHING
#define BOOST_PP_ITERATION_LIMITS (0, 9)
#define BOOST_PP_FILENAME_1 <simgear/nasal/cppbind/detail/NasalObject_callMethod_templates.hxx>
#include BOOST_PP_ITERATE()
bool _set(naContext c, const std::string& key, naRef val);
bool _get(naContext c, const std::string& key, naRef& out);

View File

@@ -1,4 +1,5 @@
///@file Wrapper class for keeping Nasal objects save from the garbage collector
///@file
/// Wrapper class for keeping Nasal objects save from the garbage collector.
//
// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
//

View File

@@ -1,4 +1,4 @@
///@file Wrapper class for Nasal strings
///@file
//
// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
//
@@ -26,7 +26,7 @@ namespace nasal
{
/**
* A Nasal String
* Wrapper class for Nasal strings.
*/
class String
{
@@ -47,7 +47,7 @@ namespace nasal
*
* @param str Existing Nasal String
*/
String(naRef string);
String(naRef str);
const char* c_str() const;
const char* begin() const;

View File

@@ -0,0 +1,35 @@
#ifndef SG_NASAL_OBJECT_HXX_
# error Nasal cppbind - do not include this file!
#endif
#ifndef SG_DONT_DO_ANYTHING
#define n BOOST_PP_ITERATION()
#define SG_CALL_ARG(z, n, dummy)\
to_nasal<typename boost::call_traits<A##n>::param_type>(ctx, a##n)
template<
class Ret
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
>
Ret callMethod( const std::string& name
BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(n, A, a) )
{
if( !_nasal_impl.valid() )
return Ret();
typedef boost::function<Ret (nasal::Me BOOST_PP_ENUM_TRAILING_PARAMS(n, A))>
MemFunc;
Context ctx;
MemFunc f = get_member<MemFunc>(ctx, _nasal_impl.get_naRef(), name.c_str());
if( f )
return f(nasal::to_nasal(ctx, this) BOOST_PP_ENUM_TRAILING_PARAMS(n, a));
return Ret();
}
#undef SG_CALL_ARG
#undef n
#endif // SG_DONT_DO_ANYTHING

View File

@@ -43,6 +43,9 @@ namespace osg
template<class T> class ref_ptr;
template<class T> class observer_ptr;
template<class T, class Y>
ref_ptr<T> static_pointer_cast(const ref_ptr<Y>&);
class Vec2b;
class Vec2d;
class Vec2f;

View File

@@ -87,7 +87,7 @@ namespace nasal
try
{
return (*func)(nasal::CallContext(c, argc, args));
return (*func)(nasal::CallContext(c, me, argc, args));
}
catch(const std::exception& ex)
{

View File

@@ -47,7 +47,7 @@ namespace nasal
/**
* Get pointer to specific version of from_nasal, converting to a type
* compatible to Var.
* compatible to \a Var.
*/
template<class Var>
struct from_nasal_ptr
@@ -63,6 +63,9 @@ namespace nasal
}
};
/**
* Get member of hash, ghost (also searching in parent objects).
*/
template<class T>
T get_member(naContext c, naRef obj, const std::string& name)
{

View File

@@ -0,0 +1,76 @@
///@file
/// Nasal context for testing and executing code
///
// 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_NASAL_TESTCONTEXT_HXX_
#define SG_NASAL_TESTCONTEXT_HXX_
#include <simgear/nasal/cppbind/NasalCallContext.hxx>
class TestContext:
public nasal::CallContext
{
public:
TestContext():
CallContext(naNewContext(), naNil(), 0, 0)
{}
~TestContext()
{
naFreeContext(c);
}
void runGC()
{
naFreeContext(c);
naGC();
c = naNewContext();
}
template<class T>
T from_str(const std::string& str)
{
return from_nasal<T>(to_nasal(str));
}
naRef exec(const std::string& code_str, nasal::Me me)
{
int err_line = -1;
naRef code = naParseCode( c, to_nasal("<TextContext::exec>"), 0,
(char*)code_str.c_str(), code_str.length(),
&err_line );
if( !naIsCode(code) )
throw std::runtime_error("Failed to parse code: " + code_str);
return naCallMethod(code, me, 0, 0, naNil());
}
template<class T>
T exec(const std::string& code)
{
return from_nasal<T>(exec(code, naNil()));
}
template<class T>
T convert(const std::string& str)
{
return from_nasal<T>(to_nasal(str));
}
};
#endif /* SG_NASAL_TESTCONTEXT_HXX_ */

View File

@@ -1,22 +1,16 @@
#include <simgear/math/SGMath.hxx>
#define BOOST_TEST_MODULE cppbind
#include <BoostTestTargetConfig.h>
#include "Ghost.hxx"
#include "NasalHash.hxx"
#include "NasalString.hxx"
#include <simgear/nasal/cppbind/Ghost.hxx>
#include <simgear/nasal/cppbind/NasalHash.hxx>
#include <simgear/nasal/cppbind/NasalString.hxx>
#include <simgear/math/SGMath.hxx>
#include <simgear/structure/map.hxx>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <cstring>
#include <iostream>
#define VERIFY(a) \
if( !(a) ) \
{ \
std::cerr << "failed: line " << __LINE__ << ": " << #a << std::endl; \
return 1; \
}
enum MyEnum
{
@@ -121,7 +115,7 @@ naRef f_derivedGetX(const Derived& d, naContext c)
}
naRef f_freeFunction(nasal::CallContext c) { return c.requireArg<naRef>(0); }
int main(int argc, char* argv[])
BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
{
naContext c = naNewContext();
naRef r;
@@ -129,44 +123,36 @@ int main(int argc, char* argv[])
using namespace nasal;
r = to_nasal(c, ENUM_ANOTHER);
VERIFY( from_nasal<int>(c, r) == ENUM_ANOTHER );
BOOST_CHECK_EQUAL(from_nasal<int>(c, r), ENUM_ANOTHER);
r = to_nasal(c, "Test");
VERIFY( strncmp("Test", naStr_data(r), naStr_len(r)) == 0 );
VERIFY( from_nasal<std::string>(c, r) == "Test" );
BOOST_CHECK( strncmp("Test", naStr_data(r), naStr_len(r)) == 0 );
BOOST_CHECK_EQUAL(from_nasal<std::string>(c, r), "Test");
r = to_nasal(c, std::string("Test"));
VERIFY( strncmp("Test", naStr_data(r), naStr_len(r)) == 0 );
VERIFY( from_nasal<std::string>(c, r) == "Test" );
BOOST_CHECK( strncmp("Test", naStr_data(r), naStr_len(r)) == 0 );
BOOST_CHECK_EQUAL(from_nasal<std::string>(c, r), "Test");
r = to_nasal(c, 42);
VERIFY( naNumValue(r).num == 42 );
VERIFY( from_nasal<int>(c, r) == 42 );
BOOST_CHECK_EQUAL(naNumValue(r).num, 42);
BOOST_CHECK_EQUAL(from_nasal<int>(c, r), 42);
r = to_nasal(c, 4.2f);
VERIFY( naNumValue(r).num == 4.2f );
VERIFY( from_nasal<float>(c, r) == 4.2f );
BOOST_CHECK_EQUAL(naNumValue(r).num, 4.2f);
BOOST_CHECK_EQUAL(from_nasal<float>(c, r), 4.2f);
float test_data[3] = {0, 4, 2};
r = to_nasal(c, test_data);
SGVec2f vec(0,2);
r = to_nasal(c, vec);
VERIFY( from_nasal<SGVec2f>(c, r) == vec );
BOOST_CHECK_EQUAL(from_nasal<SGVec2f>(c, r), vec);
std::vector<int> std_vec;
r = to_nasal(c, std_vec);
r = to_nasal(c, "string");
try
{
from_nasal<int>(c, r);
std::cerr << "failed: Expected bad_nasal_cast to be thrown" << std::endl;
return 1;
}
catch(nasal::bad_nasal_cast&)
{}
BOOST_CHECK_THROW(from_nasal<int>(c, r), bad_nasal_cast);
Hash hash(c);
hash.set("vec", r);
@@ -175,9 +161,10 @@ int main(int argc, char* argv[])
hash.set("string", std::string("blub"));
hash.set("func", &f_freeFunction);
VERIFY( hash.size() == 5 )
BOOST_CHECK_EQUAL(hash.size(), 5);
for(Hash::const_iterator it = hash.begin(); it != hash.end(); ++it)
VERIFY( hash.get<std::string>(it->getKey()) == it->getValue<std::string>() )
BOOST_CHECK_EQUAL( hash.get<std::string>(it->getKey()),
it->getValue<std::string>() );
Hash::iterator it1, it2;
Hash::const_iterator it3 = it1, it4(it2);
@@ -185,15 +172,15 @@ int main(int argc, char* argv[])
it3 = it2;
r = to_nasal(c, hash);
VERIFY( naIsHash(r) );
BOOST_REQUIRE( naIsHash(r) );
simgear::StringMap string_map = from_nasal<simgear::StringMap>(c, r);
VERIFY( string_map.at("vec") == "string" )
VERIFY( string_map.at("name") == "my-name" )
VERIFY( string_map.at("string") == "blub" )
BOOST_CHECK_EQUAL(string_map.at("vec"), "string");
BOOST_CHECK_EQUAL(string_map.at("name"), "my-name");
BOOST_CHECK_EQUAL(string_map.at("string"), "blub");
VERIFY( hash.get<std::string>("name") == "my-name" );
VERIFY( naIsString(hash.get("name")) );
BOOST_CHECK_EQUAL(hash.get<std::string>("name"), "my-name");
BOOST_CHECK(naIsString(hash.get("name")));
Hash mod = hash.createHash("mod");
mod.set("parent", hash);
@@ -201,35 +188,35 @@ int main(int argc, char* argv[])
// 'func' is a C++ function registered to Nasal and now converted back to C++
boost::function<int (int)> f = hash.get<int (int)>("func");
VERIFY( f );
VERIFY( f(3) == 3 );
BOOST_REQUIRE( f );
BOOST_CHECK_EQUAL(f(3), 3);
boost::function<std::string (int)> fs = hash.get<std::string (int)>("func");
VERIFY( fs );
VERIFY( fs(14) == "14" );
BOOST_REQUIRE( fs );
BOOST_CHECK_EQUAL(fs(14), "14");
typedef boost::function<void (int)> FuncVoidInt;
FuncVoidInt fvi = hash.get<FuncVoidInt>("func");
VERIFY( fvi );
BOOST_REQUIRE( fvi );
fvi(123);
typedef boost::function<std::string (const std::string&, int, float)> FuncMultiArg;
FuncMultiArg fma = hash.get<FuncMultiArg>("func");
VERIFY( fma );
VERIFY( fma("test", 3, .5) == "test" );
BOOST_REQUIRE( fma );
BOOST_CHECK_EQUAL(fma("test", 3, .5), "test");
typedef boost::function<naRef (naRef)> naRefMemFunc;
naRefMemFunc fmem = hash.get<naRefMemFunc>("func");
VERIFY( fmem );
BOOST_REQUIRE( fmem );
naRef ret = fmem(hash.get_naRef()),
hash_ref = hash.get_naRef();
VERIFY( naIsIdentical(ret, hash_ref) );
BOOST_CHECK( naIsIdentical(ret, hash_ref) );
// Check if nasal::Me gets passed as self/me and remaining arguments are
// passed on to function
typedef boost::function<int (Me, int)> MeIntFunc;
MeIntFunc fmeint = hash.get<MeIntFunc>("func");
VERIFY( fmeint(naNil(), 5) == 5 );
BOOST_CHECK_EQUAL(fmeint(naNil(), 5), 5);
//----------------------------------------------------------------------------
// Test exposing classes to Nasal
@@ -276,82 +263,80 @@ int main(int argc, char* argv[])
naRef nasal_ref = to_nasal(c, weak_ptr),
nasal_ptr = to_nasal(c, weak_ptr.get());
VERIFY( naIsGhost(nasal_ref) );
VERIFY( naIsGhost(nasal_ptr) );
BOOST_REQUIRE( naIsGhost(nasal_ref) );
BOOST_REQUIRE( naIsGhost(nasal_ptr) );
SGWeakRefBasedPtr ptr1 = from_nasal<SGWeakRefBasedPtr>(c, nasal_ref),
ptr2 = from_nasal<SGWeakRefBasedPtr>(c, nasal_ptr);
VERIFY( weak_ptr == ptr1 );
VERIFY( weak_ptr == ptr2 );
BOOST_CHECK_EQUAL(weak_ptr, ptr1);
BOOST_CHECK_EQUAL(weak_ptr, ptr2);
VERIFY( Ghost<BasePtr>::isInit() );
BOOST_REQUIRE( Ghost<BasePtr>::isInit() );
nasal::to_nasal(c, DoubleDerived2Ptr());
BasePtr d( new Derived );
naRef derived = to_nasal(c, d);
VERIFY( naIsGhost(derived) );
VERIFY( std::string("DerivedPtr") == naGhost_type(derived)->name );
BOOST_REQUIRE( naIsGhost(derived) );
BOOST_CHECK_EQUAL( std::string("DerivedPtr"), naGhost_type(derived)->name );
// Get member function from ghost...
naRef thisGetter = naNil();
VERIFY( naMember_get(c, derived, to_nasal(c, "this"), &thisGetter) );
VERIFY( naIsFunc(thisGetter) );
BOOST_CHECK( naMember_get(c, derived, to_nasal(c, "this"), &thisGetter) );
BOOST_CHECK( naIsFunc(thisGetter) );
// ...and check if it really gets passed the correct instance
typedef boost::function<unsigned long (Me)> MemFunc;
MemFunc fGetThis = from_nasal<MemFunc>(c, thisGetter);
VERIFY( fGetThis );
VERIFY( fGetThis(derived) == (unsigned long)d.get() );
BOOST_REQUIRE( fGetThis );
BOOST_CHECK_EQUAL( fGetThis(derived), (unsigned long)d.get() );
BasePtr d2( new DoubleDerived );
derived = to_nasal(c, d2);
VERIFY( naIsGhost(derived) );
VERIFY( std::string("DoubleDerivedPtr") == naGhost_type(derived)->name );
BOOST_CHECK( naIsGhost(derived) );
BOOST_CHECK_EQUAL( std::string("DoubleDerivedPtr"),
naGhost_type(derived)->name );
BasePtr d3( new DoubleDerived2 );
derived = to_nasal(c, d3);
VERIFY( naIsGhost(derived) );
VERIFY( std::string("DoubleDerived2Ptr") == naGhost_type(derived)->name );
BOOST_CHECK( naIsGhost(derived) );
BOOST_CHECK_EQUAL( std::string("DoubleDerived2Ptr"),
naGhost_type(derived)->name );
SGRefBasedPtr ref_based( new SGReferenceBasedClass );
naRef na_ref_based = to_nasal(c, ref_based.get());
VERIFY( naIsGhost(na_ref_based) );
VERIFY( from_nasal<SGReferenceBasedClass*>(c, na_ref_based)
== ref_based.get() );
VERIFY( from_nasal<SGRefBasedPtr>(c, na_ref_based) == ref_based );
BOOST_CHECK( naIsGhost(na_ref_based) );
BOOST_CHECK_EQUAL( from_nasal<SGReferenceBasedClass*>(c, na_ref_based),
ref_based.get() );
BOOST_CHECK_EQUAL( from_nasal<SGRefBasedPtr>(c, na_ref_based), ref_based );
VERIFY( Ghost<BasePtr>::isBaseOf(derived) );
VERIFY( Ghost<DerivedPtr>::isBaseOf(derived) );
VERIFY( Ghost<DoubleDerived2Ptr>::isBaseOf(derived) );
VERIFY( from_nasal<BasePtr>(c, derived) == d3 );
VERIFY( from_nasal<BasePtr>(c, derived) != d2 );
VERIFY( from_nasal<DerivedPtr>(c, derived)
== boost::dynamic_pointer_cast<Derived>(d3) );
VERIFY( from_nasal<DoubleDerived2Ptr>(c, derived)
== boost::dynamic_pointer_cast<DoubleDerived2>(d3) );
VERIFY( !from_nasal<DoubleDerivedPtr>(c, derived) );
BOOST_CHECK_EQUAL( from_nasal<BasePtr>(c, derived), d3 );
BOOST_CHECK_NE( from_nasal<BasePtr>(c, derived), d2 );
BOOST_CHECK_EQUAL( from_nasal<DerivedPtr>(c, derived),
boost::dynamic_pointer_cast<Derived>(d3) );
BOOST_CHECK_EQUAL( from_nasal<DoubleDerived2Ptr>(c, derived),
boost::dynamic_pointer_cast<DoubleDerived2>(d3) );
BOOST_CHECK_THROW( from_nasal<DoubleDerivedPtr>(c, derived), bad_nasal_cast );
std::map<std::string, BasePtr> instances;
VERIFY( naIsHash(to_nasal(c, instances)) );
BOOST_CHECK( naIsHash(to_nasal(c, instances)) );
std::map<std::string, DerivedPtr> instances_d;
VERIFY( naIsHash(to_nasal(c, instances_d)) );
BOOST_CHECK( naIsHash(to_nasal(c, instances_d)) );
std::map<std::string, int> int_map;
VERIFY( naIsHash(to_nasal(c, int_map)) );
BOOST_CHECK( naIsHash(to_nasal(c, int_map)) );
std::map<std::string, std::vector<int> > int_vector_map;
VERIFY( naIsHash(to_nasal(c, int_vector_map)) );
BOOST_CHECK( naIsHash(to_nasal(c, int_vector_map)) );
simgear::StringMap dict =
simgear::StringMap("hello", "value")
("key2", "value2");
naRef na_dict = to_nasal(c, dict);
VERIFY( naIsHash(na_dict) );
VERIFY( Hash(na_dict, c).get<std::string>("key2") == "value2" );
BOOST_REQUIRE( naIsHash(na_dict) );
BOOST_CHECK_EQUAL( Hash(na_dict, c).get<std::string>("key2"), "value2" );
// Check converting to Ghost if using Nasal hashes with actual ghost inside
// the hashes parents vector
@@ -361,14 +346,14 @@ int main(int argc, char* argv[])
Hash obj(c);
obj.set("parents", parents);
VERIFY( from_nasal<BasePtr>(c, obj.get_naRef()) == d3 );
BOOST_CHECK_EQUAL( from_nasal<BasePtr>(c, obj.get_naRef()), d3 );
// Check recursive parents (aka parent-of-parent)
std::vector<naRef> parents2;
parents2.push_back(obj.get_naRef());
Hash derived_obj(c);
derived_obj.set("parents", parents2);
VERIFY( from_nasal<BasePtr>(c, derived_obj.get_naRef()) == d3 );
BOOST_CHECK_EQUAL( from_nasal<BasePtr>(c, derived_obj.get_naRef()), d3 );
std::vector<naRef> nasal_objects;
nasal_objects.push_back( Ghost<BasePtr>::makeGhost(c, d) );
@@ -377,9 +362,9 @@ int main(int argc, char* argv[])
naRef obj_vec = to_nasal(c, nasal_objects);
std::vector<BasePtr> objects = from_nasal<std::vector<BasePtr> >(c, obj_vec);
VERIFY( objects[0] == d );
VERIFY( objects[1] == d2 );
VERIFY( objects[2] == d3 );
BOOST_CHECK_EQUAL( objects[0], d );
BOOST_CHECK_EQUAL( objects[1], d2 );
BOOST_CHECK_EQUAL( objects[2], d3 );
{
// Calling fallback setter for unset values
@@ -390,9 +375,9 @@ int main(int argc, char* argv[])
&errLine );
ret = naCallMethod(code, derived, 0, 0, naNil());
VERIFY( !naGetError(c) ) // TODO real error check (this seems to always
// return 0...
VERIFY( from_nasal<int>(c, ret) == 3 )
BOOST_REQUIRE( !naGetError(c) ); // TODO real error check (this seems to
// always return 0...
BOOST_CHECK_EQUAL( from_nasal<int>(c, ret), 3 );
}
{
// Calling generic (fallback) getter
@@ -403,9 +388,9 @@ int main(int argc, char* argv[])
&errLine );
ret = naCallMethod(code, derived, 0, 0, naNil());
VERIFY( !naGetError(c) ) // TODO real error check (this seems to always
// return 0...
VERIFY( from_nasal<std::string>(c, ret) == "generic-get" );
BOOST_REQUIRE( !naGetError(c) ); // TODO real error check (this seems to
// always return 0...
BOOST_CHECK_EQUAL( from_nasal<std::string>(c, ret), "generic-get" );
}
//----------------------------------------------------------------------------
@@ -421,55 +406,53 @@ int main(int argc, char* argv[])
to_nasal(c, int_vec),
to_nasal(c, map)
};
CallContext cc(c, sizeof(args)/sizeof(args[0]), args);
VERIFY( cc.requireArg<std::string>(0) == "test-arg" );
VERIFY( cc.getArg<std::string>(0) == "test-arg" );
VERIFY( cc.getArg<std::string>(10) == "" );
VERIFY( cc.isString(0) );
VERIFY( !cc.isNumeric(0) );
VERIFY( !cc.isVector(0) );
VERIFY( !cc.isHash(0) );
VERIFY( !cc.isGhost(0) );
VERIFY( cc.isNumeric(1) );
VERIFY( cc.isVector(2) );
VERIFY( cc.isHash(3) );
CallContext cc(c, naNil(), sizeof(args)/sizeof(args[0]), args);
BOOST_CHECK_EQUAL( cc.requireArg<std::string>(0), "test-arg" );
BOOST_CHECK_EQUAL( cc.getArg<std::string>(0), "test-arg" );
BOOST_CHECK_EQUAL( cc.getArg<std::string>(10), "" );
BOOST_CHECK( cc.isString(0) );
BOOST_CHECK( !cc.isNumeric(0) );
BOOST_CHECK( !cc.isVector(0) );
BOOST_CHECK( !cc.isHash(0) );
BOOST_CHECK( !cc.isGhost(0) );
BOOST_CHECK( cc.isNumeric(1) );
BOOST_CHECK( cc.isVector(2) );
BOOST_CHECK( cc.isHash(3) );
naRef args_vec = nasal::to_nasal(c, args);
VERIFY( naIsVector(args_vec) );
BOOST_CHECK( naIsVector(args_vec) );
//----------------------------------------------------------------------------
// Test nasal::String
//----------------------------------------------------------------------------
String string( to_nasal(c, "Test") );
VERIFY( from_nasal<std::string>(c, string.get_naRef()) == "Test" );
VERIFY( string.c_str() == std::string("Test") );
VERIFY( string.starts_with(string) );
VERIFY( string.starts_with(String(c, "T")) );
VERIFY( string.starts_with(String(c, "Te")) );
VERIFY( string.starts_with(String(c, "Tes")) );
VERIFY( string.starts_with(String(c, "Test")) );
VERIFY( !string.starts_with(String(c, "Test1")) );
VERIFY( !string.starts_with(String(c, "bb")) );
VERIFY( !string.starts_with(String(c, "bbasdasdafasd")) );
VERIFY( string.ends_with(String(c, "t")) );
VERIFY( string.ends_with(String(c, "st")) );
VERIFY( string.ends_with(String(c, "est")) );
VERIFY( string.ends_with(String(c, "Test")) );
VERIFY( !string.ends_with(String(c, "1Test")) );
VERIFY( !string.ends_with(String(c, "abc")) );
VERIFY( !string.ends_with(String(c, "estasdasd")) );
VERIFY( string.find('e') == 1 );
VERIFY( string.find('9') == String::npos );
VERIFY( string.find_first_of(String(c, "st")) == 2 );
VERIFY( string.find_first_of(String(c, "st"), 3) == 3 );
VERIFY( string.find_first_of(String(c, "xyz")) == String::npos );
VERIFY( string.find_first_not_of(String(c, "Tst")) == 1 );
VERIFY( string.find_first_not_of(String(c, "Tse"), 2) == 3 );
VERIFY( string.find_first_not_of(String(c, "abc")) == 0 );
VERIFY( string.find_first_not_of(String(c, "abc"), 20) == String::npos );
BOOST_CHECK_EQUAL( from_nasal<std::string>(c, string.get_naRef()), "Test" );
BOOST_CHECK_EQUAL( string.c_str(), std::string("Test") );
BOOST_CHECK( string.starts_with(string) );
BOOST_CHECK( string.starts_with(String(c, "T")) );
BOOST_CHECK( string.starts_with(String(c, "Te")) );
BOOST_CHECK( string.starts_with(String(c, "Tes")) );
BOOST_CHECK( string.starts_with(String(c, "Test")) );
BOOST_CHECK( !string.starts_with(String(c, "Test1")) );
BOOST_CHECK( !string.starts_with(String(c, "bb")) );
BOOST_CHECK( !string.starts_with(String(c, "bbasdasdafasd")) );
BOOST_CHECK( string.ends_with(String(c, "t")) );
BOOST_CHECK( string.ends_with(String(c, "st")) );
BOOST_CHECK( string.ends_with(String(c, "est")) );
BOOST_CHECK( string.ends_with(String(c, "Test")) );
BOOST_CHECK( !string.ends_with(String(c, "1Test")) );
BOOST_CHECK( !string.ends_with(String(c, "abc")) );
BOOST_CHECK( !string.ends_with(String(c, "estasdasd")) );
BOOST_CHECK_EQUAL( string.find('e'), 1 );
BOOST_CHECK_EQUAL( string.find('9'), String::npos );
BOOST_CHECK_EQUAL( string.find_first_of(String(c, "st")), 2 );
BOOST_CHECK_EQUAL( string.find_first_of(String(c, "st"), 3), 3 );
BOOST_CHECK_EQUAL( string.find_first_of(String(c, "xyz")), String::npos );
BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "Tst")), 1 );
BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "Tse"), 2), 3 );
BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "abc")), 0 );
BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "abc"), 20), String::npos );
naFreeContext(c);
return 0;
}

View File

@@ -1,7 +1,9 @@
#define BOOST_TEST_MODULE cppbind
#include <BoostTestTargetConfig.h>
#include "Ghost.hxx"
#include <simgear/nasal/cppbind/Ghost.hxx>
#include <simgear/nasal/cppbind/NasalContext.hxx>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
@@ -49,14 +51,19 @@ BOOST_STATIC_ASSERT(( nasal::supports_weak_ref<DerivedPtr>::value));
BOOST_STATIC_ASSERT(( nasal::supports_weak_ref<DerivedWeakPtr>::value));
BOOST_STATIC_ASSERT((!nasal::supports_weak_ref<SGReferencedPtr>::value));
BOOST_AUTO_TEST_CASE( ghost_weak_strong_nasal_conversion )
static void setupGhosts()
{
nasal::Ghost<Base1Ptr>::init("Base1");
nasal::Ghost<Base2Ptr>::init("Base2");
nasal::Ghost<DerivedPtr>::init("Derived")
.bases<Base1Ptr>()
.bases<Base2Ptr>();
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( ghost_weak_strong_nasal_conversion )
{
setupGhosts();
naContext c = naNewContext();
DerivedPtr d = new Derived();
@@ -70,7 +77,8 @@ BOOST_AUTO_TEST_CASE( ghost_weak_strong_nasal_conversion )
BOOST_CHECK_EQUAL( nasal::from_nasal<DerivedPtr>(c, na_d), d );
d.reset();
BOOST_REQUIRE( !nasal::from_nasal<DerivedPtr>(c, na_d) );
BOOST_REQUIRE_THROW( nasal::from_nasal<DerivedPtr>(c, na_d),
nasal::bad_nasal_cast );
// store strong pointer and extract weak pointer
d.reset(new Derived);
@@ -91,6 +99,45 @@ BOOST_AUTO_TEST_CASE( ghost_weak_strong_nasal_conversion )
BOOST_REQUIRE( !weak.lock() );
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( ghost_casting_storage )
{
setupGhosts();
nasal::Context c;
// Check converting to and from every class in the hierarchy for an instance
// of the leaf class
DerivedPtr d = new Derived();
naRef na_d = nasal::to_nasal(c, d),
na_b1 = nasal::to_nasal(c, Base1Ptr(d)),
na_b2 = nasal::to_nasal(c, Base2Ptr(d));
Derived *d0 = nasal::from_nasal<Derived*>(c, na_d),
*d1 = nasal::from_nasal<Derived*>(c, na_b1),
*d2 = nasal::from_nasal<Derived*>(c, na_b2);
BOOST_CHECK_EQUAL(d0, d.get());
BOOST_CHECK_EQUAL(d1, d.get());
BOOST_CHECK_EQUAL(d2, d.get());
Base1 *b1 = nasal::from_nasal<Base1*>(c, na_b1);
BOOST_CHECK_EQUAL(b1, static_cast<Base1*>(d.get()));
Base2 *b2 = nasal::from_nasal<Base2*>(c, na_b2);
BOOST_CHECK_EQUAL(b2, static_cast<Base2*>(d.get()));
// Check converting from base class instance to derived classes is not
// possible
Base1Ptr b1_ref = new Base1();
na_b1 = nasal::to_nasal(c, b1_ref);
BOOST_CHECK_EQUAL(nasal::from_nasal<Base1*>(c, na_b1), b1_ref.get());
BOOST_CHECK_THROW(nasal::from_nasal<Base2*>(c, na_b1), nasal::bad_nasal_cast);
BOOST_CHECK_THROW(nasal::from_nasal<Derived*>(c, na_b1), nasal::bad_nasal_cast);
}
//------------------------------------------------------------------------------
#define CHECK_PTR_STORAGE_TRAIT_TYPE(ptr_t, storage)\
BOOST_STATIC_ASSERT((boost::is_same<\
nasal::shared_ptr_storage<ptr_t>::storage_type,\

View File

@@ -0,0 +1,93 @@
#define BOOST_TEST_MODULE nasal
#include <BoostTestTargetConfig.h>
#include "TestContext.hxx"
#include <iostream>
#include <set>
static std::set<intptr_t> active_instances;
static void ghost_destroy(void* p)
{
active_instances.erase((intptr_t)p);
}
static naGhostType ghost_type = {
&ghost_destroy,
"TestGhost",
0, // get_member
0 // set_member
};
static naRef createTestGhost(TestContext& c, intptr_t p)
{
active_instances.insert(p);
return naNewGhost(c.c, &ghost_type, (void*)p);
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( ghost_gc )
{
TestContext c;
BOOST_REQUIRE(active_instances.empty());
//-----------------------------------------------
// Just create ghosts and let the gc destroy them
naRef g1 = createTestGhost(c, 1),
g2 = createTestGhost(c, 2);
BOOST_CHECK_EQUAL(active_instances.count(1), 1);
BOOST_CHECK_EQUAL(active_instances.count(2), 1);
BOOST_CHECK_EQUAL(active_instances.size(), 2);
c.runGC();
BOOST_REQUIRE(active_instances.empty());
//-----------------------------------------------
// Create some more ghosts and save one instance
// from being garbage collected.
g1 = createTestGhost(c, 1);
g2 = createTestGhost(c, 2);
int gc1 = naGCSave(g1);
c.runGC();
BOOST_CHECK_EQUAL(active_instances.count(1), 1);
BOOST_CHECK_EQUAL(active_instances.size(), 1);
naGCRelease(gc1);
c.runGC();
BOOST_REQUIRE(active_instances.empty());
//-----------------------------------------------
// Now test attaching one ghost to another
g1 = createTestGhost(c, 1);
g2 = createTestGhost(c, 2);
gc1 = naGCSave(g1);
naGhost_setData(g1, g2); // bind lifetime of g2 to g1...
c.runGC();
BOOST_CHECK_EQUAL(active_instances.count(1), 1);
BOOST_CHECK_EQUAL(active_instances.count(2), 1);
BOOST_CHECK_EQUAL(active_instances.size(), 2);
naGhost_setData(g1, naNil()); // cut connection
c.runGC();
BOOST_CHECK_EQUAL(active_instances.count(1), 1);
BOOST_CHECK_EQUAL(active_instances.size(), 1);
naGCRelease(gc1);
c.runGC();
BOOST_REQUIRE(active_instances.empty());
}

View File

@@ -1,51 +1,7 @@
#define BOOST_TEST_MODULE cppbind
#define BOOST_TEST_MODULE nasal
#include <BoostTestTargetConfig.h>
#include "NasalCallContext.hxx"
class TestContext:
public nasal::CallContext
{
public:
TestContext():
CallContext(naNewContext(), 0, 0)
{}
~TestContext()
{
naFreeContext(c);
}
template<class T>
T from_str(const std::string& str)
{
return from_nasal<T>(to_nasal(str));
}
naRef exec(const std::string& code_str, nasal::Me me)
{
int err_line = -1;
naRef code = naParseCode( c, to_nasal("<TextContext::exec>"), 0,
(char*)code_str.c_str(), code_str.length(),
&err_line );
if( !naIsCode(code) )
throw std::runtime_error("Failed to parse code: " + code_str);
return naCallMethod(code, me, 0, 0, naNil());
}
template<class T>
T exec(const std::string& code)
{
return from_nasal<T>(exec(code, naNil()));
}
template<class T>
T convert(const std::string& str)
{
return from_nasal<T>(to_nasal(str));
}
};
#include "TestContext.hxx"
static void runNumTests( double (TestContext::*test_double)(const std::string&),
int (TestContext::*test_int)(const std::string&) )

View File

@@ -161,7 +161,7 @@ struct naFunc {
struct naCCode {
GC_HEADER;
union {
naCFunction fptr; //<! pointer to simple callback function. Invalid if
naCFunction fptr; //!< pointer to simple callback function. Invalid if
// fptru is not NULL.
struct {
void* user_data;
@@ -175,6 +175,7 @@ struct naGhost {
GC_HEADER;
naGhostType* gtype;
void* ptr;
naRef data; //!< Nasal data bound to the lifetime of the ghost.
};
struct naPool {

View File

@@ -177,7 +177,7 @@ static void newBlock(struct naPool* p, int need)
newb->next = p->blocks;
p->blocks = newb;
naBZero(newb->block, need * p->elemsz);
if(need > p->freesz - p->freetop) need = p->freesz - p->freetop;
p->nfree = 0;
p->free = p->free0 + p->freetop;
@@ -263,6 +263,9 @@ static void mark(naRef r)
mark(PTR(r).func->namespace);
mark(PTR(r).func->next);
break;
case T_GHOST:
mark(PTR(r).ghost->data);
break;
}
}
@@ -300,7 +303,7 @@ static void reap(struct naPool* p)
// allocs of this type until the next collection
globals->allocCount += total/2;
// Allocate more if necessary (try to keep 25-50% of the objects
// available)
if(p->nfree < total/4) {

View File

@@ -143,10 +143,11 @@ naRef naNewGhost(naContext c, naGhostType* type, void* ptr)
// ensure 'simple' ghost users don't see garbage for these fields
type->get_member = 0;
type->set_member = 0;
ghost = naNew(c, T_GHOST);
PTR(ghost).ghost->gtype = type;
PTR(ghost).ghost->ptr = ptr;
PTR(ghost).ghost->data = naNil();
return ghost;
}
@@ -155,6 +156,7 @@ naRef naNewGhost2(naContext c, naGhostType* t, void* ptr)
naRef ghost = naNew(c, T_GHOST);
PTR(ghost).ghost->gtype = t;
PTR(ghost).ghost->ptr = ptr;
PTR(ghost).ghost->data = naNil();
return ghost;
}
@@ -170,9 +172,21 @@ void* naGhost_ptr(naRef ghost)
return PTR(ghost).ghost->ptr;
}
void naGhost_setData(naRef ghost, naRef data)
{
if(IS_GHOST(ghost))
PTR(ghost).ghost->data = data;
}
naRef naGhost_data(naRef ghost)
{
if(!IS_GHOST(ghost)) return naNil();
return PTR(ghost).ghost->data;
}
naRef naNil()
{
naRef r;
naRef r;
SETPTR(r, 0);
return r;
}

View File

@@ -281,6 +281,16 @@ naRef naNewGhost(naContext c, naGhostType* t, void* ghost);
naRef naNewGhost2(naContext c, naGhostType* t, void* ghost);
naGhostType* naGhost_type(naRef ghost);
void* naGhost_ptr(naRef ghost);
/**
* Attach a nasal object to the given ghost. Binds the lifetime of @a data to
* the lifetime of the @a ghost.
*/
void naGhost_setData(naRef ghost, naRef data);
/**
* Retrieve the object attached to the @a ghost, previously set with
* naGhost_setData().
*/
naRef naGhost_data(naRef ghost);
int naIsGhost(naRef r);
// Acquires a "modification lock" on a context, allowing the C code to

View File

@@ -88,7 +88,7 @@ voidpf ZCALLBACK fopen_mem_func (opaque, filename, mode)
* size of an int and therefore may need addressing for 64bit
* architectures
*/
if (sscanf(filename,"%p+%x",&mem->base,&mem->size)!=2)
if (sscanf(filename,"%p+%lx",&mem->base,&mem->size)!=2)
return NULL;
if (mode & ZLIB_FILEFUNC_MODE_CREATE)
@@ -202,7 +202,7 @@ int ZCALLBACK ferror_mem_func (opaque, stream)
voidpf opaque;
voidpf stream;
{
ourmemory_t *mem = (ourmemory_t *)stream;
// ourmemory_t *mem = (ourmemory_t *)stream;
/* We never return errors */
return 0;
}

View File

@@ -39,8 +39,8 @@ namespace simgear
/**
* Remove the property listener of the element.
*
* You will need to call the appropriate methods (#childAdded,
* #childRemoved, #valueChanged) yourself to ensure the element still
* You will need to call the appropriate methods (childAdded(),
* childRemoved(), valueChanged()) yourself to ensure the element still
* receives the needed events.
*/
void removeListener();

View File

@@ -60,11 +60,22 @@ namespace simgear
return 0.5 + 0.5 * (*easeOut)(t - 1);
}
/**
* Helper for exponential ease out with integer exponent.
*
* @tparam N Exponent.
* @tparam is_odd If the exponent is odd.
*/
template<size_t N, bool is_odd>
struct easeOutImpl;
/// http://easings.net/#easeOutCubic (N = 3)
/// http://easings.net/#easeOutQuint (N = 5)
/**
* Ease out with odd integer exponent.
*
* @tparam N Exponent.
* @see http://easings.net/#easeOutCubic (N = 3)
* @see http://easings.net/#easeOutQuint (N = 5)
*/
template<size_t N>
struct easeOutImpl<N, true>
{
@@ -74,8 +85,13 @@ namespace simgear
}
};
/// http://easings.net/#easeOutQuad (N = 2)
/// http://easings.net/#easeOutQuart (N = 4)
/**
* Ease out with even integer exponent.
*
* @tparam N Exponent.
* @see http://easings.net/#easeOutQuad (N = 2)
* @see http://easings.net/#easeOutQuart (N = 4)
*/
template<size_t N>
struct easeOutImpl<N, false>
{
@@ -85,10 +101,15 @@ namespace simgear
}
};
/// http://easings.net/#easeOutQuad (N = 2)
/// http://easings.net/#easeOutCubic (N = 3)
/// http://easings.net/#easeOutQuart (N = 4)
/// http://easings.net/#easeOutQuint (N = 5)
/**
* Exponential ease out with integer exponent.
*
* @see http://easings.net/#easeOutQuad (N = 2)
* @see http://easings.net/#easeOutCubic (N = 3)
* @see http://easings.net/#easeOutQuart (N = 4)
* @see http://easings.net/#easeOutQuint (N = 5)
* @see easeOutImpl
*/
template<size_t N>
double easeOutPow(double t)
{

View File

@@ -52,7 +52,6 @@ using std::cerr;
using std::endl;
using std::find;
using std::sort;
using std::string;
using std::vector;
using std::stringstream;
@@ -104,7 +103,7 @@ parse_name (const SGPropertyNode *node, const Range &path)
i++;
}
if (i != max && *i != '/')
throw string("illegal character after . or ..");
throw std::string("illegal character after . or ..");
} else if (isalpha(*i) || *i == '_') {
i++;
@@ -117,7 +116,7 @@ parse_name (const SGPropertyNode *node, const Range &path)
} else if (*i == '[' || *i == '/') {
break;
} else {
string err = "'";
std::string err = "'";
err.push_back(*i);
err.append("' found in propertyname after '"+node->getNameString()+"'");
err.append("\nname may contain only ._- and alphanumeric characters");
@@ -129,7 +128,7 @@ parse_name (const SGPropertyNode *node, const Range &path)
else {
if (path.begin() == i) {
string err = "'";
std::string err = "'";
err.push_back(*i);
err.append("' found in propertyname after '"+node->getNameString()+"'");
err.append("\nname must begin with alpha or '_'");
@@ -140,7 +139,7 @@ parse_name (const SGPropertyNode *node, const Range &path)
}
// Validate the name of a single node
inline bool validateName(const string& name)
inline bool validateName(const std::string& name)
{
using namespace boost;
if (name.empty())
@@ -287,7 +286,7 @@ find_node_aux(SGPropertyNode * current, SplitItr& itr, bool create,
if (equals(name, "..")) {
SGPropertyNode * parent = current->getParent();
if (parent == 0)
throw string("attempt to move past root with '..'");
throw std::string("attempt to move past root with '..'");
return find_node_aux(parent, ++itr, create, last_index);
}
int index = -1;
@@ -320,10 +319,10 @@ find_node_aux(SGPropertyNode * current, SplitItr& itr, bool create,
}
}
if (i == token.end() || *i != ']')
throw string("unterminated index (looking for ']')");
throw std::string("unterminated index (looking for ']')");
} else {
throw string("illegal characters in token: ")
+ string(name.begin(), name.end());
throw std::string("illegal characters in token: ")
+ std::string(name.begin(), name.end());
}
}
}
@@ -733,12 +732,12 @@ SGPropertyNode::SGPropertyNode (Itr begin, Itr end,
_local_val.string_val = 0;
_value.val = 0;
if (!validateName(_name))
throw string("plain name expected instead of '") + _name + '\'';
throw std::string("plain name expected instead of '") + _name + '\'';
}
SGPropertyNode::SGPropertyNode (const string& name,
int index,
SGPropertyNode * parent)
SGPropertyNode::SGPropertyNode( const std::string& name,
int index,
SGPropertyNode * parent)
: _index(index),
_name(name),
_parent(parent),
@@ -750,7 +749,7 @@ SGPropertyNode::SGPropertyNode (const string& name,
_local_val.string_val = 0;
_value.val = 0;
if (!validateName(name))
throw string("plain name expected instead of '") + _name + '\'';
throw std::string("plain name expected instead of '") + _name + '\'';
}
/**
@@ -956,7 +955,7 @@ SGPropertyNode::getChild (const char * name, int index, bool create)
}
SGPropertyNode *
SGPropertyNode::getChild (const string& name, int index, bool create)
SGPropertyNode::getChild (const std::string& name, int index, bool create)
{
SGPropertyNode* node = getExistingChild(name.begin(), name.end(), index);
if (node) {
@@ -1072,10 +1071,10 @@ SGPropertyNode::removeAllChildren()
_children.clear();
}
string
std::string
SGPropertyNode::getDisplayName (bool simplify) const
{
string display_name = _name;
std::string display_name = _name;
if (_index != 0 || !simplify) {
stringstream sstr;
sstr << '[' << _index << ']';
@@ -1084,14 +1083,14 @@ SGPropertyNode::getDisplayName (bool simplify) const
return display_name;
}
string
std::string
SGPropertyNode::getPath (bool simplify) const
{
typedef std::vector<SGConstPropertyNode_ptr> PList;
PList pathList;
for (const SGPropertyNode* node = this; node->_parent; node = node->_parent)
pathList.push_back(node);
string result;
std::string result;
for (PList::reverse_iterator itr = pathList.rbegin(),
rend = pathList.rend();
itr != rend;
@@ -1672,7 +1671,7 @@ bool SGPropertyNode::interpolate( const std::string& type,
//------------------------------------------------------------------------------
bool SGPropertyNode::interpolate( const std::string& type,
const PropertyList& values,
const simgear::PropertyList& values,
const double_list& deltas,
const std::string& easing )
{
@@ -1810,7 +1809,7 @@ SGPropertyNode::untie ()
}
case props::STRING:
case props::UNSPECIFIED: {
string val = getStringValue();
std::string val = getStringValue();
clearValue();
_type = props::STRING;
_local_val.string_val = copy_string(val.c_str());

View File

@@ -252,15 +252,14 @@ class SGRawBase<T, 0> : public SGRawExtended
/**
* Abstract base class for a raw value.
*
* <p>The property manager is implemented in two layers. The {@link
* SGPropertyNode} is the highest and most abstract layer,
* representing an LValue/RValue pair: it records the position of the
* property in the property tree and contains facilities for
* navigation to other nodes. It is guaranteed to be persistent: the
* {@link SGPropertyNode} will not change during a session, even if
* the property is bound and unbound multiple times.</p>
* The property manager is implemented in two layers. The SGPropertyNode is the
* highest and most abstract layer, representing an LValue/RValue pair: it
* records the position of the property in the property tree and contains
* facilities for navigation to other nodes. It is guaranteed to be persistent:
* the SGPropertyNode will not change during a session, even if the property is
* bound and unbound multiple times.
*
* <p>When the property value is not managed internally in the
* When the property value is not managed internally in the
* SGPropertyNode, the SGPropertyNode will contain a reference to an
* SGRawValue (this class), which provides an abstract way to get,
* set, and clone the underlying value. The SGRawValue may change
@@ -268,13 +267,12 @@ class SGRawBase<T, 0> : public SGRawExtended
* unbound to various data source, but the abstract SGPropertyNode
* layer insulates the application from those changes.
*
* <p>The SGPropertyNode class always keeps a *copy* of a raw value,
* not the original one passed to it; if you override a derived class
* but do not replace the {@link #clone} method, strange things will
* happen.</p>
* The SGPropertyNode class always keeps a *copy* of a raw value, not the
* original one passed to it; if you override a derived class but do not replace
* the {@link SGRaw::clone clone()} method, strange things will happen.
*
* <p>All derived SGRawValue classes must implement {@link #getValue},
* {@link #setValue}, and {@link #clone} for the appropriate type.</p>
* All derived SGRawValue classes must implement getValue(), setValue(), and
* {@link SGRaw::clone clone()} for the appropriate type.
*
* @see SGPropertyNode
* @see SGRawValuePointer
@@ -696,9 +694,15 @@ class SGPropertyChangeListener
{
public:
virtual ~SGPropertyChangeListener ();
virtual void valueChanged (SGPropertyNode * node);
virtual void childAdded (SGPropertyNode * parent, SGPropertyNode * child);
virtual void childRemoved (SGPropertyNode * parent, SGPropertyNode * child);
/// Called if value of \a node has changed.
virtual void valueChanged(SGPropertyNode * node);
/// Called if \a child has been added to the given \a parent.
virtual void childAdded(SGPropertyNode * parent, SGPropertyNode * child);
/// Called if \a child has been removed from its \a parent.
virtual void childRemoved(SGPropertyNode * parent, SGPropertyNode * child);
protected:
friend class SGPropertyNode;
@@ -1289,7 +1293,7 @@ public:
*
* @param type Type of interpolation ("numeric", "color", etc.)
* @param values Nodes containing intermediate and target values
* @param duration Durations for each interpolation step (in seconds)
* @param deltas Durations for each interpolation step (in seconds)
* @param easing Easing function (http://easings.net/)
*/
bool interpolate( const std::string& type,

View File

@@ -1,5 +1,5 @@
/**
* \file propsfwwd.hxx
/** \file
*
* Forward declarations for properties (and related structures)
*/
@@ -9,10 +9,10 @@
#include <simgear/structure/SGSharedPtr.hxx>
class SGPropertyNode;
typedef SGSharedPtr<SGPropertyNode> SGPropertyNode_ptr;
typedef SGSharedPtr<const SGPropertyNode> SGConstPropertyNode_ptr;
class SGCondition;
#endif // of SG_PROPS_FWD_HXX

View File

@@ -30,6 +30,7 @@
#include <functional>
#include <iterator>
#include <map>
#include <queue>
#include <utility>
#include <boost/tr1/unordered_map.hpp>
@@ -77,7 +78,8 @@
#include <simgear/structure/OSGUtils.hxx>
#include <simgear/structure/SGExpression.hxx>
#include <simgear/props/vectorPropTemplates.hxx>
#include <simgear/threads/SGThread.hxx>
#include <simgear/threads/SGGuard.hxx>
namespace simgear
{
@@ -87,6 +89,143 @@ using namespace osgUtil;
using namespace effect;
class UniformFactoryImpl {
public:
ref_ptr<Uniform> getUniform( Effect * effect,
const string & name,
Uniform::Type uniformType,
SGConstPropertyNode_ptr valProp,
const SGReaderWriterOptions* options );
void updateListeners( SGPropertyNode* propRoot );
void addListener(DeferredPropertyListener* listener);
private:
// Default names for vector property components
static const char* vec3Names[];
static const char* vec4Names[];
SGMutex _mutex;
typedef boost::tuple<std::string, Uniform::Type, std::string, std::string> UniformCacheKey;
typedef boost::tuple<ref_ptr<Uniform>, SGPropertyChangeListener*> UniformCacheValue;
std::map<UniformCacheKey,ref_ptr<Uniform> > uniformCache;
typedef std::queue<DeferredPropertyListener*> DeferredListenerList;
DeferredListenerList deferredListenerList;
};
const char* UniformFactoryImpl::vec3Names[] = {"x", "y", "z"};
const char* UniformFactoryImpl::vec4Names[] = {"x", "y", "z", "w"};
ref_ptr<Uniform> UniformFactoryImpl::getUniform( Effect * effect,
const string & name,
Uniform::Type uniformType,
SGConstPropertyNode_ptr valProp,
const SGReaderWriterOptions* options )
{
SGGuard<SGMutex> scopeLock(_mutex);
std::string val = "0";
if (valProp->nChildren() == 0) {
// Completely static value
val = valProp->getStringValue();
} else {
// Value references <parameters> section of Effect
const SGPropertyNode* prop = getEffectPropertyNode(effect, valProp);
if (prop) {
if (prop->nChildren() == 0) {
// Static value in parameters section
val = prop->getStringValue();
} else {
// Dynamic property value in parameters section
val = getGlobalProperty(prop, options);
}
} else {
SG_LOG(SG_GL,SG_DEBUG,"Invalid parameter " << valProp->getName() << " for uniform " << name << " in Effect ");
}
}
UniformCacheKey key = boost::make_tuple(name, uniformType, val, effect->getName());
ref_ptr<Uniform> uniform = uniformCache[key];
if (uniform.valid()) {
// We've got a hit to cache - simply return it
return uniform;
}
SG_LOG(SG_GL,SG_DEBUG,"new uniform " << name << " value " << uniformCache.size());
uniformCache[key] = uniform = new Uniform;
uniform->setName(name);
uniform->setType(uniformType);
switch (uniformType) {
case Uniform::BOOL:
initFromParameters(effect, valProp, uniform.get(),
static_cast<bool (Uniform::*)(bool)>(&Uniform::set),
options);
break;
case Uniform::FLOAT:
initFromParameters(effect, valProp, uniform.get(),
static_cast<bool (Uniform::*)(float)>(&Uniform::set),
options);
break;
case Uniform::FLOAT_VEC3:
initFromParameters(effect, valProp, uniform.get(),
static_cast<bool (Uniform::*)(const Vec3&)>(&Uniform::set),
vec3Names, options);
break;
case Uniform::FLOAT_VEC4:
initFromParameters(effect, valProp, uniform.get(),
static_cast<bool (Uniform::*)(const Vec4&)>(&Uniform::set),
vec4Names, options);
break;
case Uniform::INT:
case Uniform::SAMPLER_1D:
case Uniform::SAMPLER_2D:
case Uniform::SAMPLER_3D:
case Uniform::SAMPLER_1D_SHADOW:
case Uniform::SAMPLER_2D_SHADOW:
case Uniform::SAMPLER_CUBE:
initFromParameters(effect, valProp, uniform.get(),
static_cast<bool (Uniform::*)(int)>(&Uniform::set),
options);
break;
default: // avoid compiler warning
SG_LOG(SG_ALL,SG_ALERT,"UNKNOWN Uniform type '" << uniformType << "'");
break;
}
return uniform;
}
void UniformFactoryImpl::addListener(DeferredPropertyListener* listener)
{
if (listener != 0) {
// Uniform requires a property listener. Add it to the list to be
// created when the main thread gets to it.
deferredListenerList.push(listener);
}
}
void UniformFactoryImpl::updateListeners( SGPropertyNode* propRoot )
{
SGGuard<SGMutex> scopeLock(_mutex);
if (deferredListenerList.empty()) return;
SG_LOG(SG_GL,SG_DEBUG,"Adding " << deferredListenerList.size() << " listeners for effects.");
// Instantiate all queued listeners
while (! deferredListenerList.empty()) {
DeferredPropertyListener* listener = deferredListenerList.front();
listener->activate(propRoot);
deferredListenerList.pop();
}
}
typedef Singleton<UniformFactoryImpl> UniformFactory;
Effect::Effect()
: _cache(0), _isRealized(false)
{
@@ -104,6 +243,8 @@ Effect::Effect(const Effect& rhs, const CopyOp& copyop)
techniques.push_back(static_cast<Technique*>(copyop(itr->get())));
generator = rhs.generator;
_name = rhs._name;
_name += " clone";
}
// Assume that the last technique is always valid.
@@ -173,10 +314,6 @@ void buildPass(Effect* effect, Technique* tniq, const SGPropertyNode* prop,
}
}
// Default names for vector property components
const char* vec3Names[] = {"x", "y", "z"};
const char* vec4Names[] = {"x", "y", "z", "w"};
osg::Vec4f getColor(const SGPropertyNode* prop)
{
if (prop->nChildren() == 0) {
@@ -898,10 +1035,10 @@ struct UniformBuilder :public PassAttributeBuilder
}
if (!isAttributeActive(effect, prop))
return;
const SGPropertyNode* nameProp = prop->getChild("name");
const SGPropertyNode* typeProp = prop->getChild("type");
const SGPropertyNode* positionedProp = prop->getChild("positioned");
const SGPropertyNode* valProp = prop->getChild("value");
SGConstPropertyNode_ptr nameProp = prop->getChild("name");
SGConstPropertyNode_ptr typeProp = prop->getChild("type");
SGConstPropertyNode_ptr positionedProp = prop->getChild("positioned");
SGConstPropertyNode_ptr valProp = prop->getChild("value");
string name;
Uniform::Type uniformType = Uniform::FLOAT;
if (nameProp) {
@@ -941,44 +1078,9 @@ struct UniformBuilder :public PassAttributeBuilder
} else {
findAttr(uniformTypes, typeProp, uniformType);
}
ref_ptr<Uniform> uniform = new Uniform;
uniform->setName(name);
uniform->setType(uniformType);
switch (uniformType) {
case Uniform::BOOL:
initFromParameters(effect, valProp, uniform.get(),
static_cast<bool (Uniform::*)(bool)>(&Uniform::set),
options);
break;
case Uniform::FLOAT:
initFromParameters(effect, valProp, uniform.get(),
static_cast<bool (Uniform::*)(float)>(&Uniform::set),
options);
break;
case Uniform::FLOAT_VEC3:
initFromParameters(effect, valProp, uniform.get(),
static_cast<bool (Uniform::*)(const Vec3&)>(&Uniform::set),
vec3Names, options);
break;
case Uniform::FLOAT_VEC4:
initFromParameters(effect, valProp, uniform.get(),
static_cast<bool (Uniform::*)(const Vec4&)>(&Uniform::set),
vec4Names, options);
break;
case Uniform::INT:
case Uniform::SAMPLER_1D:
case Uniform::SAMPLER_2D:
case Uniform::SAMPLER_3D:
case Uniform::SAMPLER_1D_SHADOW:
case Uniform::SAMPLER_2D_SHADOW:
case Uniform::SAMPLER_CUBE:
initFromParameters(effect, valProp, uniform.get(),
static_cast<bool (Uniform::*)(int)>(&Uniform::set),
options);
break;
default: // avoid compiler warning
break;
}
ref_ptr<Uniform> uniform = UniformFactory::instance()->
getUniform( effect, name, uniformType, valProp, options );
// optimize common uniforms
if (uniformType == Uniform::SAMPLER_2D || uniformType == Uniform::INT)
{
@@ -1005,6 +1107,7 @@ InstallAttributeBuilder<UniformBuilder> installUniform("uniform");
// Not sure what to do with "name". At one point I wanted to use it to
// order the passes, but I do support render bin and stuff too...
// Clément de l'Hamaide 10/2014: "name" is now used in the UniformCacheKey
struct NameBuilder : public PassAttributeBuilder
{
@@ -1336,6 +1439,11 @@ bool Effect::realizeTechniques(const SGReaderWriterOptions* options)
return true;
}
void Effect::addDeferredPropertyListener(DeferredPropertyListener* listener)
{
UniformFactory::instance()->addListener(listener);
}
void Effect::InitializeCallback::doUpdate(osg::Node* node, osg::NodeVisitor* nv)
{
EffectGeode* eg = dynamic_cast<EffectGeode*>(node);
@@ -1345,15 +1453,9 @@ void Effect::InitializeCallback::doUpdate(osg::Node* node, osg::NodeVisitor* nv)
if (!effect)
return;
SGPropertyNode* root = getPropertyRoot();
for (vector<SGSharedPtr<Updater> >::iterator itr = effect->_extraData.begin(),
end = effect->_extraData.end();
itr != end;
++itr) {
InitializeWhenAdded* adder
= dynamic_cast<InitializeWhenAdded*>(itr->ptr());
if (adder)
adder->initOnAdd(effect, root);
}
// Initialize all queued listeners
UniformFactory::instance()->updateListeners(root);
}
bool Effect::Key::EqualTo::operator()(const Effect::Key& lhs,

View File

@@ -54,23 +54,10 @@ class SGReaderWriterOptions;
* things, like manipulations of the global property tree, are are
* only safe in the update process.
*/
class InitializeWhenAdded
{
class DeferredPropertyListener {
public:
InitializeWhenAdded() : _initialized(false) {};
virtual ~InitializeWhenAdded() {};
void initOnAdd(Effect* effect, SGPropertyNode* propRoot)
{
if (!_initialized) {
initOnAddImpl(effect, propRoot);
_initialized = true;
}
}
bool getInitialized() const { return _initialized; }
private:
virtual void initOnAddImpl(Effect* effect, SGPropertyNode* propRoot) = 0;
bool _initialized;
virtual void activate(SGPropertyNode* propRoot) {};
virtual ~DeferredPropertyListener() {};
};
class Effect : public osg::Object
@@ -104,16 +91,7 @@ public:
* Build the techniques from the effect properties.
*/
bool realizeTechniques(const SGReaderWriterOptions* options = 0);
/**
* Updaters that should be derefed when the effect is
* deleted. Updaters arrange to be run by listening on properties
* or something.
*/
struct Updater : public virtual SGReferenced
{
virtual ~Updater() {}
};
void addUpdater(Updater* data) { _extraData.push_back(data); }
void addDeferredPropertyListener(DeferredPropertyListener* listener);
// Callback that is added to the effect geode to initialize the
// effect.
friend struct InitializeCallback;
@@ -121,8 +99,10 @@ public:
{
void doUpdate(osg::Node* node, osg::NodeVisitor* nv);
};
std::string getName(){return _name;}
void setName(std::string name){_name = name;}
protected:
std::vector<SGSharedPtr<Updater> > _extraData;
~Effect();
// Support for a cache of effects that inherit from this one, so
// Effect objects with the same parameters and techniques can be
@@ -160,6 +140,7 @@ protected:
friend Effect* makeEffect(SGPropertyNode* prop, bool realizeTechniques,
const SGReaderWriterOptions* options);
bool _isRealized;
std::string _name;
};
// Automatic support for boost hash function
size_t hash_value(const Effect::Key&);

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