Compare commits

..

97 Commits

Author SHA1 Message Date
Torsten Dreyer
489573329e new version: 2018.2.0 2018-02-18 21:22:54 +01:00
Torsten Dreyer
843df4b334 new version: 2018.1.1 2018-02-18 21:22:54 +01:00
Erik Hofman
ef1821c025 Switch to C++11 2018-02-16 16:09:26 +01:00
James Turner
ed5372f439 Add new PROTECTED property attribute 2018-02-04 17:10:41 +00:00
James Turner
bc63381fb0 Packages: tweaks to thumbnail cache
Don’t discard the existing data when expiring from the cache, in case
the refresh request fails.
2018-01-31 15:00:58 +00:00
James Turner
c59f2a0749 Fix Windows _wutime compilation 2018-01-31 13:10:34 +00:00
James Turner
5c30ca5dc6 Add SGPath::touch() helper 2018-01-31 11:04:10 +00:00
Thomas Geymayer
37bc43c7aa canvas::Path: support for stroke-opacity and fill-opacity 2018-01-29 09:07:04 +01:00
Thomas Geymayer
4edd3f45b7 canvas: Refactor and protect against expired pointers 2018-01-28 15:57:14 +01:00
Thomas Geymayer
087ea4c5f3 canvas: Refactor with C++11 2018-01-28 00:49:05 +01:00
Florent Rougon
40534d6316 Remove overzealous, invasive behavior from SGBinding's destructor
Since the dawn of times (FG commit
1bcaf4bfdd38f18ac7c375dd9319935ff3df56ac, where SGBinding was called
FGBinding), SGBinding's destructor has had a strange behavior:

SGBinding::~SGBinding()
{
  if(_arg && _arg->getParent())
    _arg->getParent()->removeChild(_arg->getName(), _arg->getIndex());
}

In other words, it used to remove the passed-in <binding> node from its
parent node (if any) once the SGBinding instance got destroyed. This
behavior is very unintuitive to several people and has resulted in a few
workarounds in the FG code base just to cope with this strangeness.

This commit gives SGBinding the implictly-generated destructor,
therefore SGBinding::~SGBinding() does not attempt to remove the
property node anymore.

See disussion at:

  https://sourceforge.net/p/flightgear/mailman/flightgear-devel/thread/87d12b1h0h.fsf%40frougon.crabdance.com/#msg36190666
2018-01-27 11:03:06 +01:00
James Turner
8621925e75 Floating-point comparison helpers for SGMath<T>
Implemented using numeric_limits::epsilon and std::abs, fingers
crossed this is sufficiently portable!
2018-01-26 11:29:18 +00:00
Thomas Geymayer
5cb2360985 cppbind: Use separet Context::to_nasal_vec instead of overload 2018-01-23 16:15:56 +01:00
Thomas Geymayer
b831d2b64a cppbind: Fix for old g++ 2018-01-23 14:02:18 +01:00
Thomas Geymayer
e81597a176 cppbind: Opimize convert fixed size arrays and more type_traits 2018-01-23 09:30:00 +01:00
James Turner
7df4f32de6 Merge /u/janodesbois/simgear-jano/ branch mp-merge into next
https://sourceforge.net/p/flightgear/simgear/merge-requests/42/
2018-01-22 09:48:01 +00:00
Thomas Geymayer
db3725880e cppbind: simplify with 'auto' 2018-01-21 14:39:24 +01:00
Thomas Geymayer
af5833cbc5 cppbind: Refactor to use C++11+ type_traits 2018-01-21 13:21:55 +01:00
James Turner
9078a085c3 Kill exception if removeDelegate fails
This simplifies logic when changing the package::Root
2018-01-20 16:19:13 +00:00
jean pellotier
396a7d7f09 a system clock from simgear, using std::chrono libs
this is to use some kind of utc time for timestamps in mp protocol,
to improve the lag correction system for mp planes, using a
"real time" mode when possible.
2018-01-20 09:15:56 +01:00
Thomas Geymayer
8a128a57cd cppbind: forward declarations 2018-01-19 10:34:37 +01:00
Thomas Geymayer
3dca9b8a68 cppbind: Make Mac and Windows happy and add forward declaration 2018-01-19 10:24:33 +01:00
Thomas Geymayer
5f8f5a1c33 cppbind: More refactoring and finally getting rid of boost.preprocessor 2018-01-19 09:12:11 +01:00
Thomas Geymayer
5a72a7d9f4 canvas: Refactor Path::addSegment with std::initializer_list 2018-01-16 09:01:55 +01:00
James Turner
51a91bfa8a Ignore CmakeList.txt.user files 2018-01-12 22:19:54 +00:00
Thomas Geymayer
4cedd0a346 cppbind: refactor Ghost::method to use variadic templates 2018-01-12 08:32:57 +01:00
Thomas Geymayer
b989f4085d Various doxygen improvements/fixes. 2018-01-12 08:32:04 +01:00
Thomas Geymayer
19cc797c41 cppbind: fix exporting integer_sequence and small codingstyle fixes 2018-01-12 07:57:17 +01:00
Thomas Geymayer
99ee3d9dec cppbind: nasal::Context refactoring and doxygen improvements. 2018-01-09 22:50:29 +01:00
Florent Rougon
455753c774 Simplify code in NetChannelPoller::removeChannel()
Remove an apparently bogus portability workaround (which was presumably
targetting one of the bugs fixed in the previous commit [1]) and further
simplify the code using std::find().

[1] da099d4312/
2018-01-08 10:26:18 +01:00
James Turner
da099d4312 Correct use of remove_if in the test-server
Should fix erratic crashes of some tests.
2018-01-07 16:10:50 +00:00
xDraconian
a88b9181b6 Enable the test 'test_precipitation' 2018-01-04 08:18:43 +01:00
xDraconian
7f7c632914 Simgear::test_precipitation test coverage
Lines:     0.0% >> 92.3%
Functions: 0.0% >> 97.1%

Memory leaks resolved - reclaiming 169kb in 126 blocks
No memory leaks remain
2018-01-03 12:40:49 +00:00
Alessandro Menti
e7356223bb CMakeLists.txt: explicitly enable the CMP0067 (C/C++ standard flags) policy
Explicitly enable the CMP0067 policy to have try_compile use the correct
C/C++ standard flags; otherwise, CMake will default to not honoring those,
causing the C/C++ checks to be compiled with no standard flags and SimGear
to be compiled with them. This causes errors if we try to detect a new
prototype which is only present in C++14 and above).
2018-01-03 11:54:04 +01:00
Florent Rougon
7547ad0391 Improve formatting of the HAVE_WORKING_STD_REGEX test
Indent the test code in a nicer way and shorten it, taking advantage of
this guarantee from the C++ standard:

  If control reaches the end of main without encountering a return
  statement, the effect is that of executing return 0.

-> no need for "#include <cstdlib>" nor for "return EXIT_SUCCESS".
2018-01-03 00:34:52 +01:00
Thomas Geymayer
ca30d6bb3d Add missing simgear_config.h include
Required for HAVE_STD_INDEX_SEQUENCE to be properly set.
2018-01-02 11:37:46 +01:00
Thomas Geymayer
ebd2cdb7a7 SGPickAnimation: fix member initialization order 2018-01-02 09:13:14 +01:00
Thomas Geymayer
52b8f60953 Add std::index_sequence for C++11
Will be required for nasal::Ghost
2018-01-02 08:00:49 +01:00
Florent Rougon
ab1e2d49ab Add a CMake check for a working <regex> implementation
Define HAVE_WORKING_STD_REGEX if, and only if <regex> is usable.

Normally, <regex> should be available and working in any compliant C++11
implementation, however at least g++ 4.8[1] lies about its C++11
compliance: its <regex> is utterly unusable, see [2] and [3] for
details.

[1] Which appears to be (precisely 4.8.5) the version shipped in
    CentOS 7, and used on FlightGear's current Jenkins installation.

[2] https://stackoverflow.com/a/12665408/4756009

[3] https://sourceforge.net/p/flightgear/mailman/message/36170781/
2017-12-29 13:46:42 +01:00
xDraconian
401bd1c643 Simgear::test_bucket code coverage
Lines:     82.1% >>  98.7%
Functions: 83.0% >> 100.0%

No memory leaks
2017-12-23 08:52:23 +00:00
xDraconian
53e6bec425 Tests: resolve several memory leaks 2017-12-17 10:06:20 +01:00
James Turner
f6ded69fa3 Untar failure path fixes
By xDraconian, ensure we call inflateEnd in failure cases, otherwise
the inflate context is leaked.
2017-12-11 18:14:40 +00:00
Thomas Geymayer
68053d64b5 function_list<>: Simplify with C++11 2017-12-04 08:47:51 +01:00
Richard Harrison
1f12966628 Add condition to knob animation.
This will prevent both input from being received and the position being updated by property change.
2017-12-04 07:53:39 +01:00
Richard Harrison
8d3723c91f Prevent nullptr exception in treenodes.
Happened to me once or twice mainly during reposition probably as scenery changed whilst still loading.
2017-12-04 07:33:45 +01:00
Richard Harrison
7dcc359110 add condition to pick animations 2017-12-04 07:31:59 +01:00
Richard Harrison
854881bba2 Prevent nullptr exception. Had this happen once. 2017-12-04 07:30:48 +01:00
Alessandro Menti
ddb7958f40 Remove old .spec files
Remove old SuSE/Red Hat packaging files (in line with what was done
in commit ef66ba in FlightGear).
2017-12-02 17:47:05 +01:00
Alessandro Menti
fe0d3fd448 Remove the dependency on the UTF-8 external library
Remove the dependency on the UTF-8 external library by writing the
UTF-8/code point conversion routines in KeyboardEvent.cxx.
2017-12-02 16:32:27 +01:00
Thomas Geymayer
15d5c878f3 canvas::NasalWidget: don't call unsafe method in destructor
Calling a virtual method in a destructor has undefined behaviour.
Furthermore passing 'this' to nasal from within the destructor
is not safe. 'onRemove' is called for widgets within layouts
on destruction anyhow, so no need to call it here again. If widgets
are arranged manually without any layout 'onRemoved' should be
called upon removing them from a dialog.
2017-12-01 08:14:44 +01:00
Thomas Geymayer
4f63c3f8a8 nasal::Object: Simplify with C++11
C++11 variadic templates finally allow us to get rid of the
ugly boost preprocessor workaround.
2017-12-01 07:32:49 +01:00
James Turner
b766ce76ff Package thumbnail caching tweaks
Resolves some issue with expired thumbnails not being reloaded
correctly.
2017-11-27 23:37:56 +00:00
ThorstenB
0b4b09958f Merge /u/thbr/simgear/ branch next into next
https://sourceforge.net/p/flightgear/simgear/merge-requests/41/
2017-11-24 21:58:52 +00:00
Florent Rougon
e12fc5a89d Use common definition of simgear::enumValue() from <simgear/sg_inlines.h>
This avoids code duplication.
2017-11-19 08:15:44 +01:00
Florent Rougon
bd3c351f1f Add function template simgear::enumValue() to simgear/sg_inlines.h
This function template is useful to get the value of a member of a
scoped enumeration, without having to hardcode its type while casting
it.
2017-11-19 00:45:47 +01:00
Florent Rougon
6064be33e5 SGPath: add comparison operators (<, >, <=, >=) and an std::hash specialization
This allows one to use SGPath in containers such as std::map,
std::unordered_map and std::unordered_set.

Like the existing == and !=, all these operators rely solely on the
UTF-8 internal representation of the path.
2017-11-19 00:45:47 +01:00
Florent Rougon
1540d6f472 SGPath: mark the str() and utf8Str() methods as 'noexcept'
We'll use this to cleanly declare std::hash<SGPath> as 'noexcept' in the
next commit, which appears to be desirable for std::hash template
specializations, according to:

  http://en.cppreference.com/w/cpp/utility/hash
2017-11-18 16:04:32 +01:00
Florent Rougon
1310092c0c SGPath: enable move operations
This is done by simply not user-defining the copy constructor,
copy-assignment operator and destructor. See [1] for more info.

[1] http://accu.org/content/conf2014/Howard_Hinnant_Accu_2014.pdf

For the benchmark below (compiled with the next commit to allow sorting
SGPath instances), on Linux amd64 with g++ 6.3.0, I observe that
enabling SGPath move operations with this commit increases the
performance by 31% or 28% respectively, depending on whether I use this:

  // Typical code that creates a data structure in several steps and
  // benefits from move operations (the std::move() does nothing when
  // running the test with move operations disabled: a copy is made).
  auto p = SGPath::fromUtf8(randomString(0, 30));
  v.emplace_back(std::move(p));

or that:

  v.emplace_back(randomString(0, 30))

for the initialization code. Now the benchmark code:

using std::string;

static std::default_random_engine randomNumbersGenerator;

// Utility function: generate a random string whose length is in the
// [minLen, maxLen] range.
string randomString(string::size_type minLen, string::size_type maxLen)
{
  std::uniform_int_distribution<string::size_type> sLenDist(minLen, maxLen);
  std::uniform_int_distribution<int> byteDist(0, 255);
  auto randomByte = std::bind(byteDist, randomNumbersGenerator);

  string::size_type len = sLenDist(randomNumbersGenerator);
  string str;

  while (str.size() < len) {
    str += std::char_traits<char>::to_char_type(randomByte());
  }

  return str;
}

// The test function, run with nbIterations = 500000, minSize = 0 and
// maxSize = 200 to obtain the figures given above.
void SGPath_perfTest(std::size_t nbIterations,
                     std::size_t minSize, std::size_t maxSize)
{
  std::uniform_int_distribution<std::size_t> sizeDist(minSize, maxSize);
  auto randomSize = std::bind(sizeDist, randomNumbersGenerator);

  std::chrono::time_point<std::chrono::system_clock> start, end;
  start = std::chrono::system_clock::now();

  vector<SGPath> v;

  for (std::size_t i=0; i < nbIterations; i++) {
    v = vector<SGPath>{};          // start anew

    for (std::size_t j=0; j < randomSize(); j++) {
      v.emplace_back(randomString(0, 30));
    }

    std::shuffle(v.begin(), v.end(), randomNumbersGenerator);
    std::sort(v.begin(), v.end());
  }

  end = std::chrono::system_clock::now();
  std::chrono::duration<double> elapsedSecs = end - start;
  std::cout << elapsedSecs.count() << "\n"; // duration in seconds
}
2017-11-18 14:54:33 +01:00
ThorstenB
e485fac8ed Provide polar earth radius as a simgear constant. 2017-11-16 21:38:50 +01:00
Florent Rougon
55d75f18de Add missing include <algorithm> for std::max() and std::min()
This include was missing in several files from the simgear/hla
directory.
2017-11-16 16:00:14 +01:00
Florent Rougon
e8648a3f71 Add missing include <algorithm> for std::max() 2017-11-16 00:42:22 +01:00
Florent Rougon
0c7cabe46f SGSharedPtr: optimized version of the free function swap()
With this simple change, the speedup as compared to commit 18f048424 is
now 37 % for the benchmark given in the previous commit. This is because
optimized swap() only needs to swap the raw pointers, which is certainly
less work than the three move assignments on SGSharedPtr (not raw
pointers) done by std::swap().

To benefit from this, write code like:

  using std::swap;  // now useless for SGSharedPtr, but idiomatic
  swap(ptr1, ptr2); // *not* std::swap()!
2017-11-14 01:31:03 +01:00
Florent Rougon
a9ec3be2fd SGSharedPtr: the move constructor and move assignment operator are now 'noexcept'
This automatically makes SGSharedPtr more efficient when used in
standard containers (among others). See below for the benchmark details.

Mark as 'noexcept' (after checking it's legitimate!) the SGSharedPtr and
SGReferenced methods required for SGSharedPtr's move constructor and
move assignment operator to be guaranteed 'noexcept'.

Benchmark
---------

I measured a 25 % speedup with g++ 6.3.0 on Linux amd64, CFLAGS=-Wall -O2
as compared to commit 18f0484249 (which is
just before my changes to SGSharedPtr.hxx) on the following test code,
called with:

  nbIterations = 3000000
  minSize      =       0
  maxSize      =     200

------------------------------------------------------------------------
static std::default_random_engine randomNumbersGenerator;

class SGReferencedTestClass : public SGReferenced
{ int i; };

void SGSharedPtr_perfTest(std::size_t nbIterations,
                          std::size_t minSize, std::size_t maxSize)
{
  using Ref = SGSharedPtr<SGReferencedTestClass>;

  std::uniform_int_distribution<std::size_t> sizeDist(minSize, maxSize);
  auto randomSize = std::bind(sizeDist, randomNumbersGenerator);

  std::chrono::time_point<std::chrono::system_clock> start, end;
  start = std::chrono::system_clock::now();

  std::vector<Ref> v;

  for (std::size_t i=0; i < nbIterations; i++) {
    v = std::vector<Ref>{};          // start anew

    for (std::size_t j=0; j < randomSize(); j++) {
      auto p = Ref(new SGReferencedTestClass());
      v.emplace_back(std::move(p));
    }

    std::shuffle(v.begin(), v.end(), randomNumbersGenerator);
    std::sort(v.begin(), v.end());
  }

  end = std::chrono::system_clock::now();
  std::chrono::duration<double> elapsedSecs = end - start;
  std::cout << elapsedSecs.count() << "\n"; // duration in seconds
}
------------------------------------------------------------------------

Basically, these gains can be explained by the fact that copying an
SGSharedPtr requires to test SGReferenced::ref, increase the refcount,
and then when the object is destroyed, test again SGReferenced::ref,
decrease the refcount and test it in order to maybe delete. With the
move constructor and move assignment operator, copying the argument is
never necessary: its raw pointer can be swapped with the one contained
in *this, which is very fast. For the move constructor, this is all that
is needed; move assignment just needs one reset() call after that in
order to release the resource from the moved-from shared pointer.
2017-11-13 23:43:43 +01:00
Florent Rougon
abaaee1af2 Add simgear::noexceptSwap() to simgear/sg_inlines.h
This is a function template that is guaranteed to be 'noexcept' as long
as compilation succeeds. Idea and implementation from
<https://akrzemi1.wordpress.com/2011/06/10/using-noexcept/>.
2017-11-13 11:59:11 +01:00
Florent Rougon
6283a515b9 Fix missing headers in simgear/io/DNSClient.cxx and simgear/props/props_test.cxx
<algorithm> is needed for std::sort() and std::find().
2017-11-13 10:04:34 +01:00
Thomas Geymayer
ce7d463710 Fix wrong argument
One more catch by Florent Rougon.
2017-11-13 08:22:14 +01:00
Florent Rougon
bc3404fcbe SGSharedPtr: more efficient copy and move assignment operators
The copy-and-swap idiom is certainly very cute, but often causes
unnecessary copies. My commit fedafb9352
did exactly that, unfortunately.

Restore the exact same code for the copy-assignment operator as before
commit fedafb935, and add a more efficient implementation for the
move-assignment operator.

As explained by Howard Hinnant in [1] and [2], if some particular piece
of code really needs a strong exception safety guarantee, one can easily
add a specific method for that; this is not a valid reason to make the
code slower for all other places that have no use for such a guarantee!

[1] http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014
[2] https://stackoverflow.com/a/9322542/4756009
2017-11-13 07:34:48 +01:00
Thomas Geymayer
bd87d3963a Fix leak in NasalObjectHolder
Thanks to Florent Rougon for spotting it.
2017-11-12 22:41:14 +01:00
Richard Harrison
fed449a801 Fix error message in axis object animation 2017-11-12 17:07:29 +01:00
Florent Rougon
19dd92d3e0 Remove or replace obsolete uses of throw()
In C++11, destructors are 'noexcept' by default -> remove useless
throw() specifiers. There was one case that wasn't about a destructor: I
replaced the 'throw()' with 'noexcept' because this use of 'throw()' is
deprecated and 'noexcept' offers the intended meaning as far as I can
guess (in C++17, 'throw()' will be equivalent to 'noexcept' anyway). For
more info, see:

  http://en.cppreference.com/w/cpp/language/noexcept_spec
  https://akrzemi1.wordpress.com/2013/08/20/noexcept-destructors/
2017-11-12 09:56:26 +01:00
Florent Rougon
f22b9ba9f1 SGSharedPtr: add unit tests
Add unit tests for move ctor and move assignment operator, as well for a
few other SGSharedPtr methods.
2017-11-11 17:55:54 +01:00
Florent Rougon
fedafb9352 SGSharedPtr: add move constructor and move assignment operator 2017-11-11 16:18:36 +01:00
Florent Rougon
18f0484249 Convert structure/shared_ptr_test.cpp to use the SG test macros (no more boost) 2017-11-11 01:29:49 +01:00
James Turner
10956056b3 Own code for UTF-32 <-> UTF-8 conversion
Avoids codecvt dependency on Unix where it might not be present, eg
with GCC 4.8; on Windows we use <codecvt> since it’s present in VS2015
to avoid writing a seperate UTF-16 <-> UTF-8 conversion.
2017-11-01 17:04:56 +00:00
Erik Hofman
e482f04123 int16_t needs cstdint 2017-10-31 11:22:39 +01:00
Erik Hofman
db89f0d4d1 Oops, use the proper project name 2017-10-31 10:49:04 +01:00
Erik Hofman
2ac97a9f1f std::wstring_convert requires locale 2017-10-31 10:47:12 +01:00
Erik Hofman
c03359a189 Update to the (now GPL) AeonWave version 3.0+ 2017-10-31 10:42:44 +01:00
Erik Hofman
84b636debc Add a frequency filter and a bitcrusher filetr 2017-10-31 10:42:16 +01:00
James Turner
2642299d77 We require C++11 now, simplify this code.
This ensures wstring to std::string conversion is always available,
needed for some HID work I’m doing on a branch,.
2017-10-29 13:20:39 +00:00
Richard Harrison
2a60e5e338 Added touch animation.
Designed for 2d objects, such as a canvas placements, this permits the receipt of touch (mouse click) events to enable the simulation of avionics with a touchscreen.

The coordinates are passed in as arguments to the action; these can be accessed with Nasal via the cmdarg() method.

example:

    <animation>
         <type>touch</type>
         <visible>true</visible>
         <object-name>VSDImage</object-name>
         <action>
             <touch>0</touch>
             <repeatable>false</repeatable>
             <binding>
                 <command>nasal</command>
                 <script>print("touch input
(",cmdarg().getNode("x").getValue(),",",cmdarg().getNode("y").getValue())</script>
             </binding>
         </action>
     </animation>
2017-10-29 01:04:36 +02:00
Florent Rougon
880c063d04 Remove useless readdir() calls in Dir::isEmpty()
simgear::Dir::isEmpty() used to make up to 5 calls to readdir(), while 3
are enough to say whether the directory has entries other than '.' and
'..'.

Also add an automated test for this method.
2017-10-27 20:49:17 +02:00
Florent Rougon
7a374c43dc Fix CMake test for std::isnan()
It used the wrong header; std::isnan() is defined in <cmath>.
2017-10-23 19:29:07 +02:00
Richard Harrison
7be1fcc32e Add method to copy entire properry subtree 2017-10-15 16:54:36 +02:00
Stuart Buchanan
b57dca66be Handle case where no tranform matrix exists. 2017-10-13 17:35:21 +01:00
Florent Rougon
d455f5f445 props_io.cxx: use SG_ORIGIN when throwing exceptions
This will provide more accurate info than the fixed string "SimGear
Property Reader".
2017-10-10 20:15:54 +02:00
Florent Rougon
2200fad30e Rename the ResourceProxy class to EmbeddedResourceProxy
This is done so as to avoid confusion with the unrelated classes
ResourceProvider and ResourceManager already present in SimGear.

Despite this new name, EmbeddedResourceProxy is a proxy not only for
embedded resources, but also for real files (hence the initial name
choice): its purpose is precisely to allow zero-work switching from one
data source to the other.
2017-10-07 16:36:49 +02:00
Florent Rougon
e5e112c3c2 Add a ResourceProxy class
The ResourceProxy class allows one to access real files or embedded
resources in a unified way. When using it, one can switch from one data
source to the other with minimal code changes, possibly even at runtime
(in which case there is obviously no code change at all).

Sample usage (from FlightGear for the globals->get_fg_root() bit):

  simgear::ResourceProxy proxy(globals->get_fg_root(), "/FGData");
  std::string s = proxy.getString("/some/path");
  std::unique_ptr<std::istream> streamp = proxy.getIStream("/some/path");

The methods ResourceProxy::getString(const std::string& path) and
ResourceProxy::getIStream(const std::string& path) decide whether to use
embedded resources or real files depending on the boolean value passed
to ResourceProxy::setUseEmbeddedResources() (also available as an
optional parameter to the ResourceProxy constructor, defaulting to
true). It is often most convenient to set this boolean once and don't
worry about it anymore---it's stored inside the ResourceProxy object.
Otherwise, if you want to fetch resources some times from real files,
other times from embedded resources, you may use the following methods:

  // Retrieve contents using embedded resources
  std:string s = proxy.getString("/some/path", true);
  std:string s = proxy.getStringDecideOnPrefix(":/some/path");

  // Retrieve contents using real files
  std:string s = proxy.getString("/some/path", false);
  std:string s = proxy.getStringDecideOnPrefix("/some/path");

(alternatively, you could use several ResourceProxy objects with
different values for the constructor's third parameter)
2017-10-01 07:54:17 +02:00
James Turner
dd3cdf63e6 Terrasync: Fix exception deleting orphaned directories. 2017-09-29 12:47:42 +01:00
Stuart Buchanan
a800189c25 Add support for Slippy Map Canvas layers.
- Add so-called Web Mercator projection and ability to choose
  projection for a Canvas Map.
- Add supporting functions for Slippy Maps (i.e. OSM)
2017-09-28 15:36:48 +01:00
James Turner
b342245619 Fix lib64 mode on Ubuntu 17.04 / CMake 3.7 2017-09-28 15:11:09 +01:00
James Turner
6cd5ac2d0d Move readTime (parse_time) from options.cxx 2017-09-27 15:06:37 +01:00
James Turner
872a48dacb Extend the state-machine parsing test slightly 2017-09-27 14:33:57 +01:00
James Turner
721aa544c9 Use C++11 in the state-machine code 2017-09-26 16:39:04 +02:00
Richard Harrison
7bfbda7188 Fix for AMD / Radeon rendering of lights as point sprites.
Tested against R9-290 and GTX 550 Ti (thanks Nikolai V. Chr.)
2017-09-24 22:10:44 +02:00
James Turner
602244979b Tar extractor can filter file paths. 2017-09-21 17:47:28 +01:00
Torsten Dreyer
8e29cae309 new version: 2017.4.0 2017-09-17 12:14:00 +02:00
148 changed files with 4807 additions and 5259 deletions

1
.gitignore vendored
View File

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

View File

@@ -2,8 +2,6 @@ if (NOT SYSTEM_EXPAT)
add_subdirectory(expat)
endif()
add_subdirectory(utf8)
if (ENABLE_DNS AND NOT SYSTEM_UDNS)
add_subdirectory(udns)
endif()

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,6 +7,9 @@ if(COMMAND cmake_policy)
if(POLICY CMP0042)
cmake_policy(SET CMP0042 NEW)
endif()
if(POLICY CMP0067)
cmake_policy(SET CMP0067 NEW)
endif()
endif()
@@ -49,7 +52,7 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7")
# add a dependency on the versino file
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS version)
set(FIND_LIBRARY_USE_LIB64_PATHS ON)
set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS TRUE)
# use simgear version also as the SO version (if building SOs)
SET(SIMGEAR_SOVERSION ${SIMGEAR_VERSION})
@@ -135,6 +138,16 @@ include (DetectArch)
# keep the compatability link option as the default
option(OSG_FSTREAM_EXPORT_FIXED "Set to ON if the osgDB fstream export patch is applied" OFF)
if (CMAKE_COMPILER_IS_GNUCXX OR CLANG)
if (CMAKE_VERSION VERSION_LESS 3.1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++${CMAKE_CXX_STANDARD}")
elseif (CMAKE_VERSION VERSION_LESS 3.8)
# policy CMP0067 (try_compile does not honor CMAKE_CXX_STANDARD)
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++${CMAKE_CXX_STANDARD}")
endif()
endif()
if (MSVC)
GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} PATH)
if (CMAKE_CL_64)
@@ -343,7 +356,7 @@ SET(CMAKE_MINSIZEREL_POSTFIX "" CACHE STRING "add a postfix, usually empty on wi
# isnan might not be real symbol, so can't check using function_exists
check_cxx_source_compiles(
"#include <cstdlib>
"#include <cmath>
int main() { return std::isnan(0.0);} "
HAVE_STD_ISNAN)
@@ -351,14 +364,35 @@ if (NOT ${HAVE_STD_ISNAN})
message(FATAL_ERROR "Your compiler lacks C++11 std::isnan, please update it")
endif()
# Check if the <regex> implementation in the C++ standard library is usable.
# This is necessary because g++ 4.8 lies about its C++11 compliance: its
# <regex> is utterly unusable, cf. [1].
# The big preprocessor test essentially comes from [2], and gcc upstream devs
# appear to back it (see comments following [2], as well as [3]).
#
# [1] https://stackoverflow.com/a/12665408/4756009
# [2] https://stackoverflow.com/a/41186162/4756009
# [3] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78905
check_cxx_source_compiles(
"#include <regex>
int main() {
#if __cplusplus >= 201103L && \
(!defined(__GLIBCXX__) || \
(__cplusplus >= 201402L) || \
defined(_GLIBCXX_REGEX_DFS_QUANTIFIERS_LIMIT) || \
defined(_GLIBCXX_REGEX_STATE_LIMIT) || \
(defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE > 4))
#else
nullptr = void; // intentionally trigger a compilation error
#endif
}"
HAVE_WORKING_STD_REGEX)
if(CMAKE_COMPILER_IS_GNUCXX)
set(WARNING_FLAGS_CXX "-Wall -fPIC")
set(WARNING_FLAGS_C "-Wall -fPIC")
if (CMAKE_VERSION VERSION_LESS 3.1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif()
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.4)
message(WARNING "GCC 4.4 will be required soon, please upgrade")
endif()
@@ -386,10 +420,6 @@ if (CLANG)
# fix Boost compilation :(
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
if (CMAKE_VERSION VERSION_LESS 3.1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif()
if(ENABLE_SIMD)
if (X86 OR X86_64)
set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse")
@@ -462,6 +492,8 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS_C} ${MSVC_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS_CXX} ${MSVC_FLAGS} ${BOOST_CXX_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MSVC_LD_FLAGS}")
include(CheckCXXFeatures)
# use BEFORE to ensure local directories are used first,
# ahead of system-installed libs
include_directories(BEFORE ${PROJECT_BINARY_DIR}/simgear)

View File

@@ -0,0 +1,30 @@
check_cxx_source_compiles("
#include <utility>
#include <type_traits>
std::make_index_sequence<0> t;
int main() {}" HAVE_STD_INDEX_SEQUENCE
)
check_cxx_source_compiles("
#include <type_traits>
std::remove_cv_t<const int> t;
int main() {}" HAVE_STD_REMOVE_CV_T
)
check_cxx_source_compiles("
#include <type_traits>
std::remove_cvref_t<const int&> t;
int main() {}" HAVE_STD_REMOVE_CVREF_T
)
check_cxx_source_compiles("
#include <type_traits>
std::enable_if_t<true, int> t;
int main() {}" HAVE_STD_ENABLE_IF_T
)
check_cxx_source_compiles("
#include <type_traits>
std::bool_constant<true> t;
int main() {}" HAVE_STD_BOOL_CONSTANT
)

View File

@@ -10,12 +10,13 @@
#
# Created by Erik Hofman.
FIND_PATH(AAX_INCLUDE_DIR aax/aeonwave.hpp
FIND_PATH(AAX_INCLUDE_DIR aax/aax.h
HINTS
$ENV{AAXDIR}
$ENV{ProgramFiles}/aax
$ENV{ProgramFiles}/AeonWave
$ENV{ProgramFiles}/Adalin/AeonWave
${CMAKE_SOURCE_DIR}/aax
PATH_SUFFIXES include
PATHS
~/Library/Frameworks
@@ -26,23 +27,35 @@ FIND_PATH(AAX_INCLUDE_DIR aax/aeonwave.hpp
)
FIND_LIBRARY(AAX_LIBRARY
NAMES AAX aax AAX32 libAAX32
NAMES AAX aax AAX32
HINTS
$ENV{AAXDIR}
$ENV{ProgramFiles}/AAX
$ENV{ProgramFiles}/AeonWave
$ENV{ProgramFiles}/Adalin/AeonWave
${CMAKE_BUILD_DIR}/aax
PATH_SUFFIXES bin lib lib/${CMAKE_LIBRARY_ARCHITECTURE} lib64 libs64 libs libs/Win32 libs/Win64
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/opt
/usr/local
)
SET(AAX_FOUND "NO")
IF(AAX_LIBRARY AND AAX_INCLUDE_DIR)
SET(AAX_FOUND "YES")
ELSE(AAX_LIBRARY AND AAX_INCLUDE_DIR)
IF(NOT AAX_INCLUDE_DIR)
MESSAGE(FATAL_ERROR "Unable to find the AAX library development files.")
SET(AAX_FOUND "NO")
ENDIF(NOT AAX_INCLUDE_DIR)
IF(NOT AAX_LIBRARY)
IF(SINGLE_PACKAGE)
SET(AAX_LIBRARY "${aax_BUILD_DIR}/aax/AAX32.dll")
SET(AAX_FOUND "YES")
ELSE(SINGLE_PACKAGE)
ENDIF(SINGLE_PACKAGE)
ENDIF(NOT AAX_LIBRARY)
ENDIF(AAX_LIBRARY AND AAX_INCLUDE_DIR)

View File

@@ -2,6 +2,10 @@
documentation. It has a .cxx extension so that emacs will happily
autoindent correctly. */
/**
* \namespace simgear
* \brief \ref index "SimGear" main namespace.
*/
/** \mainpage SimGear
* Simulation, Visualization, and Game development libraries.

View File

@@ -7,6 +7,7 @@ find_dependency(Threads)
set(SIMGEAR_HEADLESS @SIMGEAR_HEADLESS@)
set(SIMGEAR_SOUND @ENABLE_SOUND@)
set(USE_AEONWAVE @USE_AEONWAVE@)
# OpenAL isn't a public dependency, so maybe not needed
#if (SIMGEAR_SOUND)

View File

@@ -1,13 +0,0 @@
Building a SimGear RPM package for Red Hat
Please see the "package/openSUSE" directory for an
example how to build a SimGear RPM package with
shared SimGear libraries.
You may need to adapt the names (exact spelling) of some
of the package dependencies in the openSUSE RPM spec,
since these may slightly differ for Red Hat.
(If you have a working and tested Red Hat RPM spec,
you're welcome to contribute it to this project.)

View File

@@ -1,23 +0,0 @@
Building a SimGear RPM package for openSUSE
(Last tested with openSUSE 11.4+12.1)
This directory contains the files which, along with
the source code tar files, can be used to build
an RPM package targeted at an openSUSE Linux system.
To build SimGear from source do the following:
1. obtain simgear-2.8.0.tar.bz2 (adapt version if
necessary) and copy it into ~/rpmbuild/SOURCES
2. look in the BuildRequires section of SimGear.spec
and check that all the packages referred to are
installed (note, some of these packages may be part
of openSUSE's "games" repository).
3. run 'rpmbuild -ba simgear.spec' and find the RPM
build result in ~/rpmbuild/RPMS
That's all!

View File

@@ -1,63 +0,0 @@
Summary: Simulator Construction Gear
Name: SimGear
Version: 2.8.0
Release: 1
License: LGPL
URL: http://www.flightgear.org
Group: Amusements/Games/3D/Simulation
Source: http://mirrors.ibiblio.org/pub/mirrors/flightgear/ftp/Source/simgear-%{version}.tar.bz2
BuildRoot: %{_tmppath}/%{name}-%{version}-build
BuildRequires: gcc, gcc-c++, cmake
BuildRequires: libopenal1-soft, openal-soft
BuildRequires: libOpenSceneGraph-devel >= 3.0
BuildRequires: zlib, zlib-devel
BuildRequires: libjpeg62, libjpeg62-devel
BuildRequires: boost-devel >= 1.37
BuildRequires: subversion-devel, libapr1-devel
Requires: OpenSceneGraph-plugins >= 3.0
%description
This package contains a tools and libraries useful for constructing
simulation and visualization applications such as FlightGear or TerraGear.
%package devel
Group: Development/Libraries/Other
Summary: Development header files for SimGear
Requires: SimGear = %{version}
%description devel
Development headers and libraries for building applications against SimGear.
%prep
%setup -T -q -n simgear-%{version} -b 0
%build
export CFLAGS="$RPM_OPT_FLAGS"
export CXXFLAGS="$RPM_OPT_FLAGS"
# build SHARED simgear libraries
cmake -DCMAKE_INSTALL_PREFIX=%{_prefix} -DSIMGEAR_SHARED:BOOL=ON -DENABLE_TESTS:BOOL=OFF -DJPEG_FACTORY:BOOL=ON
make %{?_smp_mflags}
%install
make DESTDIR=%{buildroot} install
%post -p /sbin/ldconfig
%postun -p /sbin/ldconfig
%files
%defattr (-, root, root, -)
%doc AUTHORS COPYING ChangeLog NEWS README
%{_libdir}/libSimGear*.so.*
%files devel
%defattr(-,root,root,-)
%dir %{_includedir}/simgear
%{_includedir}/simgear/*
%{_libdir}/libSimGear*.so
%changelog
* Mon Jul 02 2012 thorstenb@flightgear.org
- Initial version

View File

@@ -15,6 +15,7 @@ foreach( mylibfolder
nasal/cppbind
props
serial
std
structure
threads
timing

View File

@@ -53,7 +53,7 @@ void testBasic()
SG_CHECK_EQUAL(b1.gen_index(), 3040320);
SG_CHECK_EQUAL(b1.gen_base_path(), "e000n50/e005n55");
SG_VERIFY(b1.isValid());
SGBucket b2(-10.1, -43.8);
SG_CHECK_EQUAL(b2.get_chunk_lon(), -11);
SG_CHECK_EQUAL(b2.get_chunk_lat(), -44);
@@ -62,7 +62,7 @@ void testBasic()
SG_CHECK_EQUAL(b2.get_y(), 1);
SG_CHECK_EQUAL(b2.gen_base_path(), "w020s50/w011s44");
SG_VERIFY(b2.isValid());
SGBucket b3(123.48, 9.01);
SG_CHECK_EQUAL(b3.get_chunk_lon(), 123);
SG_CHECK_EQUAL(b3.get_chunk_lat(), 9);
@@ -70,10 +70,10 @@ void testBasic()
SG_CHECK_EQUAL(b3.get_y(), 0);
SG_CHECK_EQUAL(b3.gen_base_path(), "e120n00/e123n09");
SG_VERIFY(b3.isValid());
SGBucket defBuck;
SG_VERIFY(!defBuck.isValid());
b3.make_bad();
SG_VERIFY(!b3.isValid());
@@ -87,19 +87,47 @@ void testBasic()
SG_CHECK_EQUAL(atAntiMeridian2.get_chunk_lon(), -180);
SG_CHECK_EQUAL(atAntiMeridian2.get_x(), 0);
// check comparisom operator overload
// check comparison operator overload
SGBucket b4(5.11, 55.1);
SG_VERIFY(b1 == b4); // should be equal
SG_VERIFY(b1 == b1);
SG_VERIFY(b1 != defBuck);
SG_VERIFY(b1 != b2);
// check wrapping/clipping of inputs
// check wrapping/clipping of inputs
SGBucket wrapMeridian(-200.0, 45.0);
SG_CHECK_EQUAL(wrapMeridian.get_chunk_lon(), 160);
SGBucket clipPole(48.9, 91);
SG_CHECK_EQUAL(clipPole.get_chunk_lat(), 89);
// test override of a bucket's geod
auto geod = SGGeod::fromDegFt(-86.678, 36.1248, 599.0);
#ifndef NO_DEPRECATED_API
SGBucket bna_airport;
bna_airport.set_bucket(geod);
#else
SGBucket bna_airport(geod);
#endif
SG_VERIFY(bna_airport.isValid());
SG_CHECK_EQUAL(bna_airport.get_chunk_lon(), -87); // left origin of the 1-degree chunk
SG_CHECK_EQUAL(bna_airport.get_chunk_lat(), 36); // bottom origin of the 1-degree chunk
SG_CHECK_EQUAL(bna_airport.get_x(), 1); // buckets are 0.25 deg wide at the W87 parallel
// we're 0.322 deg from the origin (second bucket)
SG_CHECK_EQUAL(bna_airport.get_y(), 0); // buckets are always 0.125 deg tall
// we're 0.1248 deg from the origin (first bucket)
SG_CHECK_EQUAL(bna_airport.gen_base_path(), "w090n30/w087n36");
SG_CHECK_EQUAL_EP2(bna_airport.get_width_m(), 22479.1, 0.1);
SG_CHECK_EQUAL_EP2(bna_airport.get_height_m(), 13914.9, 0.1);
SG_CHECK_EQUAL(bna_airport.gen_index_str(), "1531777"); // 0x175F81 = b01011101|01111110|000|001
// = 93-180 | 126-90 | 0 | 1
// = -87 | 36 | 0 | 1
// test stream output
cout << "[TEST] BNA Airport: " << bna_airport << endl;
auto center = bna_airport.get_center();
cout << "[TEST] BNA lon: " << center.getLongitudeDeg() << endl;
cout << "[TEST] BNA lat: " << center.getLatitudeDeg() << endl;
}
void testPolar()
@@ -110,17 +138,17 @@ void testPolar()
SG_CHECK_EQUAL(b1.get_chunk_lon(), 0);
SG_CHECK_EQUAL(b1.get_x(), 0);
SG_CHECK_EQUAL(b1.get_y(), 7);
SG_CHECK_EQUAL_EP(b1.get_highest_lat(), 90.0);
SG_CHECK_EQUAL_EP(b1.get_width_m(), 10.0);
SG_CHECK_EQUAL(b2.get_chunk_lat(), 89);
SG_CHECK_EQUAL(b2.get_chunk_lon(), 0);
SG_CHECK_EQUAL(b2.get_x(), 0);
SG_CHECK_EQUAL(b2.get_y(), 7);
SG_CHECK_EQUAL(b1.gen_index(), b2.gen_index());
SGGeod actualNorthPole1 = b1.get_corner(2);
SGGeod actualNorthPole2 = b1.get_corner(3);
SG_CHECK_EQUAL_EP(actualNorthPole1.getLatitudeDeg(), 90.0);
@@ -131,11 +159,11 @@ void testPolar()
SGBucket b3(-2, 89.88);
SGBucket b4(-7, 89.88);
SG_CHECK_EQUAL(b3.gen_index(), b4.gen_index());
// south pole
SGBucket b5(-170, -89.88);
SGBucket b6(-179, -89.88);
SG_CHECK_EQUAL(b5.get_chunk_lat(), -90);
SG_CHECK_EQUAL(b5.get_chunk_lon(), -180);
SG_CHECK_EQUAL(b5.get_x(), 0);
@@ -143,17 +171,16 @@ void testPolar()
SG_CHECK_EQUAL(b5.gen_index(), b6.gen_index());
SG_CHECK_EQUAL_EP(b5.get_highest_lat(), -90.0);
SG_CHECK_EQUAL_EP(b5.get_width_m(), 10.0);
SGGeod actualSouthPole1 = b5.get_corner(0);
SGGeod actualSouthPole2 = b5.get_corner(1);
SG_CHECK_EQUAL_EP(actualSouthPole1.getLatitudeDeg(), -90.0);
SG_CHECK_EQUAL_EP(actualSouthPole1.getLongitudeDeg(), -180);
SG_CHECK_EQUAL_EP(actualSouthPole2.getLatitudeDeg(), -90.0);
SG_CHECK_EQUAL_EP(actualSouthPole2.getLongitudeDeg(), -168);
SGBucket b7(200, 89.88);
SG_CHECK_EQUAL(b7.get_chunk_lon(), -168);
}
// test the tiles just below the pole (between 86 & 89 degrees N/S)
@@ -167,7 +194,7 @@ void testNearPolar()
SGBucket b3(176.1, 88.5);
SG_CHECK_EQUAL(b3.get_chunk_lon(), 176);
SGBucket b4(-178, 88.5);
SG_CHECK_EQUAL(b4.get_chunk_lon(), -180);
}
@@ -181,7 +208,7 @@ void testOffset()
SG_CHECK_EQUAL(b1.get_chunk_lon(), -60);
SG_CHECK_EQUAL(b1.get_x(), 1);
SG_CHECK_EQUAL(b1.get_y(), 7);
// offset vertically
SGBucket b2(b1.sibling(0, 1));
SG_CHECK_EQUAL(b2.get_chunk_lat(), 22);
@@ -190,7 +217,7 @@ void testOffset()
SG_CHECK_EQUAL(b2.get_y(), 0);
SG_CHECK_EQUAL(b2.gen_index(), sgBucketOffset(-59.8, 21.9, 0, 1));
// offset vertically and horizontally. We compute horizontal (x)
// movement at the target latitude, so this should move 0.25 * -3 degrees,
// NOT 0.125 * -3 degrees.
@@ -199,7 +226,7 @@ void testOffset()
SG_CHECK_EQUAL(b3.get_chunk_lon(), -61);
SG_CHECK_EQUAL(b3.get_x(), 1);
SG_CHECK_EQUAL(b3.get_y(), 0);
SG_CHECK_EQUAL(b3.gen_index(), sgBucketOffset(-59.8, 21.9, -3, 1));
}
@@ -210,40 +237,40 @@ void testPolarOffset()
SG_CHECK_EQUAL(b1.get_chunk_lon(), -12);
SG_CHECK_EQUAL(b1.get_x(), 0);
SG_CHECK_EQUAL(b1.get_y(), 3);
// offset horizontally
SGBucket b2(b1.sibling(-2, 0));
SG_CHECK_EQUAL(b2.get_chunk_lat(), -90);
SG_CHECK_EQUAL(b2.get_chunk_lon(), -36);
SG_CHECK_EQUAL(b2.get_x(), 0);
SG_CHECK_EQUAL(b2.get_y(), 3);
SG_CHECK_EQUAL(b2.gen_index(), sgBucketOffset(-11.7, -89.6, -2, 0));
// offset and wrap
// offset and wrap
SGBucket b3(-170, 89.1);
SGBucket b4(b3.sibling(-1, 0));
SG_CHECK_EQUAL(b4.get_chunk_lat(), 89);
SG_CHECK_EQUAL(b4.get_chunk_lon(), 168);
SG_CHECK_EQUAL(b4.get_x(), 0);
SG_CHECK_EQUAL(b4.get_y(), 0);
SG_CHECK_EQUAL(b4.gen_index(), sgBucketOffset(-170, 89.1, -1, 0));
SGBucket b5(177, 87.3);
SGBucket b6(b5.sibling(1, 1));
SG_CHECK_EQUAL(b6.get_chunk_lat(), 87);
SG_CHECK_EQUAL(b6.get_chunk_lon(), -180);
SG_CHECK_EQUAL(b6.get_x(), 0);
SG_CHECK_EQUAL(b6.get_y(), 3);
SG_CHECK_EQUAL(b6.gen_index(), sgBucketOffset(177, 87.3, 1, 1));
// offset vertically towards the pole
SGBucket b7(b1.sibling(0, -5));
SG_VERIFY(!b7.isValid());
SG_VERIFY(!SGBucket(0, 90).sibling(0, 1).isValid());
}
@@ -256,29 +283,80 @@ void testOffsetWrap()
SG_CHECK_EQUAL(b1.get_chunk_lon(), -180);
SG_CHECK_EQUAL(b1.get_x(), 1);
SG_CHECK_EQUAL(b1.get_y(), 6);
SGBucket b2(b1.sibling(-2, 0));
SG_CHECK_EQUAL(b2.get_chunk_lat(), 16);
SG_CHECK_EQUAL(b2.get_chunk_lon(), 179);
SG_CHECK_EQUAL(b2.get_x(), 7);
SG_CHECK_EQUAL(b2.get_y(), 6);
SG_CHECK_EQUAL(b2.gen_index(), sgBucketOffset(-179.8, 16.8, -2, 0));
}
void testSiblings()
{
SGBucket bna_airport(-86.678, 36.1248);
SG_VERIFY(bna_airport.isValid());
// retrieve the sibling two positions north-east of my position
auto sib1 = bna_airport.sibling(2, 2);
SG_CHECK_EQUAL(sib1.get_chunk_lon(), bna_airport.get_chunk_lon());
SG_CHECK_EQUAL(sib1.get_chunk_lat(), bna_airport.get_chunk_lat());
SG_CHECK_EQUAL(sib1.get_x(), 3); // my x-pos (1) + 2 = 3
SG_CHECK_EQUAL(sib1.get_y(), 2); // my y-pos (0) + 2 = 2
SG_CHECK_EQUAL(sib1.gen_base_path(), bna_airport.gen_base_path());
// retrieve the one sibling two positions to the north-east
std::vector<SGBucket> siblings;
bna_airport.siblings(2, 2, siblings);
SG_CHECK_EQUAL(siblings.size(), static_cast<std::vector<SGBucket>::size_type>(1));
siblings.clear();
// retrieve the one sibling at the chunk origin of sib1
sib1.siblings(-2, -2, siblings);
SG_CHECK_EQUAL(siblings.size(), static_cast<std::vector<SGBucket>::size_type>(1));
siblings.clear();
// calculate delta between two buckets
int dx = 0;
int dy = 0;
sgBucketDiff(bna_airport, sib1, &dx, &dy);
SG_CHECK_EQUAL(dx, 2);
SG_CHECK_EQUAL(dy, 2);
// retrieve all siblings between two geodetic locations
auto geod_bna = SGGeod::fromDegFt(-86.678, 36.1248, 599.0);
auto geod_m54 = SGGeod::fromDegFt(-86.317, 36.1908, 122.0);
sgGetBuckets(geod_bna, geod_m54, siblings);
SG_CHECK_EQUAL(siblings.size(), static_cast<std::vector<SGBucket>::size_type>(4));
siblings.clear();
// edge cases
// ensure you cannot retrieve the sibling of an invalid bucket
SGBucket bad;
auto bad_sib = bad.sibling(1, 1);
SG_CHECK_EQUAL(bad_sib.get_chunk_lon(), -1000);
SG_CHECK_EQUAL(bad.siblings(2, 2, siblings), 0);
// if we drop below the 22nd parallel, the bucket widths are half the size
// expect this to retrieve two buckets
bna_airport.siblings(0, -160, siblings);
SG_CHECK_EQUAL(siblings.size(), static_cast<std::vector<SGBucket>::size_type>(2));
siblings.clear();
}
int main(int argc, char* argv[])
{
testBucketSpans();
testBasic();
testPolar();
testNearPolar();
testOffset();
testOffsetWrap();
testPolarOffset();
testSiblings();
cout << "all tests passed OK" << endl;
return 0; // passed
}

View File

@@ -22,8 +22,10 @@
#include "CanvasEventManager.hxx"
#include "CanvasEventVisitor.hxx"
#include "CanvasPlacement.hxx"
#include <simgear/canvas/events/KeyboardEvent.hxx>
#include <simgear/canvas/events/MouseEvent.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/scene/util/parse_color.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
@@ -32,9 +34,6 @@
#include <osgText/Text>
#include <osgViewer/Viewer>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/foreach.hpp>
namespace simgear
{
namespace canvas
@@ -64,18 +63,9 @@ namespace canvas
//----------------------------------------------------------------------------
Canvas::Canvas(SGPropertyNode* node):
PropertyBasedElement(node),
_canvas_mgr(0),
_event_manager(new EventManager),
_size_x(-1),
_size_y(-1),
_view_width(-1),
_view_height(-1),
_status(node, "status"),
_status_msg(node, "status-msg"),
_sampling_dirty(false),
_render_dirty(true),
_visible(true),
_render_always(false)
_status_msg(node, "status-msg")
{
_status = 0;
setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
@@ -236,6 +226,10 @@ namespace canvas
if( _status & STATUS_DIRTY )
{
// Retrieve reference here, to ensure the scene group is not deleted while
// creating the new texture and camera
osg::ref_ptr<osg::Group> root_scene_group = _root_group->getSceneGroup();
_texture.setSize(_size_x, _size_y);
if( !_texture.serviceable() )
@@ -261,7 +255,7 @@ namespace canvas
parseColor(_node->getStringValue("background"), clear_color);
camera->setClearColor(clear_color);
camera->addChild(_root_group->getMatrixTransform());
camera->addChild(root_scene_group);
if( _texture.serviceable() )
{
@@ -281,7 +275,7 @@ namespace canvas
if( _visible || _render_always )
{
BOOST_FOREACH(CanvasWeakPtr canvas_weak, _child_canvases)
for(auto& canvas_weak: _child_canvases)
{
// TODO should we check if the image the child canvas is displayed
// within is really visible?
@@ -293,7 +287,7 @@ namespace canvas
if( _render_dirty )
{
// Also mark all canvases this canvas is displayed within as dirty
BOOST_FOREACH(CanvasWeakPtr canvas_weak, _parent_canvases)
for(auto& canvas_weak: _parent_canvases)
{
CanvasPtr canvas = canvas_weak.lock();
if( canvas )
@@ -522,8 +516,8 @@ namespace canvas
{
const std::string& name = node->getNameString();
if( boost::starts_with(name, "status")
|| boost::starts_with(name, "data-") )
if( strutils::starts_with(name, "status")
|| strutils::starts_with(name, "data-") )
return;
_render_dirty = true;
@@ -538,7 +532,7 @@ namespace canvas
if( !placements.empty() )
{
bool placement_dirty = false;
BOOST_FOREACH(PlacementPtr& placement, placements)
for(auto& placement: placements)
{
// check if change can be directly handled by placement
if( placement->getProps() == node->getParent()

View File

@@ -33,7 +33,7 @@
#include <osg/NodeCallback>
#include <osg/observer_ptr>
#include <boost/scoped_ptr.hpp>
#include <memory>
#include <string>
namespace simgear
@@ -211,21 +211,21 @@ namespace canvas
protected:
CanvasMgr *_canvas_mgr;
CanvasMgr *_canvas_mgr {nullptr};
boost::scoped_ptr<EventManager> _event_manager;
std::unique_ptr<EventManager> _event_manager;
int _size_x,
_size_y,
_view_width,
_view_height;
int _size_x {-1},
_size_y {-1},
_view_width {-1},
_view_height {-1};
PropertyObject<int> _status;
PropertyObject<std::string> _status_msg;
bool _sampling_dirty,
_render_dirty,
_visible;
bool _sampling_dirty {false},
_render_dirty {true},
_visible {true};
ODGauge _texture;
@@ -235,7 +235,9 @@ namespace canvas
ElementWeakPtr _focus_element;
CullCallbackPtr _cull_callback;
bool _render_always; //!< Used to disable automatic lazy rendering (culling)
/** Used to disable automatic lazy rendering (culling) */
bool _render_always {false};
std::vector<SGPropertyNode*> _dirty_placements;
std::vector<Placements> _placements;

View File

@@ -22,13 +22,11 @@
#include "CanvasWindow.hxx"
#include <simgear/canvas/Canvas.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/scene/util/OsgMath.hxx>
#include <osgGA/GUIEventHandler>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/foreach.hpp>
namespace simgear
{
namespace canvas
@@ -43,9 +41,6 @@ namespace canvas
const Style& parent_style,
Element* parent ):
Image(canvas, node, parent_style, parent),
_attributes_dirty(0),
_resizable(false),
_capture_events(true),
_resize_top(node, "resize-top"),
_resize_right(node, "resize-right"),
_resize_bottom(node, "resize-bottom"),
@@ -92,7 +87,7 @@ namespace canvas
_capture_events = node->getBoolValue();
else if( name == "decoration-border" )
parseDecorationBorder(node->getStringValue());
else if( boost::starts_with(name, "shadow-")
else if( strutils::starts_with(name, "shadow-")
|| name == "content-size" )
_attributes_dirty |= DECORATION;
else
@@ -103,16 +98,10 @@ namespace canvas
Image::valueChanged(node);
}
//----------------------------------------------------------------------------
osg::Group* Window::getGroup()
{
return getMatrixTransform();
}
//----------------------------------------------------------------------------
const SGVec2<float> Window::getPosition() const
{
const osg::Matrix& m = getMatrixTransform()->getMatrix();
auto const& m = getMatrix();
return SGVec2<float>( m(3, 0), m(3, 1) );
}

View File

@@ -70,7 +70,6 @@ namespace canvas
virtual void update(double delta_time_sec);
virtual void valueChanged(SGPropertyNode* node);
osg::Group* getGroup();
const SGVec2<float> getPosition() const;
const SGRect<float> getScreenRegion() const;
@@ -104,7 +103,7 @@ namespace canvas
DECORATION = 1
};
uint32_t _attributes_dirty;
uint32_t _attributes_dirty {0};
CanvasPtr _canvas_decoration;
CanvasWeakPtr _canvas_content;
@@ -113,8 +112,8 @@ namespace canvas
ImagePtr _image_content,
_image_shadow;
bool _resizable,
_capture_events;
bool _resizable {false},
_capture_events {true};
PropertyObject<int> _resize_top,
_resize_right,

View File

@@ -9,10 +9,6 @@ set(HEADERS
CanvasText.hxx
)
set(DETAIL_HEADERS
detail/add_segment_variadic.hxx
)
set(SOURCES
CanvasElement.cxx
CanvasGroup.cxx
@@ -23,7 +19,6 @@ set(SOURCES
)
simgear_scene_component(canvas-elements canvas/elements "${SOURCES}" "${HEADERS}")
simgear_component(canvas-elements/detail canvas/elements/detail "" "${DETAIL_HEADERS}")
add_boost_test(canvas_element
SOURCES canvas_element_test.cpp

View File

@@ -31,10 +31,6 @@
#include <osg/StateAttribute>
#include <osg/Version>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/foreach.hpp>
#include <boost/make_shared.hpp>
#include <cassert>
#include <cmath>
#include <cstring>
@@ -214,22 +210,22 @@ namespace canvas
//----------------------------------------------------------------------------
void Element::onDestroy()
{
if( !_transform.valid() )
if( !_scene_group.valid() )
return;
// The transform node keeps a reference on this element, so ensure it is
// deleted.
BOOST_FOREACH(osg::Group* parent, _transform->getParents())
for(osg::Group* parent: _scene_group->getParents())
{
parent->removeChild(_transform.get());
parent->removeChild(_scene_group.get());
}
// Hide in case someone still holds a reference
setVisible(false);
removeListener();
_parent = 0;
_transform = 0;
_parent = nullptr;
_scene_group = nullptr;
}
//----------------------------------------------------------------------------
@@ -247,29 +243,8 @@ namespace canvas
//----------------------------------------------------------------------------
void Element::update(double dt)
{
if( !isVisible() )
return;
// Trigger matrix update
getMatrix();
// Update bounding box on manual update (manual updates pass zero dt)
if( dt == 0 && _drawable )
_drawable->getBound();
if( (_attributes_dirty & BLEND_FUNC) && _transform.valid() )
{
parseBlendFunc(
_transform->getOrCreateStateSet(),
_node->getChild("blend-source"),
_node->getChild("blend-destination"),
_node->getChild("blend-source-rgb"),
_node->getChild("blend-destination-rgb"),
_node->getChild("blend-source-alpha"),
_node->getChild("blend-destination-alpha")
);
_attributes_dirty &= ~BLEND_FUNC;
}
if( isVisible() )
updateImpl(dt);
}
//----------------------------------------------------------------------------
@@ -341,7 +316,8 @@ namespace canvas
if( listeners == _listener.end() )
return false;
BOOST_FOREACH(EventListener const& listener, listeners->second)
for(auto const& listener: listeners->second)
{
try
{
listener(event);
@@ -354,6 +330,7 @@ namespace canvas
"canvas::Element: event handler error: '" << ex.what() << "'"
);
}
}
return true;
}
@@ -395,9 +372,9 @@ namespace canvas
getBoundingBox()
#endif
.contains(osg::Vec3f(local_pos, 0));
else if( _transform.valid() )
else if( _scene_group.valid() )
// ... for other elements, i.e. groups only a bounding sphere is available
return _transform->getBound().contains(osg::Vec3f(parent_pos, 0));
return _scene_group->getBound().contains(osg::Vec3f(parent_pos, 0));
else
return false;
}
@@ -406,34 +383,32 @@ namespace canvas
//----------------------------------------------------------------------------
void Element::setVisible(bool visible)
{
if( _transform.valid() )
if( _scene_group.valid() )
// TODO check if we need another nodemask
_transform->setNodeMask(visible ? 0xffffffff : 0);
_scene_group->setNodeMask(visible ? 0xffffffff : 0);
}
//----------------------------------------------------------------------------
bool Element::isVisible() const
{
return _transform.valid() && _transform->getNodeMask() != 0;
return _scene_group.valid() && _scene_group->getNodeMask() != 0;
}
//----------------------------------------------------------------------------
osg::MatrixTransform* Element::getMatrixTransform()
osg::MatrixTransform* Element::getSceneGroup() const
{
return _transform.get();
}
//----------------------------------------------------------------------------
osg::MatrixTransform const* Element::getMatrixTransform() const
{
return _transform.get();
return _scene_group.get();
}
//----------------------------------------------------------------------------
osg::Vec2f Element::posToLocal(const osg::Vec2f& pos) const
{
getMatrix();
const osg::Matrix& m = _transform->getInverseMatrix();
if( !_scene_group )
// TODO log warning?
return pos;
updateMatrix();
const osg::Matrix& m = _scene_group->getInverseMatrix();
return osg::Vec2f
(
m(0, 0) * pos[0] + m(1, 0) * pos[1] + m(3, 0),
@@ -486,9 +461,6 @@ namespace canvas
{
if( child->getNameString() == NAME_TRANSFORM )
{
if( !_transform.valid() )
return;
if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
{
SG_LOG
@@ -525,7 +497,7 @@ namespace canvas
if( parent == _node )
{
const std::string& name = child->getNameString();
if( boost::starts_with(name, "data-") )
if( strutils::starts_with(name, "data-") )
return;
else if( StyleInfo const* style_info = getStyleInfo(name) )
{
@@ -540,7 +512,7 @@ namespace canvas
}
else if( name == "update" )
return update(0);
else if( boost::starts_with(name, "blend-") )
else if( strutils::starts_with(name, "blend-") )
return (void)(_attributes_dirty |= BLEND_FUNC);
}
else if( parent
@@ -564,6 +536,10 @@ namespace canvas
//----------------------------------------------------------------------------
void Element::setClip(const std::string& clip)
{
if( !_scene_group )
// TODO warn?
return;
osg::StateSet* ss = getOrCreateStateSet();
if( !ss )
return;
@@ -577,8 +553,8 @@ namespace canvas
// TODO generalize CSS property parsing
const std::string RECT("rect(");
if( !boost::ends_with(clip, ")")
|| !boost::starts_with(clip, RECT) )
if( !strutils::ends_with(clip, ")")
|| !strutils::starts_with(clip, RECT) )
{
SG_LOG(SG_GENERAL, SG_WARN, "Canvas: invalid clip: " << clip);
return;
@@ -618,7 +594,7 @@ namespace canvas
}
if( !_scissor )
_scissor = new RelativeScissor(_transform.get());
_scissor = new RelativeScissor(_scene_group.get());
// <top>, <right>, <bottom>, <left>
_scissor->x() = values[3];
@@ -642,26 +618,26 @@ namespace canvas
_scissor->_coord_reference = rf;
}
//----------------------------------------------------------------------------
void Element::setRotation(unsigned int index, double r)
{
_node->getChild(NAME_TRANSFORM, index, true)->setDoubleValue("rot", r);
}
//----------------------------------------------------------------------------
void Element::setRotation(unsigned int index, double r)
{
_node->getChild(NAME_TRANSFORM, index, true)->setDoubleValue("rot", r);
}
//----------------------------------------------------------------------------
void Element::setTranslation(unsigned int index, double x, double y)
{
SGPropertyNode* tf = _node->getChild(NAME_TRANSFORM, index, true);
tf->getChild("t", 0, true)->setDoubleValue(x);
tf->getChild("t", 1, true)->setDoubleValue(y);
}
//----------------------------------------------------------------------------
void Element::setTranslation(unsigned int index, double x, double y)
{
SGPropertyNode* tf = _node->getChild(NAME_TRANSFORM, index, true);
tf->getChild("t", 0, true)->setDoubleValue(x);
tf->getChild("t", 1, true)->setDoubleValue(y);
}
//----------------------------------------------------------------------------
void Element::setTransformEnabled(unsigned int index, bool enabled)
{
SGPropertyNode* tf = _node->getChild(NAME_TRANSFORM, index, true);
tf->setBoolValue("enabled", enabled);
}
//----------------------------------------------------------------------------
void Element::setTransformEnabled(unsigned int index, bool enabled)
{
SGPropertyNode* tf = _node->getChild(NAME_TRANSFORM, index, true);
tf->setBoolValue("enabled", enabled);
}
//----------------------------------------------------------------------------
osg::BoundingBox Element::getBoundingBox() const
@@ -675,8 +651,8 @@ namespace canvas
osg::BoundingBox bb;
if( _transform.valid() )
bb.expandBy(_transform->getBound());
if( _scene_group.valid() )
bb.expandBy( _scene_group->getBound() );
return bb;
}
@@ -710,73 +686,11 @@ namespace canvas
//----------------------------------------------------------------------------
osg::Matrix Element::getMatrix() const
{
if( !_transform )
if( !_scene_group )
return osg::Matrix::identity();
if( !(_attributes_dirty & TRANSFORM) )
return _transform->getMatrix();
osg::Matrix m;
for( size_t i = 0; i < _transform_types.size(); ++i )
{
// Skip unused indizes...
if( _transform_types[i] == TT_NONE )
continue;
SGPropertyNode* tf_node = _node->getChild("tf", i, true);
if (!tf_node->getBoolValue("enabled", true)) {
continue; // skip disabled transforms
}
// Build up the matrix representation of the current transform node
osg::Matrix tf;
switch( _transform_types[i] )
{
case TT_MATRIX:
tf = osg::Matrix( tf_node->getDoubleValue("m[0]", 1),
tf_node->getDoubleValue("m[1]", 0),
0,
tf_node->getDoubleValue("m[6]", 0),
tf_node->getDoubleValue("m[2]", 0),
tf_node->getDoubleValue("m[3]", 1),
0,
tf_node->getDoubleValue("m[7]", 0),
0,
0,
1,
0,
tf_node->getDoubleValue("m[4]", 0),
tf_node->getDoubleValue("m[5]", 0),
0,
tf_node->getDoubleValue("m[8]", 1) );
break;
case TT_TRANSLATE:
tf.makeTranslate( osg::Vec3f( tf_node->getDoubleValue("t[0]", 0),
tf_node->getDoubleValue("t[1]", 0),
0 ) );
break;
case TT_ROTATE:
tf.makeRotate( tf_node->getDoubleValue("rot", 0), 0, 0, 1 );
break;
case TT_SCALE:
{
float sx = tf_node->getDoubleValue("s[0]", 1);
// sy defaults to sx...
tf.makeScale( sx, tf_node->getDoubleValue("s[1]", sx), 1 );
break;
}
default:
break;
}
m.postMult( tf );
}
_transform->setMatrix(m);
_attributes_dirty &= ~TRANSFORM;
return m;
updateMatrix();
return _scene_group->getMatrix();
}
//----------------------------------------------------------------------------
@@ -790,11 +704,8 @@ namespace canvas
PropertyBasedElement(node),
_canvas( canvas ),
_parent( parent ),
_attributes_dirty( 0 ),
_transform( new osg::MatrixTransform ),
_style( parent_style ),
_scissor( 0 ),
_drawable( 0 )
_scene_group( new osg::MatrixTransform ),
_style( parent_style )
{
staticInit();
@@ -806,15 +717,15 @@ namespace canvas
);
// Ensure elements are drawn in order they appear in the element tree
_transform->getOrCreateStateSet()
->setRenderBinDetails
(
0,
"PreOrderBin",
osg::StateSet::OVERRIDE_RENDERBIN_DETAILS
);
_scene_group
->getOrCreateStateSet()
->setRenderBinDetails(
0,
"PreOrderBin",
osg::StateSet::OVERRIDE_RENDERBIN_DETAILS
);
_transform->setUserData( new OSGUserData(this) );
_scene_group->setUserData( new OSGUserData(this) );
}
//----------------------------------------------------------------------------
@@ -902,11 +813,21 @@ namespace canvas
void Element::setDrawable( osg::Drawable* drawable )
{
_drawable = drawable;
assert( _drawable );
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
if( !_drawable )
{
SG_LOG(SG_GL, SG_WARN, "canvas::Element::setDrawable: NULL drawable");
return;
}
if( !_scene_group )
{
SG_LOG(SG_GL, SG_WARN, "canvas::Element::setDrawable: NULL scenegroup");
return;
}
auto geode = new osg::Geode;
geode->addDrawable(_drawable);
_transform->addChild(geode);
_scene_group->addChild(geode);
}
//----------------------------------------------------------------------------
@@ -914,18 +835,109 @@ namespace canvas
{
if( _drawable.valid() )
return _drawable->getOrCreateStateSet();
if( _transform.valid() )
return _transform->getOrCreateStateSet();
return 0;
else if( _scene_group.valid() )
return _scene_group->getOrCreateStateSet();
else
return nullptr;
}
//----------------------------------------------------------------------------
void Element::setupStyle()
{
BOOST_FOREACH( Style::value_type style, _style )
for(auto const& style: _style)
setStyle(style.second);
}
//----------------------------------------------------------------------------
void Element::updateMatrix() const
{
if( !(_attributes_dirty & TRANSFORM) || !_scene_group )
return;
osg::Matrix m;
for( size_t i = 0; i < _transform_types.size(); ++i )
{
// Skip unused indizes...
if( _transform_types[i] == TT_NONE )
continue;
SGPropertyNode* tf_node = _node->getChild("tf", i, true);
if (!tf_node->getBoolValue("enabled", true)) {
continue; // skip disabled transforms
}
// Build up the matrix representation of the current transform node
osg::Matrix tf;
switch( _transform_types[i] )
{
case TT_MATRIX:
tf = osg::Matrix( tf_node->getDoubleValue("m[0]", 1),
tf_node->getDoubleValue("m[1]", 0),
0,
tf_node->getDoubleValue("m[6]", 0),
tf_node->getDoubleValue("m[2]", 0),
tf_node->getDoubleValue("m[3]", 1),
0,
tf_node->getDoubleValue("m[7]", 0),
0,
0,
1,
0,
tf_node->getDoubleValue("m[4]", 0),
tf_node->getDoubleValue("m[5]", 0),
0,
tf_node->getDoubleValue("m[8]", 1) );
break;
case TT_TRANSLATE:
tf.makeTranslate( osg::Vec3f( tf_node->getDoubleValue("t[0]", 0),
tf_node->getDoubleValue("t[1]", 0),
0 ) );
break;
case TT_ROTATE:
tf.makeRotate( tf_node->getDoubleValue("rot", 0), 0, 0, 1 );
break;
case TT_SCALE:
{
float sx = tf_node->getDoubleValue("s[0]", 1);
// sy defaults to sx...
tf.makeScale( sx, tf_node->getDoubleValue("s[1]", sx), 1 );
break;
}
default:
break;
}
m.postMult( tf );
}
_scene_group->setMatrix(m);
_attributes_dirty &= ~TRANSFORM;
}
//----------------------------------------------------------------------------
void Element::updateImpl(double dt)
{
updateMatrix();
// Update bounding box on manual update (manual updates pass zero dt)
if( dt == 0 && _drawable )
_drawable->getBound();
if( (_attributes_dirty & BLEND_FUNC) )
{
parseBlendFunc(
_scene_group->getOrCreateStateSet(),
_node->getChild("blend-source"),
_node->getChild("blend-destination"),
_node->getChild("blend-source-rgb"),
_node->getChild("blend-destination-rgb"),
_node->getChild("blend-source-alpha"),
_node->getChild("blend-destination-alpha")
);
_attributes_dirty &= ~BLEND_FUNC;
}
}
} // namespace canvas
} // namespace simgear

View File

@@ -24,13 +24,13 @@
#include <simgear/canvas/CanvasEvent.hxx>
#include <simgear/props/PropertyBasedElement.hxx>
#include <simgear/misc/stdint.hxx> // for uint32_t
#include <simgear/std/type_traits.hxx>
#include <osg/BoundingBox>
#include <osg/MatrixTransform>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/type_traits/is_base_of.hpp>
namespace osg
{
@@ -49,6 +49,7 @@ namespace canvas
public PropertyBasedElement
{
public:
using SceneGroupWeakPtr = osg::observer_ptr<osg::MatrixTransform>;
/**
* Store pointer to window as user data
@@ -142,8 +143,11 @@ namespace canvas
*/
virtual bool isVisible() const;
osg::MatrixTransform* getMatrixTransform();
osg::MatrixTransform const* getMatrixTransform() const;
/**
* Get the according group in the OSG scene graph
*/
// TODO ref_ptr
osg::MatrixTransform* getSceneGroup() const;
/**
* Transform position to local coordinages.
@@ -217,13 +221,14 @@ namespace canvas
*/
template<typename Derived>
static
typename boost::enable_if<
boost::is_base_of<Element, Derived>,
std::enable_if_t<
std::is_base_of<Element, Derived>::value,
ElementPtr
>::type create( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& style = Style(),
Element* parent = NULL )
>
create( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& style = Style(),
Element* parent = NULL )
{
return ElementPtr( new Derived(canvas, node, style, parent) );
}
@@ -251,13 +256,13 @@ namespace canvas
CanvasWeakPtr _canvas;
ElementWeakPtr _parent;
mutable uint32_t _attributes_dirty;
mutable uint32_t _attributes_dirty = 0;
osg::observer_ptr<osg::MatrixTransform> _transform;
std::vector<TransformType> _transform_types;
SceneGroupWeakPtr _scene_group;
std::vector<TransformType> _transform_types;
Style _style;
RelativeScissor *_scissor;
RelativeScissor *_scissor = nullptr;
typedef std::vector<EventListener> Listener;
typedef std::map<int, Listener> ListenerMap;
@@ -584,6 +589,10 @@ namespace canvas
void setupStyle();
void updateMatrix() const;
virtual void updateImpl(double dt);
private:
osg::ref_ptr<osg::Drawable> _drawable;

View File

@@ -23,13 +23,10 @@
#include "CanvasMap.hxx"
#include "CanvasPath.hxx"
#include "CanvasText.hxx"
#include <simgear/canvas/CanvasEventVisitor.hxx>
#include <simgear/canvas/events/MouseEvent.hxx>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/lambda/core.hpp>
namespace simgear
{
namespace canvas
@@ -48,7 +45,7 @@ namespace canvas
ElementFactories Group::_child_factories;
const std::string Group::TYPE_NAME = "group";
void warnTransformExpired(const char* member_name)
void warnSceneGroupExpired(const char* member_name)
{
SG_LOG( SG_GENERAL,
SG_WARN,
@@ -135,63 +132,57 @@ namespace canvas
//----------------------------------------------------------------------------
ElementPtr Group::getElementById(const std::string& id)
{
if( !_transform.valid() )
if( !_scene_group.valid() )
{
warnTransformExpired("getElementById");
return ElementPtr();
warnSceneGroupExpired("getElementById");
return {};
}
std::vector<GroupPtr> groups;
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
// TODO check search algorithm. Not completely breadth-first and might be
// possible with using less dynamic memory
std::vector<GroupPtr> child_groups;
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
{
const ElementPtr& el = getChildByIndex(i);
if( el->get<std::string>("id") == id )
return el;
Group* group = dynamic_cast<Group*>(el.get());
if( group )
groups.push_back(group);
if( Group* child_group = dynamic_cast<Group*>(el.get()) )
child_groups.push_back(child_group);
}
BOOST_FOREACH( GroupPtr group, groups )
for(auto group: child_groups)
{
ElementPtr el = group->getElementById(id);
if( el )
if( ElementPtr el = group->getElementById(id) )
return el;
}
return ElementPtr();
return {};
}
//----------------------------------------------------------------------------
void Group::clearEventListener()
{
if( !_transform.valid() )
return warnTransformExpired("clearEventListener");
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
getChildByIndex(i)->clearEventListener();
Element::clearEventListener();
}
//----------------------------------------------------------------------------
void Group::update(double dt)
{
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
getChildByIndex(i)->update(dt);
if( !_scene_group.valid() )
return warnSceneGroupExpired("clearEventListener");
Element::update(dt);
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
getChildByIndex(i)->clearEventListener();
}
//----------------------------------------------------------------------------
bool Group::traverse(EventVisitor& visitor)
{
// Iterate in reverse order as last child is displayed on top
for(size_t i = _transform->getNumChildren(); i --> 0;)
if( _scene_group.valid() )
{
if( getChildByIndex(i)->accept(visitor) )
return true;
// Iterate in reverse order as last child is displayed on top
for(size_t i = _scene_group->getNumChildren(); i --> 0;)
{
if( getChildByIndex(i)->accept(visitor) )
return true;
}
}
return false;
}
@@ -206,13 +197,13 @@ namespace canvas
bool handled = setStyleImpl(style, style_info);
if( style_info->inheritable )
{
if( !_transform.valid() )
if( !_scene_group.valid() )
{
warnTransformExpired("setStyle");
warnSceneGroupExpired("setStyle");
return false;
}
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
handled |= getChildByIndex(i)->setStyle(style, style_info);
}
@@ -222,26 +213,20 @@ namespace canvas
//----------------------------------------------------------------------------
osg::BoundingBox Group::getTransformedBounds(const osg::Matrix& m) const
{
osg::BoundingBox bb;
if( !_transform.valid() )
if( !_scene_group.valid() )
{
warnTransformExpired("getTransformedBounds");
return bb;
warnSceneGroupExpired("getTransformedBounds");
return {};
}
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
osg::BoundingBox bb;
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
{
const ElementPtr& child = getChildByIndex(i);
if( !child->getMatrixTransform()->getNodeMask() )
auto child = getChildByIndex(i);
if( !child || !child->isVisible() )
continue;
bb.expandBy
(
child->getTransformedBounds
(
child->getMatrixTransform()->getMatrix() * m
)
);
bb.expandBy( child->getTransformedBounds(child->getMatrix() * m) );
}
return bb;
@@ -257,6 +242,15 @@ namespace canvas
return ElementFactory();
}
//----------------------------------------------------------------------------
void Group::updateImpl(double dt)
{
Element::updateImpl(dt);
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
getChildByIndex(i)->update(dt);
}
//----------------------------------------------------------------------------
void Group::childAdded(SGPropertyNode* child)
{
@@ -266,13 +260,13 @@ namespace canvas
ElementFactory child_factory = getChildFactory( child->getNameString() );
if( child_factory )
{
if( !_transform.valid() )
return warnTransformExpired("childAdded");
if( !_scene_group.valid() )
return warnSceneGroupExpired("childAdded");
ElementPtr element = child_factory(_canvas, child, _style, this);
// Add to osg scene graph...
_transform->addChild( element->getMatrixTransform() );
_scene_group->addChild(element->getSceneGroup());
// ...and ensure correct ordering
handleZIndexChanged(element);
@@ -293,7 +287,7 @@ namespace canvas
if( getChildFactory(node->getNameString()) )
{
if( !_transform.valid() )
if( !_scene_group.valid() )
// If transform is destroyed also all children are destroyed, so we can
// not do anything here.
return;
@@ -323,7 +317,7 @@ namespace canvas
void Group::childChanged(SGPropertyNode* node)
{
SGPropertyNode* parent = node->getParent();
SGPropertyNode* grand_parent = parent ? parent->getParent() : NULL;
SGPropertyNode* grand_parent = parent ? parent->getParent() : nullptr;
if( grand_parent == _node
&& node->getNameString() == "z-index" )
@@ -333,16 +327,18 @@ namespace canvas
//----------------------------------------------------------------------------
void Group::handleZIndexChanged(ElementPtr child, int z_index)
{
if( !child || !_transform.valid() )
if( !child || !_scene_group.valid() )
return;
osg::ref_ptr<osg::MatrixTransform> tf = child->getMatrixTransform();
size_t index = _transform->getChildIndex(tf),
// Keep reference to prevent deleting while removing and re-inserting later
osg::ref_ptr<osg::MatrixTransform> tf = child->getSceneGroup();
size_t index = _scene_group->getChildIndex(tf),
index_new = index;
for(;; ++index_new)
{
if( index_new + 1 == _transform->getNumChildren() )
if( index_new + 1 == _scene_group->getNumChildren() )
break;
// Move to end of block with same index (= move upwards until the next
@@ -369,8 +365,8 @@ namespace canvas
return;
}
_transform->removeChild(index);
_transform->insertChild(index_new, tf);
_scene_group->removeChild(index);
_scene_group->insertChild(index_new, tf);
SG_LOG
(
@@ -383,24 +379,27 @@ namespace canvas
//----------------------------------------------------------------------------
ElementPtr Group::getChildByIndex(size_t index) const
{
assert(_transform.valid());
OSGUserData* ud =
static_cast<OSGUserData*>(_transform->getChild(index)->getUserData());
assert(ud);
return ud->element;
assert( _scene_group.valid() );
auto child = _scene_group->getChild(index);
if( !child )
return {};
auto ud = static_cast<OSGUserData*>(child->getUserData());
return ud ? ud->element : ElementPtr();
}
//----------------------------------------------------------------------------
ElementPtr Group::findChild( const SGPropertyNode* node,
const std::string& id ) const
{
if( !_transform.valid() )
if( !_scene_group.valid() )
{
warnTransformExpired("findChild");
return ElementPtr();
warnSceneGroupExpired("findChild");
return {};
}
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
{
ElementPtr el = getChildByIndex(i);
@@ -416,7 +415,7 @@ namespace canvas
}
}
return ElementPtr();
return {};
}
} // namespace canvas

View File

@@ -88,8 +88,6 @@ namespace canvas
virtual void clearEventListener();
virtual void update(double dt);
virtual bool traverse(EventVisitor& visitor);
virtual bool setStyle( const SGPropertyNode* child,
@@ -107,6 +105,8 @@ namespace canvas
*/
virtual ElementFactory getChildFactory(const std::string& type) const;
virtual void updateImpl(double dt);
virtual void childAdded(SGPropertyNode * child);
virtual void childRemoved(SGPropertyNode * child);
virtual void childChanged(SGPropertyNode * child);

View File

@@ -117,9 +117,7 @@ namespace canvas
ElementWeakPtr parent ):
Element(canvas, node, parent_style, parent),
_texture(new osg::Texture2D),
_node_src_rect( node->getNode("source", 0, true) ),
_src_rect(0,0),
_region(0,0)
_node_src_rect( node->getNode("source", 0, true) )
{
staticInit();
@@ -157,22 +155,207 @@ namespace canvas
//----------------------------------------------------------------------------
Image::~Image()
{
if( _http_request ) {
Canvas::getSystemAdapter()->getHTTPClient()->cancelRequest(_http_request, "image destroyed");
}
if( _http_request )
{
Canvas::getSystemAdapter()
->getHTTPClient()
->cancelRequest(_http_request, "image destroyed");
}
}
//----------------------------------------------------------------------------
void Image::update(double dt)
void Image::valueChanged(SGPropertyNode* child)
{
Element::update(dt);
// If the image is switched from invisible to visible, and it shows a
// canvas, we need to delay showing it by one frame to ensure the canvas is
// updated before the image is displayed.
//
// As canvas::Element handles and filters changes to the "visible" property
// we can not check this in Image::childChanged but instead have to override
// Element::valueChanged.
if( !isVisible()
&& child->getParent() == _node
&& child->getNameString() == "visible"
&& child->getBoolValue() )
{
CullCallback* cb =
#if OSG_VERSION_LESS_THAN(3,3,2)
static_cast<CullCallback*>
#else
dynamic_cast<CullCallback*>
#endif
( _geom->getCullCallback() );
if( cb )
cb->cullNextFrame();
}
Element::valueChanged(child);
}
//----------------------------------------------------------------------------
void Image::setSrcCanvas(CanvasPtr canvas)
{
CanvasPtr src_canvas = _src_canvas.lock(),
self_canvas = _canvas.lock();
if( src_canvas )
src_canvas->removeParentCanvas(self_canvas);
if( self_canvas )
self_canvas->removeChildCanvas(src_canvas);
_src_canvas = src_canvas = canvas;
_attributes_dirty |= SRC_CANVAS;
_geom->setCullCallback(canvas ? new CullCallback(canvas) : 0);
if( src_canvas )
{
setupDefaultDimensions();
if( self_canvas )
{
self_canvas->addChildCanvas(src_canvas);
src_canvas->addParentCanvas(self_canvas);
}
}
}
//----------------------------------------------------------------------------
CanvasWeakPtr Image::getSrcCanvas() const
{
return _src_canvas;
}
//----------------------------------------------------------------------------
void Image::setImage(osg::ref_ptr<osg::Image> img)
{
// remove canvas...
setSrcCanvas( CanvasPtr() );
_texture->setResizeNonPowerOfTwoHint(false);
_texture->setImage(img);
_texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
_texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
_geom->getOrCreateStateSet()
->setTextureAttributeAndModes(0, _texture);
if( img )
setupDefaultDimensions();
}
//----------------------------------------------------------------------------
void Image::setFill(const std::string& fill)
{
osg::Vec4 color(1,1,1,1);
if( !fill.empty() // If no color is given default to white
&& !parseColor(fill, color) )
return;
setFill(color);
}
//----------------------------------------------------------------------------
void Image::setFill(const osg::Vec4& color)
{
_colors->front() = color;
_colors->dirty();
}
//----------------------------------------------------------------------------
void Image::setOutset(const std::string& outset)
{
_outset = CSSBorder::parse(outset);
_attributes_dirty |= DEST_SIZE;
}
//----------------------------------------------------------------------------
void Image::setPreserveAspectRatio(const std::string& scale)
{
_preserve_aspect_ratio = SVGpreserveAspectRatio::parse(scale);
_attributes_dirty |= SRC_RECT;
}
//----------------------------------------------------------------------------
void Image::setSourceRect(const SGRect<float>& sourceRect)
{
_attributes_dirty |= SRC_RECT;
_src_rect = sourceRect;
}
//----------------------------------------------------------------------------
void Image::setSlice(const std::string& slice)
{
_slice = CSSBorder::parse(slice);
_attributes_dirty |= SRC_RECT | DEST_SIZE;
}
//----------------------------------------------------------------------------
void Image::setSliceWidth(const std::string& width)
{
_slice_width = CSSBorder::parse(width);
_attributes_dirty |= DEST_SIZE;
}
//----------------------------------------------------------------------------
const SGRect<float>& Image::getRegion() const
{
return _region;
}
//----------------------------------------------------------------------------
bool Image::handleEvent(const EventPtr& event)
{
bool handled = Element::handleEvent(event);
CanvasPtr src_canvas = _src_canvas.lock();
if( !src_canvas )
return handled;
if( MouseEventPtr mouse_event = dynamic_cast<MouseEvent*>(event.get()) )
{
mouse_event.reset( new MouseEvent(*mouse_event) );
mouse_event->client_pos = mouse_event->local_pos
- toOsg(_region.getMin());
osg::Vec2f size(_region.width(), _region.height());
if( _outset.isValid() )
{
CSSBorder::Offsets outset =
_outset.getAbsOffsets(getTextureDimensions());
mouse_event->client_pos += osg::Vec2f(outset.l, outset.t);
size.x() += outset.l + outset.r;
size.y() += outset.t + outset.b;
}
// Scale event pos according to canvas view size vs. displayed/screen size
mouse_event->client_pos.x() *= src_canvas->getViewWidth() / size.x();
mouse_event->client_pos.y() *= src_canvas->getViewHeight()/ size.y();
mouse_event->local_pos = mouse_event->client_pos;
handled |= src_canvas->handleMouseEvent(mouse_event);
}
else if( KeyboardEventPtr keyboard_event =
dynamic_cast<KeyboardEvent*>(event.get()) )
{
handled |= src_canvas->handleKeyboardEvent(keyboard_event);
}
return handled;
}
//----------------------------------------------------------------------------
void Image::updateImpl(double dt)
{
Element::updateImpl(dt);
osg::Texture2D* texture = dynamic_cast<osg::Texture2D*>
(
_geom->getOrCreateStateSet()
->getTextureAttribute(0, osg::StateAttribute::TEXTURE)
);
simgear::canvas::CanvasPtr canvas = _src_canvas.lock();
auto canvas = _src_canvas.lock();
if( (_attributes_dirty & SRC_CANVAS)
// check if texture has changed (eg. due to resizing)
@@ -402,188 +585,6 @@ namespace canvas
}
}
//----------------------------------------------------------------------------
void Image::valueChanged(SGPropertyNode* child)
{
// If the image is switched from invisible to visible, and it shows a
// canvas, we need to delay showing it by one frame to ensure the canvas is
// updated before the image is displayed.
//
// As canvas::Element handles and filters changes to the "visible" property
// we can not check this in Image::childChanged but instead have to override
// Element::valueChanged.
if( !isVisible()
&& child->getParent() == _node
&& child->getNameString() == "visible"
&& child->getBoolValue() )
{
CullCallback* cb =
#if OSG_VERSION_LESS_THAN(3,3,2)
static_cast<CullCallback*>
#else
dynamic_cast<CullCallback*>
#endif
( _geom->getCullCallback() );
if( cb )
cb->cullNextFrame();
}
Element::valueChanged(child);
}
//----------------------------------------------------------------------------
void Image::setSrcCanvas(CanvasPtr canvas)
{
CanvasPtr src_canvas = _src_canvas.lock(),
self_canvas = _canvas.lock();
if( src_canvas )
src_canvas->removeParentCanvas(self_canvas);
if( self_canvas )
self_canvas->removeChildCanvas(src_canvas);
_src_canvas = src_canvas = canvas;
_attributes_dirty |= SRC_CANVAS;
_geom->setCullCallback(canvas ? new CullCallback(canvas) : 0);
if( src_canvas )
{
setupDefaultDimensions();
if( self_canvas )
{
self_canvas->addChildCanvas(src_canvas);
src_canvas->addParentCanvas(self_canvas);
}
}
}
//----------------------------------------------------------------------------
CanvasWeakPtr Image::getSrcCanvas() const
{
return _src_canvas;
}
//----------------------------------------------------------------------------
void Image::setImage(osg::ref_ptr<osg::Image> img)
{
// remove canvas...
setSrcCanvas( CanvasPtr() );
_texture->setResizeNonPowerOfTwoHint(false);
_texture->setImage(img);
_texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
_texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
_geom->getOrCreateStateSet()
->setTextureAttributeAndModes(0, _texture);
if( img )
setupDefaultDimensions();
}
//----------------------------------------------------------------------------
void Image::setFill(const std::string& fill)
{
osg::Vec4 color(1,1,1,1);
if( !fill.empty() // If no color is given default to white
&& !parseColor(fill, color) )
return;
setFill(color);
}
//----------------------------------------------------------------------------
void Image::setFill(const osg::Vec4& color)
{
_colors->front() = color;
_colors->dirty();
}
//----------------------------------------------------------------------------
void Image::setOutset(const std::string& outset)
{
_outset = CSSBorder::parse(outset);
_attributes_dirty |= DEST_SIZE;
}
//----------------------------------------------------------------------------
void Image::setPreserveAspectRatio(const std::string& scale)
{
_preserve_aspect_ratio = SVGpreserveAspectRatio::parse(scale);
_attributes_dirty |= SRC_RECT;
}
//----------------------------------------------------------------------------
void Image::setSourceRect(const SGRect<float>& sourceRect)
{
_attributes_dirty |= SRC_RECT;
_src_rect = sourceRect;
}
//----------------------------------------------------------------------------
void Image::setSlice(const std::string& slice)
{
_slice = CSSBorder::parse(slice);
_attributes_dirty |= SRC_RECT | DEST_SIZE;
}
//----------------------------------------------------------------------------
void Image::setSliceWidth(const std::string& width)
{
_slice_width = CSSBorder::parse(width);
_attributes_dirty |= DEST_SIZE;
}
//----------------------------------------------------------------------------
const SGRect<float>& Image::getRegion() const
{
return _region;
}
//----------------------------------------------------------------------------
bool Image::handleEvent(const EventPtr& event)
{
bool handled = Element::handleEvent(event);
CanvasPtr src_canvas = _src_canvas.lock();
if( !src_canvas )
return handled;
if( MouseEventPtr mouse_event = dynamic_cast<MouseEvent*>(event.get()) )
{
mouse_event.reset( new MouseEvent(*mouse_event) );
mouse_event->client_pos = mouse_event->local_pos
- toOsg(_region.getMin());
osg::Vec2f size(_region.width(), _region.height());
if( _outset.isValid() )
{
CSSBorder::Offsets outset =
_outset.getAbsOffsets(getTextureDimensions());
mouse_event->client_pos += osg::Vec2f(outset.l, outset.t);
size.x() += outset.l + outset.r;
size.y() += outset.t + outset.b;
}
// Scale event pos according to canvas view size vs. displayed/screen size
mouse_event->client_pos.x() *= src_canvas->getViewWidth() / size.x();
mouse_event->client_pos.y() *= src_canvas->getViewHeight()/ size.y();
mouse_event->local_pos = mouse_event->client_pos;
handled |= src_canvas->handleMouseEvent(mouse_event);
}
else if( KeyboardEventPtr keyboard_event =
dynamic_cast<KeyboardEvent*>(event.get()) )
{
handled |= src_canvas->handleKeyboardEvent(keyboard_event);
}
return handled;
}
//----------------------------------------------------------------------------
void Image::childChanged(SGPropertyNode* child)
{
@@ -634,7 +635,9 @@ namespace canvas
// Abort pending request
if( _http_request )
{
Canvas::getSystemAdapter()->getHTTPClient()->cancelRequest(_http_request, "setting new image");
Canvas::getSystemAdapter()
->getHTTPClient()
->cancelRequest(_http_request, "setting new image");
_http_request.reset();
}

View File

@@ -53,7 +53,6 @@ namespace canvas
ElementWeakPtr parent = 0 );
virtual ~Image();
virtual void update(double dt);
virtual void valueChanged(SGPropertyNode* child);
void setSrcCanvas(CanvasPtr canvas);
@@ -100,8 +99,8 @@ namespace canvas
*
*/
void setSourceRect(const SGRect<float>& sourceRect);
protected:
protected:
enum ImageAttributes
{
SRC_RECT = LAST_ATTRIBUTE << 1, // Source image rectangle
@@ -109,6 +108,8 @@ namespace canvas
SRC_CANVAS = DEST_SIZE << 1
};
virtual void updateImpl(double dt);
virtual void childChanged(SGPropertyNode * child);
void setupDefaultDimensions();
@@ -134,9 +135,9 @@ namespace canvas
osg::ref_ptr<osg::Vec2Array> _texCoords;
osg::ref_ptr<osg::Vec4Array> _colors;
SGPropertyNode *_node_src_rect;
SGRect<float> _src_rect,
_region;
SGPropertyNode *_node_src_rect = nullptr;
SGRect<float> _src_rect {0, 0},
_region {0, 0};
SVGpreserveAspectRatio _preserve_aspect_ratio;

View File

@@ -18,13 +18,14 @@
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasMap.hxx"
#include "map/geo_node_pair.hxx"
#include "map/projection.hxx"
#include <cmath>
#include <simgear/misc/strutils.hxx>
#include <boost/algorithm/string/predicate.hpp>
#include <cmath>
#define LOG_GEO_RET(msg) \
{\
@@ -47,6 +48,12 @@ namespace canvas
const std::string GEO = "-geo";
const std::string HDG = "hdg";
const std::string Map::TYPE_NAME = "map";
const std::string WEB_MERCATOR = "webmercator";
const std::string REF_LAT = "ref-lat";
const std::string REF_LON = "ref-lon";
const std::string SCREEN_RANGE = "screen-range";
const std::string RANGE = "range";
const std::string PROJECTION = "projection";
//----------------------------------------------------------------------------
void Map::staticInit()
@@ -64,12 +71,11 @@ namespace canvas
const SGPropertyNode_ptr& node,
const Style& parent_style,
ElementWeakPtr parent ):
Group(canvas, node, parent_style, parent),
// TODO make projection configurable
_projection(new SansonFlamsteedProjection),
_projection_dirty(true)
Group(canvas, node, parent_style, parent)
{
staticInit();
projectionNodeChanged(node->getChild(PROJECTION));
}
//----------------------------------------------------------------------------
@@ -79,13 +85,13 @@ namespace canvas
}
//----------------------------------------------------------------------------
void Map::update(double dt)
void Map::updateImpl(double dt)
{
for( GeoNodes::iterator it = _geo_nodes.begin();
it != _geo_nodes.end();
++it )
Group::updateImpl(dt);
for(auto& it: _geo_nodes)
{
GeoNodePair* geo_node = it->second.get();
GeoNodePair* geo_node = it.second.get();
if( !geo_node->isComplete()
|| (!geo_node->isDirty() && !_projection_dirty) )
continue;
@@ -107,14 +113,12 @@ namespace canvas
geo_node->setDirty(false);
}
_projection_dirty = false;
Group::update(dt);
}
//----------------------------------------------------------------------------
void Map::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
{
if( boost::ends_with(child->getNameString(), GEO) )
if( strutils::ends_with(child->getNameString(), GEO) )
_geo_nodes[child].reset(new GeoNodePair());
else if( parent != _node && child->getNameString() == HDG )
_hdg_nodes.insert(child);
@@ -125,7 +129,7 @@ namespace canvas
//----------------------------------------------------------------------------
void Map::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
{
if( boost::ends_with(child->getNameString(), GEO) )
if( strutils::ends_with(child->getNameString(), GEO) )
// TODO remove from other node
_geo_nodes.erase(child);
else if( parent != _node && child->getName() == HDG )
@@ -147,7 +151,7 @@ namespace canvas
{
const std::string& name = child->getNameString();
if( boost::ends_with(name, GEO) )
if( strutils::ends_with(name, GEO) )
return geoNodeChanged(child);
else if( name == HDG )
return hdgNodeChanged(child);
@@ -162,10 +166,10 @@ namespace canvas
if( child->getParent() != _node )
return Group::childChanged(child);
if( child->getNameString() == "ref-lat"
|| child->getNameString() == "ref-lon" )
_projection->setWorldPosition( _node->getDoubleValue("ref-lat"),
_node->getDoubleValue("ref-lon") );
if( child->getNameString() == REF_LAT
|| child->getNameString() == REF_LON )
_projection->setWorldPosition( _node->getDoubleValue(REF_LAT),
_node->getDoubleValue(REF_LON) );
else if( child->getNameString() == HDG )
{
_projection->setOrientation(child->getFloatValue());
@@ -174,16 +178,35 @@ namespace canvas
++it )
hdgNodeChanged(*it);
}
else if( child->getNameString() == "range" )
else if( child->getNameString() == RANGE )
_projection->setRange(child->getDoubleValue());
else if( child->getNameString() == "screen-range" )
else if( child->getNameString() == SCREEN_RANGE )
_projection->setScreenRange(child->getDoubleValue());
else if( child->getNameString() == PROJECTION )
projectionNodeChanged(child);
else
return Group::childChanged(child);
_projection_dirty = true;
}
//----------------------------------------------------------------------------
void Map::projectionNodeChanged(SGPropertyNode* child)
{
if(child && child->getStringValue() == WEB_MERCATOR)
_projection = std::make_shared<WebMercatorProjection>();
else
_projection = std::make_shared<SansonFlamsteedProjection>();
_projection->setWorldPosition(_node->getDoubleValue(REF_LAT),
_node->getDoubleValue(REF_LON) );
_projection->setOrientation(_node->getFloatValue(HDG));
_projection->setScreenRange(_node->getDoubleValue(SCREEN_RANGE));
_projection->setRange(_node->getDoubleValue(RANGE));
_projection_dirty = true;
}
//----------------------------------------------------------------------------
void Map::geoNodeChanged(SGPropertyNode* child)
{

View File

@@ -22,9 +22,9 @@
#include "CanvasGroup.hxx"
#include <boost/shared_ptr.hpp>
#include <boost/unordered_map.hpp>
#include <boost/unordered_set.hpp>
#include <memory>
#include <unordered_map>
#include <unordered_set>
namespace simgear
{
@@ -45,45 +45,41 @@ namespace canvas
ElementWeakPtr parent = 0 );
virtual ~Map();
virtual void update(double dt);
virtual void childAdded( SGPropertyNode * parent,
SGPropertyNode * child );
virtual void childRemoved( SGPropertyNode * parent,
SGPropertyNode * child );
virtual void valueChanged(SGPropertyNode * child);
protected:
virtual void updateImpl(double dt);
virtual void childChanged(SGPropertyNode * child);
void updateProjection(SGPropertyNode* type_node);
typedef boost::unordered_map< SGPropertyNode*,
boost::shared_ptr<GeoNodePair>
> GeoNodes;
typedef boost::unordered_set<SGPropertyNode*> NodeSet;
virtual void childAdded( SGPropertyNode* parent,
SGPropertyNode* child );
virtual void childRemoved( SGPropertyNode* parent,
SGPropertyNode* child );
virtual void valueChanged(SGPropertyNode* child);
virtual void childChanged(SGPropertyNode* child);
using GeoNodes =
std::unordered_map<SGPropertyNode*, std::shared_ptr<GeoNodePair>>;
using NodeSet = std::unordered_set<SGPropertyNode*>;
GeoNodes _geo_nodes;
NodeSet _hdg_nodes;
boost::shared_ptr<HorizontalProjection> _projection;
bool _projection_dirty;
std::shared_ptr<HorizontalProjection> _projection;
bool _projection_dirty = false;
struct GeoCoord
{
GeoCoord():
type(INVALID),
value(0)
{}
enum
{
INVALID,
LATITUDE,
LONGITUDE
} type;
double value;
} type = INVALID;
double value = 0;
};
void geoNodeChanged(SGPropertyNode * child);
void hdgNodeChanged(SGPropertyNode * child);
void projectionNodeChanged(SGPropertyNode* child);
void geoNodeChanged(SGPropertyNode* child);
void hdgNodeChanged(SGPropertyNode* child);
GeoCoord parseGeoCoord(const std::string& val) const;
};

View File

@@ -206,23 +206,13 @@ namespace canvas
return SGVec2f(-1.0f, -1.0f);
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
class Path::PathDrawable:
public osg::Drawable
{
public:
PathDrawable(Path* path):
_path_element(path),
_path(VG_INVALID_HANDLE),
_paint(VG_INVALID_HANDLE),
_paint_fill(VG_INVALID_HANDLE),
_attributes_dirty(~0),
_mode(0),
_fill_rule(VG_EVEN_ODD),
_stroke_width(1),
_stroke_linecap(VG_CAP_BUTT),
_stroke_linejoin(VG_JOIN_MITER)
_path_element(path)
{
setSupportsDisplayList(false);
setDataVariance(Object::DYNAMIC);
@@ -283,6 +273,16 @@ namespace canvas
}
}
/**
* Set path fill opacity (Only used if fill is not "none")
*/
void setFillOpacity(float opacity)
{
_fill_opacity =
static_cast<uint8_t>(SGMiscf::clip(opacity, 0.f, 1.f) * 255);
_attributes_dirty |= FILL_COLOR;
}
/**
* Set path fill rule ("pseudo-nonzero" or "evenodd")
*
@@ -310,7 +310,7 @@ namespace canvas
else if( parseColor(stroke, _stroke_color) )
{
_mode |= VG_STROKE_PATH;
_attributes_dirty |= STROKE_COLOR;
_attributes_dirty |= STROKE_COLOR;
}
else
{
@@ -323,6 +323,16 @@ namespace canvas
}
}
/**
* Set path stroke opacity (only used if stroke is not "none")
*/
void setStrokeOpacity(float opacity)
{
_stroke_opacity =
static_cast<uint8_t>(SGMiscf::clip(opacity, 0.f, 1.f) * 255);
_attributes_dirty |= STROKE_COLOR;
}
/**
* Set stroke width
*/
@@ -390,31 +400,22 @@ namespace canvas
osg::StateAttribute const* blend_func =
state->getLastAppliedAttribute(osg::StateAttribute::BLENDFUNC);
// Initialize/Update the paint
if( _attributes_dirty & STROKE_COLOR )
{
if( _paint == VG_INVALID_HANDLE )
_paint = vgCreatePaint();
vgSetParameterfv(_paint, VG_PAINT_COLOR, 4, _stroke_color._v);
_attributes_dirty &= ~STROKE_COLOR;
}
// Initialize/update fill paint
if( _attributes_dirty & FILL_COLOR )
{
if( _paint_fill == VG_INVALID_HANDLE )
_paint_fill = vgCreatePaint();
vgSetParameterfv(_paint_fill, VG_PAINT_COLOR, 4, _fill_color._v);
_attributes_dirty &= ~FILL_COLOR;
}
// Setup paint
if( _mode & VG_STROKE_PATH )
{
// Initialize/Update the paint
if( _attributes_dirty & STROKE_COLOR )
{
if( _paint == VG_INVALID_HANDLE )
_paint = vgCreatePaint();
auto color = _stroke_color;
color.a() *= _stroke_opacity / 255.f;
vgSetParameterfv(_paint, VG_PAINT_COLOR, 4, color._v);
_attributes_dirty &= ~STROKE_COLOR;
}
vgSetPaint(_paint, VG_STROKE_PATH);
vgSetf(VG_STROKE_LINE_WIDTH, _stroke_width);
@@ -426,6 +427,19 @@ namespace canvas
}
if( _mode & VG_FILL_PATH )
{
// Initialize/update fill paint
if( _attributes_dirty & FILL_COLOR )
{
if( _paint_fill == VG_INVALID_HANDLE )
_paint_fill = vgCreatePaint();
auto color = _fill_color;
color.a() *= _fill_opacity / 255.f;
vgSetParameterfv(_paint_fill, VG_PAINT_COLOR, 4, color._v);
_attributes_dirty &= ~FILL_COLOR;
}
vgSetPaint(_paint_fill, VG_FILL_PATH);
vgSeti(VG_FILL_RULE, _fill_rule);
@@ -579,22 +593,24 @@ namespace canvas
Path *_path_element;
mutable VGPath _path;
mutable VGPaint _paint;
mutable VGPaint _paint_fill;
mutable uint32_t _attributes_dirty;
mutable VGPath _path {VG_INVALID_HANDLE};
mutable VGPaint _paint {VG_INVALID_HANDLE};
mutable VGPaint _paint_fill {VG_INVALID_HANDLE};
mutable uint32_t _attributes_dirty {~0u};
CmdList _cmds;
CoordList _coords;
VGbitfield _mode;
VGbitfield _mode {0};
osg::Vec4f _fill_color;
VGFillRule _fill_rule;
uint8_t _fill_opacity {255};
VGFillRule _fill_rule {VG_EVEN_ODD};
osg::Vec4f _stroke_color;
VGfloat _stroke_width;
uint8_t _stroke_opacity {255};
VGfloat _stroke_width {1};
std::vector<VGfloat> _stroke_dash;
VGCapStyle _stroke_linecap;
VGJoinStyle _stroke_linejoin;
VGCapStyle _stroke_linecap {VG_CAP_BUTT};
VGJoinStyle _stroke_linejoin {VG_JOIN_MITER};
osg::Vec3f transformPoint( const osg::Matrix& m,
osg::Vec2f pos ) const
@@ -670,8 +686,10 @@ namespace canvas
PathDrawableRef Path::*path = &Path::_path;
addStyle("fill", "color", &PathDrawable::setFill, path);
addStyle("fill-opacity", "numeric", &PathDrawable::setFillOpacity, path);
addStyle("fill-rule", "", &PathDrawable::setFillRule, path);
addStyle("stroke", "color", &PathDrawable::setStroke, path);
addStyle("stroke-opacity", "numeric", &PathDrawable::setStrokeOpacity, path);
addStyle("stroke-width", "numeric", &PathDrawable::setStrokeWidth, path);
addStyle("stroke-dasharray", "", &PathDrawable::setStrokeDashArray, path);
addStyle("stroke-linecap", "", &PathDrawable::setStrokeLinecap, path);
@@ -700,92 +718,69 @@ namespace canvas
}
//----------------------------------------------------------------------------
void Path::update(double dt)
{
if( _attributes_dirty & (CMDS | COORDS) )
{
_path->setSegments
(
_node->getChildValues<VGubyte, int>("cmd"),
_node->getChildValues<VGfloat, float>("coord")
);
_attributes_dirty &= ~(CMDS | COORDS);
}
// SVG path overrides manual cmd/coord specification
if ( _hasSVG && (_attributes_dirty & SVG))
{
CmdList cmds;
CoordList coords;
parseSVGPathToVGPath(_node->getStringValue("svg"), cmds, coords);
_path->setSegments(cmds, coords);
_attributes_dirty &= ~SVG;
}
if ( _hasRect &&(_attributes_dirty & RECT))
{
parseRectToVGPath();
_attributes_dirty &= ~RECT;
}
Element::update(dt);
}
//----------------------------------------------------------------------------
osg::BoundingBox Path::getTransformedBounds(const osg::Matrix& m) const
{
return _path->getTransformedBounds(m);
}
//----------------------------------------------------------------------------
Path& Path::addSegment(uint8_t cmd, std::initializer_list<float> coords)
{
_node->addChild("cmd")->setIntValue(cmd);
for(float coord: coords)
_node->addChild("coord")->setFloatValue(coord);
return *this;
}
//----------------------------------------------------------------------------
Path& Path::moveTo(float x_abs, float y_abs)
{
return addSegment(VG_MOVE_TO_ABS, x_abs, y_abs);
return addSegment(VG_MOVE_TO_ABS, {x_abs, y_abs});
}
//----------------------------------------------------------------------------
Path& Path::move(float x_rel, float y_rel)
{
return addSegment(VG_MOVE_TO_REL, x_rel, y_rel);
return addSegment(VG_MOVE_TO_REL, {x_rel, y_rel});
}
//----------------------------------------------------------------------------
Path& Path::lineTo(float x_abs, float y_abs)
{
return addSegment(VG_LINE_TO_ABS, x_abs, y_abs);
return addSegment(VG_LINE_TO_ABS, {x_abs, y_abs});
}
//----------------------------------------------------------------------------
Path& Path::line(float x_rel, float y_rel)
{
return addSegment(VG_LINE_TO_REL, x_rel, y_rel);
return addSegment(VG_LINE_TO_REL, {x_rel, y_rel});
}
//----------------------------------------------------------------------------
Path& Path::horizTo(float x_abs)
{
return addSegment(VG_HLINE_TO_ABS, x_abs);
return addSegment(VG_HLINE_TO_ABS, {x_abs});
}
//----------------------------------------------------------------------------
Path& Path::horiz(float x_rel)
{
return addSegment(VG_HLINE_TO_REL, x_rel);
return addSegment(VG_HLINE_TO_REL, {x_rel});
}
//----------------------------------------------------------------------------
Path& Path::vertTo(float y_abs)
{
return addSegment(VG_VLINE_TO_ABS, y_abs);
return addSegment(VG_VLINE_TO_ABS, {y_abs});
}
//----------------------------------------------------------------------------
Path& Path::vert(float y_rel)
{
return addSegment(VG_VLINE_TO_REL, y_rel);
return addSegment(VG_VLINE_TO_REL, {y_rel});
}
//----------------------------------------------------------------------------
@@ -828,36 +823,70 @@ namespace canvas
_node->getChild("border-radius", 1, true)->setDoubleValue(radiusY);
}
//----------------------------------------------------------------------------
void Path::updateImpl(double dt)
{
Element::updateImpl(dt);
if( _attributes_dirty & (CMDS | COORDS) )
{
_path->setSegments
(
_node->getChildValues<VGubyte, int>("cmd"),
_node->getChildValues<VGfloat, float>("coord")
);
_attributes_dirty &= ~(CMDS | COORDS);
}
// SVG path overrides manual cmd/coord specification
if( _hasSVG && (_attributes_dirty & SVG) )
{
CmdList cmds;
CoordList coords;
parseSVGPathToVGPath(_node->getStringValue("svg"), cmds, coords);
_path->setSegments(cmds, coords);
_attributes_dirty &= ~SVG;
}
if( _hasRect &&(_attributes_dirty & RECT) )
{
parseRectToVGPath();
_attributes_dirty &= ~RECT;
}
}
//----------------------------------------------------------------------------
void Path::childChanged(SGPropertyNode* child)
{
const std::string& name = child->getNameString();
const std::string &prName = child->getParent()->getNameString();
const std::string& name = child->getNameString();
const std::string &prName = child->getParent()->getNameString();
if (simgear::strutils::starts_with(name, "border-"))
{
_attributes_dirty |= RECT;
return;
}
if( strutils::starts_with(name, "border-") )
{
_attributes_dirty |= RECT;
return;
}
if (prName == "rect") {
_hasRect = true;
if (name == "left") {
_rect.setLeft(child->getDoubleValue());
} else if (name == "top") {
_rect.setTop(child->getDoubleValue());
} else if (name == "right") {
_rect.setRight(child->getDoubleValue());
} else if (name == "bottom") {
_rect.setBottom(child->getDoubleValue());
} else if (name == "width") {
_rect.setWidth(child->getDoubleValue());
} else if (name == "height") {
_rect.setHeight(child->getDoubleValue());
}
_attributes_dirty |= RECT;
return;
if (prName == "rect")
{
_hasRect = true;
if (name == "left") {
_rect.setLeft(child->getDoubleValue());
} else if (name == "top") {
_rect.setTop(child->getDoubleValue());
} else if (name == "right") {
_rect.setRight(child->getDoubleValue());
} else if (name == "bottom") {
_rect.setBottom(child->getDoubleValue());
} else if (name == "width") {
_rect.setWidth(child->getDoubleValue());
} else if (name == "height") {
_rect.setHeight(child->getDoubleValue());
}
_attributes_dirty |= RECT;
return;
}
if( child->getParent() != _node )
return;
@@ -893,67 +922,68 @@ namespace canvas
return values;
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
void operator+=(CoordList& base, const std::initializer_list<VGfloat>& other)
{
base.insert(base.end(), other.begin(), other.end());
}
void operator+=(CoordList& base, const std::initializer_list<VGfloat>& other)
{
base.insert(base.end(), other.begin(), other.end());
//----------------------------------------------------------------------------
void Path::parseRectToVGPath()
{
CmdList commands;
CoordList coords;
commands.reserve(4);
coords.reserve(8);
bool haveCorner = false;
SGVec2f topLeft = parseRectCornerRadius(_node, "left", "top", haveCorner);
if (haveCorner) {
commands.push_back(VG_MOVE_TO_ABS);
coords += {_rect.l(), _rect.t() + topLeft.y()};
commands.push_back(VG_SCCWARC_TO_REL);
coords += {topLeft.x(), topLeft.y(), 0.0, topLeft.x(), -topLeft.y()};
} else {
commands.push_back(VG_MOVE_TO_ABS);
coords += {_rect.l(), _rect.t()};
}
void Path::parseRectToVGPath()
{
CmdList commands;
CoordList coords;
commands.reserve(4);
coords.reserve(8);
bool haveCorner = false;
SGVec2f topLeft = parseRectCornerRadius(_node, "left", "top", haveCorner);
if (haveCorner) {
commands.push_back(VG_MOVE_TO_ABS);
coords += {_rect.l(), _rect.t() + topLeft.y()};
commands.push_back(VG_SCCWARC_TO_REL);
coords += {topLeft.x(), topLeft.y(), 0.0, topLeft.x(), -topLeft.y()};
} else {
commands.push_back(VG_MOVE_TO_ABS);
coords += {_rect.l(), _rect.t()};
}
SGVec2f topRight = parseRectCornerRadius(_node, "right", "top", haveCorner);
if (haveCorner) {
commands.push_back(VG_HLINE_TO_ABS);
coords += {_rect.r() - topRight.x()};
commands.push_back(VG_SCCWARC_TO_REL);
coords += {topRight.x(), topRight.y(), 0.0, topRight.x(), topRight.y()};
} else {
commands.push_back(VG_HLINE_TO_ABS);
coords += {_rect.r()};
}
SGVec2f bottomRight = parseRectCornerRadius(_node, "right", "bottom", haveCorner);
if (haveCorner) {
commands.push_back(VG_VLINE_TO_ABS);
coords += {_rect.b() - bottomRight.y()};
commands.push_back(VG_SCCWARC_TO_REL);
coords += {bottomRight.x(), bottomRight.y(), 0.0, -bottomRight.x(), bottomRight.y()};
} else {
commands.push_back(VG_VLINE_TO_ABS);
coords += {_rect.b()};
}
SGVec2f bottomLeft = parseRectCornerRadius(_node, "left", "bottom", haveCorner);
if (haveCorner) {
commands.push_back(VG_HLINE_TO_ABS);
coords += {_rect.l() + bottomLeft.x()};
commands.push_back(VG_SCCWARC_TO_REL);
coords += {bottomLeft.x(), bottomLeft.y(), 0.0, -bottomLeft.x(), -bottomLeft.y()};
} else {
commands.push_back(VG_HLINE_TO_ABS);
coords += {_rect.l()};
}
commands.push_back(VG_CLOSE_PATH);
_path->setSegments(commands, coords);
SGVec2f topRight = parseRectCornerRadius(_node, "right", "top", haveCorner);
if (haveCorner) {
commands.push_back(VG_HLINE_TO_ABS);
coords += {_rect.r() - topRight.x()};
commands.push_back(VG_SCCWARC_TO_REL);
coords += {topRight.x(), topRight.y(), 0.0, topRight.x(), topRight.y()};
} else {
commands.push_back(VG_HLINE_TO_ABS);
coords += {_rect.r()};
}
SGVec2f bottomRight = parseRectCornerRadius(_node, "right", "bottom", haveCorner);
if (haveCorner) {
commands.push_back(VG_VLINE_TO_ABS);
coords += {_rect.b() - bottomRight.y()};
commands.push_back(VG_SCCWARC_TO_REL);
coords += {bottomRight.x(), bottomRight.y(), 0.0, -bottomRight.x(), bottomRight.y()};
} else {
commands.push_back(VG_VLINE_TO_ABS);
coords += {_rect.b()};
}
SGVec2f bottomLeft = parseRectCornerRadius(_node, "left", "bottom", haveCorner);
if (haveCorner) {
commands.push_back(VG_HLINE_TO_ABS);
coords += {_rect.l() + bottomLeft.x()};
commands.push_back(VG_SCCWARC_TO_REL);
coords += {bottomLeft.x(), bottomLeft.y(), 0.0, -bottomLeft.x(), -bottomLeft.y()};
} else {
commands.push_back(VG_HLINE_TO_ABS);
coords += {_rect.l()};
}
commands.push_back(VG_CLOSE_PATH);
_path->setSegments(commands, coords);
}
} // namespace canvas
} // namespace simgear

View File

@@ -20,8 +20,8 @@
#define CANVAS_PATH_HXX_
#include "CanvasElement.hxx"
#include <boost/preprocessor/iteration/iterate.hpp>
#include <simgear/math/SGRect.hxx>
#include <initializer_list>
namespace simgear
{
@@ -40,14 +40,10 @@ namespace canvas
ElementWeakPtr parent = 0 );
virtual ~Path();
virtual void update(double dt);
virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
#define BOOST_PP_ITERATION_LIMITS (0, 6)
#define BOOST_PP_FILENAME_1 \
<simgear/canvas/elements/detail/add_segment_variadic.hxx>
#include BOOST_PP_ITERATE()
/** Add a segment with the given command and coordinates */
Path& addSegment(uint8_t cmd, std::initializer_list<float> coords = {});
/** Move path cursor */
Path& moveTo(float x_abs, float y_abs);
@@ -70,10 +66,10 @@ namespace canvas
void setSVGPath(const std::string& svgPath);
void setRect(const SGRect<float>& r);
void setRoundRect(const SGRect<float>& r, float radiusX, float radiusY = -1.0);
protected:
void setRect(const SGRectf& r);
void setRoundRect(const SGRectf& r, float radiusX, float radiusY = -1.0);
protected:
enum PathAttributes
{
CMDS = LAST_ATTRIBUTE << 1,
@@ -88,12 +84,14 @@ namespace canvas
bool _hasSVG : 1;
bool _hasRect : 1;
SGRect<float> _rect;
SGRectf _rect;
virtual void updateImpl(double dt);
void parseRectToVGPath();
virtual void childRemoved(SGPropertyNode * child);
virtual void childChanged(SGPropertyNode * child);
void parseRectToVGPath();
};
} // namespace canvas

View File

@@ -865,12 +865,12 @@ namespace canvas
//----------------------------------------------------------------------------
osg::StateSet* Text::getOrCreateStateSet()
{
if( !_transform.valid() )
return 0;
if( !_scene_group.valid() )
return nullptr;
// Only check for StateSet on Transform, as the text stateset is shared
// between all text instances using the same font (texture).
return _transform->getOrCreateStateSet();
return _scene_group->getOrCreateStateSet();
}
} // namespace canvas

View File

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

View File

@@ -190,6 +190,32 @@ namespace canvas
}
};
/**
* WebMercator projection, relative to the projection center.
* Required for Slippy Maps - i.e. openstreetmap
*/
class WebMercatorProjection:
public HorizontalProjection
{
protected:
virtual ScreenPosition project(double lat, double lon) const
{
double d_lat = lat - _ref_lat,
d_lon = lon - _ref_lon;
double r = 6378137.f / 1852; // Equatorial radius divided by ?
ScreenPosition pos;
pos.x = r * d_lon;
pos.y = r * (log(tan(d_lat) + 1.0 / cos(d_lat)));
//pos.x = lon;
//pos.y = log(tan(lat) + 1.0 / cos(lat));
return pos;
}
};
} // namespace canvas
} // namespace simgear

View File

@@ -19,7 +19,6 @@
#include <simgear_config.h>
#include "KeyboardEvent.hxx"
#include "utf8.h"
#include <osgGA/GUIEventAdapter>
@@ -42,6 +41,7 @@ namespace canvas
// TODO check Win/Mac keycode for altgr/ISO Level3 Shift
const uint32_t KEY_AltGraph = 0xfe03;
//----------------------------------------------------------------------------
KeyboardEvent::KeyboardEvent():
@@ -269,10 +269,24 @@ namespace canvas
// Empty or no mapping -> convert UTF-32 key value to UTF-8
if( _name.empty() )
{
if( !utf8::internal::is_code_point_valid(_key) )
if (( _key >= 0xd800u && _key <= 0xdfffu ) || _key > 0x10ffffu )
_name = "Unidentified";
else
utf8::unchecked::append(_key, std::back_inserter(_name));
if ( _key <= 0x7f ) {
_name.push_back(static_cast<uint8_t>(_key));
} else if ( _key <= 0x7ff ) {
_name.push_back(static_cast<uint8_t>((_key >> 6) | 0xc0));
_name.push_back(static_cast<uint8_t>((_key & 0x3f) | 0x80));
} else if ( _key <= 0xffff ) {
_name.push_back(static_cast<uint8_t>((_key >> 12) | 0xe0));
_name.push_back(static_cast<uint8_t>(((_key >> 6) & 0x3f) | 0x80));
_name.push_back(static_cast<uint8_t>((_key & 0x3f) | 0x80));
} else {
_name.push_back(static_cast<uint8_t>((_key >> 18) | 0xf0));
_name.push_back(static_cast<uint8_t>(((_key >> 12) & 0x3f) | 0x80));
_name.push_back(static_cast<uint8_t>(((_key >> 6) & 0x3f) | 0x80));
_name.push_back(static_cast<uint8_t>((_key & 0x3f) | 0x80));
}
}
// Keys on the numpad with NumLock enabled are reported just like their
@@ -307,11 +321,30 @@ namespace canvas
if( key_name.empty() )
return false;
std::string::const_iterator it = key_name.begin();
uint32_t cp = utf8::next(it, key_name.end());
// Convert the key name to the corresponding code point by checking the
// sequence length (the first bits of the first byte) and performing the
// conversion accordingly.
uint32_t cp = key_name[0] & 0xff;
size_t len;
if (cp < 0x80) {
len = 1;
} else if ((cp >> 5) == 0x6) {
cp = ((cp << 6) & 0x7ff) + (key_name[1] & 0x3f);
len = 2;
} else if ((cp >> 4) == 0xe) {
cp = ((cp << 12) & 0xffff) + (((key_name[1] & 0xff) << 6) & 0xfff)
+ (key_name[2] & 0x3f);
len = 3;
} else if ((cp >> 3) == 0x1e) {
cp = ((cp << 18) & 0x1fffff) + (((key_name[1] & 0xff) << 12) & 0x3ffff)
+ (((key_name[2] & 0xff) << 6) & 0xfff) + (key_name[3] & 0x3f);
len = 4;
} else {
return false;
}
// Check if _name contains exactly one (UTF-8 encoded) character.
if( it != key_name.end() )
if (key_name.length() > len)
return false;
// C0 and C1 control characters are not printable.

View File

@@ -45,7 +45,7 @@ namespace canvas
//----------------------------------------------------------------------------
NasalWidget::~NasalWidget()
{
onRemove();
}
//----------------------------------------------------------------------------
@@ -189,10 +189,10 @@ namespace canvas
if( hfw.empty() )
return -1;
naContext c = naNewContext();
try
{
return hfw(nasal::to_nasal(c, const_cast<NasalWidget*>(this)), w);
nasal::Context ctx;
return hfw(ctx.to_me(const_cast<NasalWidget*>(this)), w);
}
catch( std::exception const& ex )
{
@@ -202,7 +202,6 @@ namespace canvas
"NasalWidget.heightForWidth: callback error: '" << ex.what() << "'"
);
}
naFreeContext(c);
return -1;
}
@@ -262,8 +261,8 @@ namespace canvas
try
{
nasal::Context c;
_set_geometry(nasal::to_nasal(c, this), rect);
nasal::Context ctx;
_set_geometry(ctx.to_me(this), rect);
_flags &= ~LAYOUT_DIRTY;
}
catch( std::exception const& ex )

View File

@@ -95,7 +95,7 @@ const float SG_RADIANS_TO_DEGREES = 180.0f / SG_PI;
/** Value of earth radius from LaRCsim (ft) */
#define SG_EQUATORIAL_RADIUS_FT 20925650.
/** Value of earth radius from LaRCsim (meter) */
/** Value of equatorial earth radius from LaRCsim (meter) */
#define SG_EQUATORIAL_RADIUS_M 6378138.12
/** Radius squared (ft) */
@@ -104,6 +104,8 @@ const float SG_RADIANS_TO_DEGREES = 180.0f / SG_PI;
/** Radius squared (meter) */
#define SG_EQ_RAD_SQUARE_M 40680645877797.1344
/** Value of WGS84 polar earth radius (meter) */
#define SG_POLAR_RADIUS_M 6356752.3142451794975639668
// Physical Constants, SI

View File

@@ -1,7 +1,11 @@
include (SimGearComponent)
set(HEADERS EmbeddedResource.hxx EmbeddedResourceManager.hxx)
set(SOURCES EmbeddedResource.cxx EmbeddedResourceManager.cxx)
set(HEADERS EmbeddedResource.hxx
EmbeddedResourceManager.hxx
EmbeddedResourceProxy.hxx)
set(SOURCES EmbeddedResource.cxx
EmbeddedResourceManager.cxx
EmbeddedResourceProxy.cxx)
simgear_component(embedded_resources embedded_resources
"${SOURCES}" "${HEADERS}")

View File

@@ -0,0 +1,257 @@
// -*- coding: utf-8 -*-
//
// EmbeddedResourceProxy.cxx --- Unified access to real files or embedded
// resources
// Copyright (C) 2017 Florent Rougon
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA.
#include <simgear_config.h>
#include <algorithm> // std::find()
#include <ios> // std::streamsize
#include <istream>
#include <limits> // std::numeric_limits
#include <memory>
#include <vector>
#include <string>
#include <cstdlib> // std::size_t
#include <cassert>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/structure/exception.hxx>
#include "EmbeddedResourceManager.hxx"
#include "EmbeddedResourceProxy.hxx"
using std::string;
using std::vector;
using std::shared_ptr;
using std::unique_ptr;
namespace simgear
{
EmbeddedResourceProxy::EmbeddedResourceProxy(const SGPath& realRoot,
const string& virtualRoot,
bool useEmbeddedResourcesByDefault)
: _realRoot(realRoot),
_virtualRoot(normalizeVirtualRoot(virtualRoot)),
_useEmbeddedResourcesByDefault(useEmbeddedResourcesByDefault)
{ }
SGPath
EmbeddedResourceProxy::getRealRoot() const
{ return _realRoot; }
void
EmbeddedResourceProxy::setRealRoot(const SGPath& realRoot)
{ _realRoot = realRoot; }
string
EmbeddedResourceProxy::getVirtualRoot() const
{ return _virtualRoot; }
void
EmbeddedResourceProxy::setVirtualRoot(const string& virtualRoot)
{ _virtualRoot = normalizeVirtualRoot(virtualRoot); }
bool
EmbeddedResourceProxy::getUseEmbeddedResources() const
{ return _useEmbeddedResourcesByDefault; }
void
EmbeddedResourceProxy::setUseEmbeddedResources(bool useEmbeddedResources)
{ _useEmbeddedResourcesByDefault = useEmbeddedResources; }
// Static method: normalize the 'virtualRoot' argument of the constructor
//
// The argument must start with a slash and mustn't contain any '.' or '..'
// component. The return value never ends with a slash.
string
EmbeddedResourceProxy::normalizeVirtualRoot(const string& path)
{
EmbeddedResourceProxy::checkPath(__func__, path,
false /* allowStartWithColon */);
string res = path;
// Make sure 'res' doesn't end with a '/'.
while (!res.empty() && res.back() == '/') {
res.pop_back(); // This will ease path concatenation
}
return res;
}
// Static method
void
EmbeddedResourceProxy::checkPath(const string& callerMethod, const string& path,
bool allowStartWithColon)
{
if (path.empty()) {
throw sg_format_exception(
"Invalid empty path for EmbeddedResourceProxy::" +
callerMethod + "(): '" + path + "'", path);
} else if (allowStartWithColon &&
!simgear::strutils::starts_with(path, ":/") && path[0] != '/') {
throw sg_format_exception(
"Invalid path for EmbeddedResourceProxy::" + callerMethod + "(): "
"it should start with either ':/' or '/'", path);
} else if (!allowStartWithColon && path[0] != '/') {
throw sg_format_exception(
"Invalid path for EmbeddedResourceProxy::" + callerMethod + "(): "
"it should start with a slash ('/')", path);
} else {
const vector<string> components = simgear::strutils::split(path, "/");
auto find = [&components](const string& s) -> bool {
return (std::find(components.begin(), components.end(), s) !=
components.end());
};
if (find(".") || find("..")) {
throw sg_format_exception(
"Invalid path for EmbeddedResourceProxy::" + callerMethod + "(): "
"'.' and '..' components are not allowed", path);
}
}
}
unique_ptr<std::istream>
EmbeddedResourceProxy::getIStream(const string& path, bool fromEmbeddedResource)
const
{
EmbeddedResourceProxy::checkPath(__func__, path,
false /* allowStartWithColon */);
assert(!path.empty() && path.front() == '/');
if (fromEmbeddedResource) {
const auto& embeddedResMgr = simgear::EmbeddedResourceManager::instance();
return embeddedResMgr->getIStream(
_virtualRoot + path,
""); // fetch the default-locale version of the resource
} else {
const SGPath sgPath = _realRoot / path.substr(std::size_t(1));
return unique_ptr<std::istream>(new sg_ifstream(sgPath));
}
}
unique_ptr<std::istream>
EmbeddedResourceProxy::getIStream(const string& path) const
{
return getIStream(path, _useEmbeddedResourcesByDefault);
}
unique_ptr<std::istream>
EmbeddedResourceProxy::getIStreamDecideOnPrefix(const string& path) const
{
EmbeddedResourceProxy::checkPath(__func__, path,
true /* allowStartWithColon */);
// 'path' is non-empty
if (path.front() == '/') {
return getIStream(path, false /* fromEmbeddedResource */);
} else if (path.front() == ':') {
assert(path.size() >= 2 && path[1] == '/');
// Skip the leading ':'
return getIStream(path.substr(std::size_t(1)),
true /* fromEmbeddedResource */);
} else {
// The checkPath() call should make it impossible to reach this point.
std::abort();
}
}
string
EmbeddedResourceProxy::getString(const string& path, bool fromEmbeddedResource)
const
{
string result;
EmbeddedResourceProxy::checkPath(__func__, path,
false /* allowStartWithColon */);
assert(!path.empty() && path.front() == '/');
if (fromEmbeddedResource) {
const auto& embeddedResMgr = simgear::EmbeddedResourceManager::instance();
// Fetch the default-locale version of the resource
result = embeddedResMgr->getString(_virtualRoot + path, "");
} else {
const SGPath sgPath = _realRoot / path.substr(std::size_t(1));
result.reserve(sgPath.sizeInBytes());
const unique_ptr<std::istream> streamp = getIStream(path,
fromEmbeddedResource);
std::streamsize nbCharsRead;
// Allocate a buffer
static constexpr std::size_t bufSize = 65536;
static_assert(bufSize <= std::numeric_limits<std::streamsize>::max(),
"Type std::streamsize is unexpectedly small");
static_assert(bufSize <= std::numeric_limits<string::size_type>::max(),
"Type std::string::size_type is unexpectedly small");
unique_ptr<char[]> buf(new char[bufSize]);
do {
streamp->read(buf.get(), bufSize);
nbCharsRead = streamp->gcount();
if (nbCharsRead > 0) {
result.append(buf.get(), nbCharsRead);
}
} while (*streamp);
// streamp->fail() would *not* indicate an error, due to the semantics
// of std::istream::read().
if (streamp->bad()) {
throw sg_io_exception("Error reading from file", sg_location(path));
}
}
return result;
}
string
EmbeddedResourceProxy::getString(const string& path) const
{
return getString(path, _useEmbeddedResourcesByDefault);
}
string
EmbeddedResourceProxy::getStringDecideOnPrefix(const string& path) const
{
string result;
EmbeddedResourceProxy::checkPath(__func__, path,
true /* allowStartWithColon */);
// 'path' is non-empty
if (path.front() == '/') {
result = getString(path, false /* fromEmbeddedResource */);
} else if (path.front() == ':') {
assert(path.size() >= 2 && path[1] == '/');
// Skip the leading ':'
result = getString(path.substr(std::size_t(1)),
true /* fromEmbeddedResource */);
} else {
// The checkPath() call should make it impossible to reach this point.
std::abort();
}
return result;
}
} // of namespace simgear

View File

@@ -0,0 +1,155 @@
// -*- coding: utf-8 -*-
//
// EmbeddedResourceProxy.hxx --- Unified access to real files or embedded
// resources
// Copyright (C) 2017 Florent Rougon
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA.
#ifndef FG_EMBEDDEDRESOURCEPROXY_HXX
#define FG_EMBEDDEDRESOURCEPROXY_HXX
#include <istream>
#include <memory>
#include <string>
#include <simgear/misc/sg_path.hxx>
// The EmbeddedResourceProxy class allows one to access real files or embedded
// resources in a unified way. When using it, one can switch from one data
// source to the other with minimal code changes, possibly even at runtime (in
// which case there is obviously no code change at all).
//
// Sample usage of the EmbeddedResourceProxy class (from FlightGear):
//
// simgear::EmbeddedResourceProxy proxy(globals->get_fg_root(), "/FGData");
// std::string s = proxy.getString("/some/path");
// std::unique_ptr<std::istream> streamp = proxy.getIStream("/some/path");
//
// The methods getString(const std::string& path) and
// getIStream(const std::string& path) of EmbeddedResourceProxy decide whether
// to use embedded resources or real files depending on the boolean value
// passed to EmbeddedResourceProxy::setUseEmbeddedResources() (also available
// as an optional parameter to the EmbeddedResourceProxy constructor,
// defaulting to true). It is often most convenient to set this boolean once
// and then don't worry about it anymore (it is stored as a data member of
// EmbeddedResourceProxy). Otherwise, if you want to fetch resources some
// times from real files, other times from embedded resources, you may use the
// following methods:
//
// // Retrieve contents using embedded resources
// std:string s = proxy.getString("/some/path", true);
// std:string s = proxy.getStringDecideOnPrefix(":/some/path");
//
// // Retrieve contents using real files
// std:string s = proxy.getString("/some/path", false);
// std:string s = proxy.getStringDecideOnPrefix("/some/path");
//
// You can do exactly the same with EmbeddedResourceProxy::getIStream() and
// EmbeddedResourceProxy::getIStreamDecideOnPrefix(), except they return an
// std::unique_ptr<std::istream> instead of an std::string.
//
// Given how the 'proxy' object was constructed above, each of these calls
// will fetch data from either the real file $FG_ROOT/some/path or the
// embedded resource whose virtual path is '/FGData/some/path' (more
// precisely: the default-locale version of this resource).
//
// The 'path' argument of EmbeddedResourceProxy's methods getString(),
// getIStream(), getStringDecideOnPrefix() and getIStreamDecideOnPrefix()
// must:
//
// - use UTF-8 encoding;
//
// - start with:
// * either '/' or ':/' for the 'DecideOnPrefix' variants;
// * only '/' for the other methods.
//
// - have its components separated by slashes;
//
// - not contain any '.' or '..' component.
//
// For the 'DecideOnPrefix' variants:
//
// - if the path starts with a slash ('/'), a real file access is done;
//
// - if, on the other hand, it starts with ':/', EmbeddedResourceProxy uses
// the embedded resource whose virtual path is the specified path without
// its leading ':' (more precisely: the default-locale version of this
// resource).
namespace simgear
{
class EmbeddedResourceProxy
{
public:
// 'virtualRoot' must start with a '/', e.g: '/FGData'. Whether it ends
// with a '/' doesn't make a difference.
explicit EmbeddedResourceProxy(const SGPath& realRoot,
const std::string& virtualRoot,
bool useEmbeddedResourcesByDefault = true);
// Getters and setters for the corresponding data members
SGPath getRealRoot() const;
void setRealRoot(const SGPath& realRoot);
std::string getVirtualRoot() const;
void setVirtualRoot(const std::string& virtualRoot);
bool getUseEmbeddedResources() const;
void setUseEmbeddedResources(bool useEmbeddedResources);
// Get an std::istream to read from a file or from an embedded resource.
std::unique_ptr<std::istream>
getIStream(const std::string& path, bool fromEmbeddedResource) const;
std::unique_ptr<std::istream>
getIStream(const std::string& path) const;
std::unique_ptr<std::istream>
getIStreamDecideOnPrefix(const std::string& path) const;
// Get a file or embedded resource contents as a string.
std::string
getString(const std::string& path, bool fromEmbeddedResource) const;
std::string
getString(const std::string& path) const;
std::string
getStringDecideOnPrefix(const std::string& path) const;
private:
// Check that 'path' starts with either ':/' or '/', and doesn't contain any
// '..' component ('path' may only start with ':/' if 'allowStartWithColon'
// is true).
static void
checkPath(const std::string& callerMethod, const std::string& path,
bool allowStartWithColon);
// Normalize the 'virtualRoot' argument of the constructor. The argument
// must start with a '/' and mustn't contain any '.' or '..' component. The
// return value never ends with a '/'.
static std::string
normalizeVirtualRoot(const std::string& path);
SGPath _realRoot;
std::string _virtualRoot;
bool _useEmbeddedResourcesByDefault;
};
} // of namespace simgear
#endif // of FG_EMBEDDEDRESOURCEPROXY_HXX

View File

@@ -33,11 +33,14 @@
#include <cstddef> // std::size_t
#include <simgear/misc/test_macros.hxx>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/io/iostreams/CharArrayStream.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/io/iostreams/zlibstream.hxx>
#include "EmbeddedResource.hxx"
#include "EmbeddedResourceManager.hxx"
#include "EmbeddedResourceProxy.hxx"
using std::cout;
using std::cerr;
@@ -395,6 +398,106 @@ void test_getLocaleAndSelectLocale()
}
}
// Auxiliary function for test_EmbeddedResourceProxy()
void auxTest_EmbeddedResourceProxy_getIStream(unique_ptr<std::istream> iStream,
const string& contents)
{
cout << "Testing EmbeddedResourceProxy::getIStream()" << endl;
iStream->exceptions(std::ios_base::badbit);
static constexpr std::size_t bufSize = 65536;
unique_ptr<char[]> buf(new char[bufSize]); // intermediate buffer
string result;
do {
iStream->read(buf.get(), bufSize);
result.append(buf.get(), iStream->gcount());
} while (*iStream); // iStream *points* to an std::istream
// 1) If set, badbit would have caused an exception to be raised (see above).
// 2) failbit doesn't necessarily indicate an error here: it is set as soon
// as the read() call can't provide the requested number of characters.
SG_VERIFY(iStream->eof() && !iStream->bad());
SG_CHECK_EQUAL(result, contents);
}
void test_EmbeddedResourceProxy()
{
cout << "Testing the EmbeddedResourceProxy class" << endl;
// Initialize stuff we need and create two files containing the contents of
// the default-locale version of two embedded resources: those with virtual
// paths '/path/to/resource1' and '/path/to/resource2'.
const auto& resMgr = EmbeddedResourceManager::instance();
simgear::Dir tmpDir = simgear::Dir::tempDir("FlightGear");
tmpDir.setRemoveOnDestroy();
const SGPath path1 = tmpDir.path() / "resource1";
const SGPath path2 = tmpDir.path() / "resource2";
sg_ofstream out1(path1);
sg_ofstream out2(path2);
const string s1 = resMgr->getString("/path/to/resource1", "");
// To make sure in these tests that we can tell whether something came from
// a real file or from an embedded resource.
const string rs1 = s1 + " from real file";
const string rlipsum = lipsum + " from real file";
out1 << rs1;
out1.close();
if (!out1) {
throw sg_io_exception("Error writing to file", sg_location(path1));
}
out2 << rlipsum;
out2.close();
if (!out2) {
throw sg_io_exception("Error writing to file", sg_location(path2));
}
// 'proxy' defaults to using embedded resources
const simgear::EmbeddedResourceProxy proxy(tmpDir.path(), "/path/to",
/* useEmbeddedResourcesByDefault */
true);
simgear::EmbeddedResourceProxy rproxy(tmpDir.path(), "/path/to");
// 'rproxy' defaults to using real files
rproxy.setUseEmbeddedResources(false); // could be done from the ctor too
// Test EmbeddedResourceProxy::getString()
SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix("/resource1"), rs1);
SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix(":/resource1"), s1);
SG_CHECK_EQUAL(proxy.getString("/resource1", false), rs1);
SG_CHECK_EQUAL(proxy.getString("/resource1", true), s1);
SG_CHECK_EQUAL(proxy.getString("/resource1"), s1);
SG_CHECK_EQUAL(rproxy.getString("/resource1"), rs1);
SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix("/resource2"), rlipsum);
SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix(":/resource2"), lipsum);
SG_CHECK_EQUAL(proxy.getString("/resource2", false), rlipsum);
SG_CHECK_EQUAL(proxy.getString("/resource2", true), lipsum);
SG_CHECK_EQUAL(proxy.getString("/resource2"), lipsum);
SG_CHECK_EQUAL(rproxy.getString("/resource2"), rlipsum);
// Test EmbeddedResourceProxy::getIStream()
auxTest_EmbeddedResourceProxy_getIStream(
proxy.getIStreamDecideOnPrefix("/resource1"),
rs1);
auxTest_EmbeddedResourceProxy_getIStream(
proxy.getIStreamDecideOnPrefix(":/resource1"),
s1);
auxTest_EmbeddedResourceProxy_getIStream(proxy.getIStream("/resource1"), s1);
auxTest_EmbeddedResourceProxy_getIStream(rproxy.getIStream("/resource1"), rs1);
auxTest_EmbeddedResourceProxy_getIStream(proxy.getIStream("/resource2", false),
rlipsum);
auxTest_EmbeddedResourceProxy_getIStream(proxy.getIStream("/resource2", true),
lipsum);
auxTest_EmbeddedResourceProxy_getIStream(proxy.getIStream("/resource2"),
lipsum);
auxTest_EmbeddedResourceProxy_getIStream(rproxy.getIStream("/resource2"),
rlipsum);
}
int main(int argc, char **argv)
{
// Initialize the EmbeddedResourceManager instance, add a few resources
@@ -407,6 +510,7 @@ int main(int argc, char **argv)
test_addAlreadyExistingResource();
test_localeDependencyOfResourceFetching();
test_getLocaleAndSelectLocale();
test_EmbeddedResourceProxy();
return EXIT_SUCCESS;
}

View File

@@ -21,4 +21,9 @@ else()
endif()
add_test(metar ${EXECUTABLE_OUTPUT_PATH}/test_metar)
add_executable(test_precipitation test_precipitation.cxx)
target_link_libraries(test_precipitation ${TEST_LIBS})
add_test(precipitation ${EXECUTABLE_OUTPUT_PATH}/test_precipitation)
endif(ENABLE_TESTS)

View File

@@ -7,7 +7,7 @@
*
* @brief Precipitation effects to draw rain and snow.
*
* @par Licences
* @par License
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
@@ -25,7 +25,6 @@
*/
#include "precipitation.hxx"
//#include "visual_enviro.hxx"
#include <simgear/constants.h>
#include <osg/ClipNode>
@@ -65,7 +64,7 @@ bool SGPrecipitation::getEnabled() const
*/
osg::Group* SGPrecipitation::build(void)
{
osg::Group* group = new osg::Group;
osg::ref_ptr<osg::Group> group = new osg::Group;
_precipitationEffect->snow(0);
_precipitationEffect->rain(0);
@@ -73,8 +72,9 @@ osg::Group* SGPrecipitation::build(void)
if (_clip_distance!=0.0)
{
osg::ref_ptr<osg::ClipNode> clipNode = new osg::ClipNode;
clipNode->addClipPlane( new osg::ClipPlane( 0 ) );
clipNode->getClipPlane(0)->setClipPlane( 0.0, 0.0, -1.0, -_clip_distance );
osg::ref_ptr<osg::ClipPlane> clipPlane = new osg::ClipPlane(0);
clipNode->addClipPlane(clipPlane.get());
clipNode->getClipPlane(0)->setClipPlane(0.0, 0.0, -1.0, -_clip_distance);
clipNode->setReferenceFrame(osg::ClipNode::ABSOLUTE_RF);
clipNode->addChild(_precipitationEffect.get());
@@ -87,7 +87,7 @@ osg::Group* SGPrecipitation::build(void)
group->setNodeMask( ~(simgear::CASTSHADOW_BIT | simgear::MODELLIGHT_BIT) );
return group;
return group.release();
}
@@ -140,8 +140,8 @@ void SGPrecipitation::setRainDropletSize(float size)
/**
* @brief Define the illumination multiplier
*
* This function permits you to define and change the rain droplet size
* which is used if external droplet size control is enabled
* This function permits you to define and change the brightness
* of the precipitation.
*/
void SGPrecipitation::setIllumination(float illumination)
@@ -163,10 +163,9 @@ void SGPrecipitation::setSnowFlakeSize(float size)
/**
* @brief Define the rain droplet size
* @brief Define the clip plane distance
*
* This function permits you to define and change the rain droplet size
* which is used if external droplet size control is enabled
* This function permits you to define and change the clip plane distance.
*/
void SGPrecipitation::setClipDistance(float distance)
@@ -224,7 +223,7 @@ void SGPrecipitation::setWindProperty(double heading, double speed)
* Be careful, if snow and rain intensity are greater than '0', snow effect
* will be first.
*
* The settings come from the osgParticule/PrecipitationEffect.cpp exemple.
* The settings come from the osgParticle/PrecipitationEffect.cpp example.
*/
bool SGPrecipitation::update(void)
{

View File

@@ -34,7 +34,7 @@
class SGPrecipitation : public osg::Referenced
{
private:
protected:
bool _freeze;
bool _enabled;
bool _droplet_external;
@@ -66,7 +66,7 @@ public:
void setIllumination(float);
void setClipDistance(float);
void setEnabled( bool );
void setEnabled(bool);
bool getEnabled() const;
};

View File

@@ -0,0 +1,239 @@
/**************************************************************************
* test_precipitation.cxx -- unit-tests for SGPrecipitation class
*
* Copyright (C) 2017 Scott Giese (xDraconian) - <scttgs0@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Id$
**************************************************************************/
#include <cmath>
#include <memory>
#include <osg/Group>
#include <simgear/constants.h>
#include <simgear/misc/test_macros.hxx>
#include "precipitation.hxx"
using std::cout;
using std::cerr;
using std::endl;
class SGPrecipitationTestFixture : public SGPrecipitation
{
public:
osg::ref_ptr<osg::Group> _group;
void test_configuration()
{
_group = build();
SG_VERIFY(getEnabled());
SG_VERIFY(_precipitationEffect);
SG_CHECK_EQUAL_EP(_rain_intensity, 0.0f);
SG_CHECK_EQUAL_EP(_snow_intensity, 0.0f);
SG_VERIFY(!_freeze);
}
void test_rain()
{
int count = 500;
do {
if (--count == 0) {
cerr << "error: method setRainIntensity took too long.";
exit(EXIT_FAILURE);
}
float saveRainIntensity = _rain_intensity;
setRainIntensity(0.4f);
update();
SG_CHECK_GE(_rain_intensity, saveRainIntensity);
} while (std::fabs(_rain_intensity - 0.4f) > 0.00001f);
SG_CHECK_EQUAL_EP2(_rain_intensity, 0.4f, 0.00001f);
}
void test_rain_external()
{
int count = 500;
do {
if (--count == 0) {
cerr << "error: method setRainIntensity-external took too long.";
exit(EXIT_FAILURE);
}
float saveRainIntensity = _rain_intensity;
setRainIntensity(0.25f);
update();
SG_CHECK_LE(_rain_intensity, saveRainIntensity);
} while (std::fabs(_rain_intensity - 0.25f) > 0.00001f);
// call once more to ensure we have intensity precisely set
setRainIntensity(0.25f);
update();
SG_CHECK_EQUAL_EP2(_rain_intensity, 0.25f, 0.00001f);
}
void test_freeze()
{
float saveParticleSize = _precipitationEffect->getParticleSize();
float saveParticleSpeed = std::fabs(_precipitationEffect->getParticleSpeed());
// change rain to snow
// expect particle size to increase
// expect particle speed to decrease
setFreezing(true);
update();
SG_CHECK_GT(_precipitationEffect->getParticleSize(), saveParticleSize);
SG_CHECK_LT(std::fabs(_precipitationEffect->getParticleSpeed()), saveParticleSpeed);
}
void test_snow()
{
int count = 500;
do {
if (--count == 0) {
cerr << "error: method setSnowIntensity took too long.";
exit(EXIT_FAILURE);
}
float saveSnowIntensity = _snow_intensity;
// not a typo - when freezing is enabled, snow intensity is keep in sync with rain intensity
setRainIntensity(0.3f);
update();
SG_CHECK_LE(_snow_intensity, saveSnowIntensity);
} while (std::fabs(_snow_intensity - 0.3f) > 0.00001f);
SG_CHECK_EQUAL_EP2(_snow_intensity, 0.3f, 0.00001f);
}
void test_snow_external()
{
setRainDropletSize(0.025f);
setSnowFlakeSize(0.04f);
setDropletExternal(true);
int count = 600;
do {
if (--count == 0) {
cerr << "error: method setSnowIntensity-external took too long.";
exit(EXIT_FAILURE);
}
float saveSnowIntensity = _snow_intensity;
setSnowIntensity(0.55f);
update();
SG_CHECK_GE(_snow_intensity, saveSnowIntensity);
} while (std::fabs(_snow_intensity - 0.55f) > 0.00001f);
// call once more to ensure we have intensity precisely set
setSnowIntensity(0.55f);
update();
SG_CHECK_EQUAL_EP2(_snow_intensity, 0.55f, 0.00001f);
}
void test_unfreeze()
{
float saveParticleSize = _precipitationEffect->getParticleSize();
float saveParticleSpeed = std::fabs(_precipitationEffect->getParticleSpeed());
// change snow to rain
// expect particle size to decrease
// expect particle speed to increase
setFreezing(false);
update();
SG_CHECK_LT(_precipitationEffect->getParticleSize(), saveParticleSize);
SG_CHECK_GT(std::fabs(_precipitationEffect->getParticleSpeed()), saveParticleSpeed);
}
void test_no_precipitation()
{
setEnabled(false);
SG_VERIFY(!getEnabled());
update();
// intensity drops to zero, so we should see this reflected in the particles
SG_CHECK_EQUAL_EP(_precipitationEffect->getParticleSize(), 0.01f);
SG_CHECK_EQUAL_EP(_precipitationEffect->getParticleSpeed(), -2.0f);
setEnabled(true);
}
void test_illumination()
{
setIllumination(0.87f);
SG_CHECK_EQUAL_EP(_illumination, 0.87f);
}
void test_clipping()
{
setClipDistance(6.5);
SG_CHECK_EQUAL_EP(_clip_distance, 6.5f);
}
void test_wind()
{
setWindProperty(87.0f, 0.7f);
auto vec = _wind_vec;
SG_CHECK_EQUAL_EP2(vec[0], 0.011166f, 0.00001f);
SG_CHECK_EQUAL_EP2(vec[1], -0.213068f, 0.00001f);
SG_CHECK_EQUAL_EP2(vec[2], 0.0f, 0.00001f);
}
};
int main(int argc, char* argv[])
{
auto fixture = std::unique_ptr<SGPrecipitationTestFixture>(new SGPrecipitationTestFixture);
fixture->test_configuration();
fixture->test_rain();
fixture->test_freeze();
fixture->test_snow();
fixture->test_unfreeze();
fixture->test_no_precipitation();
fixture->test_snow_external();
fixture->test_rain_external();
fixture->test_illumination();
fixture->test_clipping();
fixture->test_wind();
fixture->test_no_precipitation();
cout << "all tests passed OK" << endl;
return EXIT_SUCCESS;
}

View File

@@ -19,6 +19,8 @@
# include <simgear_config.h>
#endif
#include <algorithm>
#include <simgear/compiler.h>
#include "HLAArrayDataType.hxx"

View File

@@ -19,6 +19,8 @@
# include <simgear_config.h>
#endif
#include <algorithm>
#include <simgear/compiler.h>
#include "HLADataType.hxx"

View File

@@ -21,11 +21,11 @@
#include <simgear/compiler.h>
#include "HLAEnumeratedDataType.hxx"
#include <algorithm>
#include <map>
#include <sstream>
#include <vector>
#include "HLAEnumeratedDataType.hxx"
#include "HLADataTypeVisitor.hxx"

View File

@@ -19,10 +19,11 @@
# include <simgear_config.h>
#endif
#include <algorithm>
#include <simgear/compiler.h>
#include "HLAFixedRecordDataType.hxx"
#include "HLADataTypeVisitor.hxx"
#include "HLAFixedRecordDataElement.hxx"

View File

@@ -19,12 +19,12 @@
# include <simgear_config.h>
#endif
#include <algorithm>
#include <simgear/compiler.h>
#include "HLAInteractionClass.hxx"
#include <simgear/debug/logstream.hxx>
#include "HLAInteractionClass.hxx"
#include "HLADataElement.hxx"
#include "HLAFederate.hxx"

View File

@@ -19,6 +19,8 @@
# include <simgear_config.h>
#endif
#include <algorithm>
#include <simgear/compiler.h>
#include "HLAObjectClass.hxx"

View File

@@ -19,10 +19,11 @@
# include <simgear_config.h>
#endif
#include <algorithm>
#include <simgear/compiler.h>
#include "HLAVariantRecordDataType.hxx"
#include "HLADataTypeVisitor.hxx"
#include "HLAVariantRecordDataElement.hxx"

View File

@@ -22,9 +22,13 @@
//
#include <simgear_config.h>
#include <algorithm>
#include "DNSClient.hxx"
#include <udns.h>
#include <time.h>
#include <ctime>
#include <simgear/debug/logstream.hxx>
namespace simgear {

View File

@@ -142,7 +142,7 @@ public:
void finishedRequest(const RepoRequestPtr& req);
HTTPDirectory* getOrCreateDirectory(const std::string& path);
bool deleteDirectory(const std::string& path);
bool deleteDirectory(const std::string& relPath, const SGPath& absPath);
typedef std::vector<HTTPDirectory*> DirectoryVector;
DirectoryVector directories;
@@ -317,7 +317,8 @@ public:
ChildInfoList::iterator c = findIndexChild(it->file());
if (c == children.end()) {
SG_LOG(SG_TERRASYNC, SG_DEBUG, "is orphan '" << it->file() << "'" );
orphans.push_back(it->file());
orphans.push_back(it->file());
} else if (c->hash != hash) {
SG_LOG(SG_TERRASYNC, SG_DEBUG, "hash mismatch'" << it->file() );
// file exists, but hash mismatch, schedule update
@@ -534,7 +535,7 @@ private:
std::string fpath = _relativePath + "/" + name;
if (p.isDir()) {
ok = _repository->deleteDirectory(fpath);
ok = _repository->deleteDirectory(fpath, p);
} else {
// remove the hash cache entry
_repository->updatedFileContents(p, std::string());
@@ -1044,25 +1045,26 @@ HTTPRepository::failure() const
return d;
}
bool HTTPRepoPrivate::deleteDirectory(const std::string& path)
bool HTTPRepoPrivate::deleteDirectory(const std::string& relPath, const SGPath& absPath)
{
DirectoryWithPath p(path);
DirectoryVector::iterator it = std::find_if(directories.begin(), directories.end(), p);
DirectoryWithPath p(relPath);
auto it = std::find_if(directories.begin(), directories.end(), p);
if (it != directories.end()) {
HTTPDirectory* d = *it;
assert(d->absolutePath() == absPath);
directories.erase(it);
Dir dir(d->absolutePath());
bool result = dir.remove(true);
// update the hash cache too
updatedFileContents(d->absolutePath(), std::string());
delete d;
} else {
// we encounter this code path when deleting an orphaned directory
}
Dir dir(absPath);
bool result = dir.remove(true);
return result;
}
// update the hash cache too
updatedFileContents(absPath, std::string());
return false;
return result;
}
void HTTPRepoPrivate::makeRequest(RepoRequestPtr req)

View File

@@ -38,20 +38,17 @@
#include <zlib.h>
#include <simgear/debug/logstream.hxx>
#include <simgear/io/iostreams/zlibstream.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/sg_inlines.h>
#include <simgear/structure/exception.hxx>
#include <simgear/debug/logstream.hxx>
using std::string;
using traits = std::char_traits<char>;
using simgear::enumValue;
// Cast an enum value to its underlying type
template <typename T>
static constexpr typename std::underlying_type<T>::type enumValue(T e) {
return static_cast<typename std::underlying_type<T>::type>(e);
}
using traits = std::char_traits<char>;
// Private utility function
static string zlibErrorMessage(const z_stream& zstream, int errorCode)

View File

@@ -32,10 +32,12 @@
#include <simgear_config.h>
#include "sg_netChannel.hxx"
#include <algorithm>
#include <memory>
#include <cassert>
#include <cerrno>
#include <cstring>
#include <errno.h>
#include <simgear/debug/logstream.hxx>
@@ -258,18 +260,10 @@ NetChannelPoller::removeChannel(NetChannel* channel)
assert(channel);
assert(channel->poller == this);
channel->poller = NULL;
// portability: MSVC throws assertion failure when empty
if (channels.empty()) {
return;
}
ChannelList::iterator it = channels.begin();
for (; it != channels.end(); ++it) {
if (*it == channel) {
channels.erase(it);
return;
}
auto it = std::find(channels.begin(), channels.end(), channel);
if (it != channels.end()) {
channels.erase(it);
}
}

View File

@@ -183,15 +183,6 @@ public:
int requestContentLength;
};
class EraseIfClosed
{
public:
bool operator()(simgear::NetChannel* chan) const
{
return chan->isClosed();
}
};
template <class T>
class TestServer : public NetChannel
{
@@ -202,7 +193,6 @@ public:
{
Socket::initSockets();
open();
bind(NULL, 2000); // localhost, any port
listen(16);
@@ -212,6 +202,7 @@ public:
virtual ~TestServer()
{
_poller.removeChannel(this);
}
virtual bool writable (void) { return false ; }
@@ -231,15 +222,16 @@ public:
{
_poller.poll();
typename std::vector<T*>::iterator it;
it = std::remove_if(_channels.begin(), _channels.end(), EraseIfClosed());
for (typename std::vector<T*>::iterator it2 = it; it2 != _channels.end(); ++it2) {
delete *it2;
}
auto it = std::remove_if(_channels.begin(), _channels.end(), [&](T* channel) {
if (channel->isClosed()) {
_poller.removeChannel(channel);
delete channel;
return true;
}
return false;
});
_channels.erase(it, _channels.end());
}
int connectCount()

View File

@@ -33,6 +33,7 @@ void testTarGz()
SG_VERIFY(TarExtractor::isTarData(buf, bufSize));
f.close();
}
void testPlainTar()
@@ -48,12 +49,13 @@ void testPlainTar()
SG_VERIFY(TarExtractor::isTarData(buf, bufSize));
f.close();
}
int main (int ac, char ** av)
int main(int ac, char ** av)
{
testTarGz();
testPlainTar();
return 0;
return 0;
}

View File

@@ -28,6 +28,7 @@
#include <zlib.h>
#include <simgear/sg_inlines.h>
#include <simgear/io/sg_file.hxx>
#include <simgear/misc/sg_dir.hxx>
@@ -92,7 +93,8 @@ public:
END_OF_ARCHIVE,
ERROR_STATE, ///< states above this are error conditions
BAD_ARCHIVE,
BAD_DATA
BAD_DATA,
FILTER_STOPPED
} State;
SGPath path;
@@ -110,10 +112,13 @@ public:
bool haveInitedZLib;
bool uncompressedData; // set if reading a plain .tar (not tar.gz)
uint8_t* headerPtr;
TarExtractorPrivate() :
TarExtractor* outer;
bool skipCurrentEntry = false;
TarExtractorPrivate(TarExtractor* o) :
haveInitedZLib(false),
uncompressedData(false)
uncompressedData(false),
outer(o)
{
}
@@ -129,7 +134,10 @@ public:
}
if (state == READING_FILE) {
currentFile->close();
if (currentFile) {
currentFile->close();
currentFile.reset();
}
size_t pad = currentFileSize % TAR_HEADER_BLOCK_SIZE;
if (pad) {
bytesRemaining = TAR_HEADER_BLOCK_SIZE - pad;
@@ -177,26 +185,36 @@ public:
return;
}
skipCurrentEntry = false;
std::string tarPath = std::string(header.prefix) + std::string(header.fileName);
if (!isSafePath(tarPath)) {
//state = BAD_ARCHIVE;
SG_LOG(SG_IO, SG_WARN, "bad tar path:" << tarPath);
//return;
skipCurrentEntry = true;
}
SGPath p = path;
p.append(tarPath);
auto result = outer->filterPath(tarPath);
if (result == TarExtractor::Stop) {
setState(FILTER_STOPPED);
return;
} else if (result == TarExtractor::Skipped) {
skipCurrentEntry = true;
}
SGPath p = path / tarPath;
if (header.typeflag == DIRTYPE) {
Dir dir(p);
dir.create(0755);
if (!skipCurrentEntry) {
Dir dir(p);
dir.create(0755);
}
setState(READING_HEADER);
} else if ((header.typeflag == REGTYPE) || (header.typeflag == AREGTYPE)) {
currentFileSize = ::strtol(header.size, NULL, 8);
bytesRemaining = currentFileSize;
currentFile.reset(new SGBinaryFile(p));
currentFile->open(SG_IO_OUT);
if (!skipCurrentEntry) {
currentFile.reset(new SGBinaryFile(p));
currentFile->open(SG_IO_OUT);
}
setState(READING_FILE);
} else {
SG_LOG(SG_IO, SG_WARN, "Unsupported tar file type:" << header.typeflag);
@@ -212,7 +230,9 @@ public:
size_t curBytes = std::min(bytesRemaining, count);
if (state == READING_FILE) {
currentFile->write(bytes, curBytes);
if (currentFile) {
currentFile->write(bytes, curBytes);
}
bytesRemaining -= curBytes;
} else if ((state == READING_HEADER) || (state == PRE_END_OF_ARCHVE) || (state == END_OF_ARCHIVE)) {
memcpy(headerPtr, bytes, curBytes);
@@ -264,7 +284,7 @@ public:
};
TarExtractor::TarExtractor(const SGPath& rootPath) :
d(new TarExtractorPrivate)
d(new TarExtractorPrivate(this))
{
d->path = rootPath;
@@ -383,22 +403,26 @@ bool TarExtractor::isTarData(const uint8_t* bytes, size_t count)
z.avail_in = count;
if (inflateInit2(&z, ZLIB_INFLATE_WINDOW_BITS | ZLIB_DECODE_GZIP_HEADER) != Z_OK) {
inflateEnd(&z);
return false;
}
int result = inflate(&z, Z_SYNC_FLUSH);
if (result != Z_OK) {
SG_LOG(SG_IO, SG_WARN, "inflate failed:" << result);
inflateEnd(&z);
return false; // not tar data
}
size_t written = 4096 - z.avail_out;
if (written < TAR_HEADER_BLOCK_SIZE) {
SG_LOG(SG_IO, SG_WARN, "insufficient data for header");
inflateEnd(&z);
return false;
}
header = reinterpret_cast<UstarHeaderBlock*>(zlibOutput);
inflateEnd(&z);
} else {
// uncompressed tar
if (count < TAR_HEADER_BLOCK_SIZE) {
@@ -417,4 +441,11 @@ bool TarExtractor::isTarData(const uint8_t* bytes, size_t count)
return true;
}
auto TarExtractor::filterPath(std::string& pathToExtract)
-> PathResult
{
SG_UNUSED(pathToExtract);
return Accepted;
}
} // of simgear

View File

@@ -43,7 +43,17 @@ public:
bool hasError() const;
protected:
enum PathResult {
Accepted,
Skipped,
Modified,
Stop
};
virtual PathResult filterPath(std::string& pathToExtract);
private:
friend class TarExtractorPrivate;
std::unique_ptr<TarExtractorPrivate> d;
};

View File

@@ -155,6 +155,12 @@ public:
{
return std::isnan(v);
}
static bool eq(const T& a, const T& b, const T& epsilon = SGLimits<T>::epsilon())
{ return std::abs(a - b) < epsilon; }
static bool neq(const T& a, const T& b, const T& epsilon = SGLimits<T>::epsilon())
{ return !eq(a, b, epsilon); }
};
#endif

View File

@@ -2,7 +2,11 @@
#include <simgear/compiler.h>
#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>
#include <cstdlib>
#include <cstring>
@@ -286,6 +290,49 @@ void test_permissions()
SG_CHECK_EQUAL(fileInRW.canWrite(), false);
}
void test_comparisons()
{
std::cout << "Testing comparisons\n";
SG_CHECK_EQUAL(SGPath("/abc/def ghi"), SGPath("/abc/def ghi"));
SG_CHECK_NE(SGPath("/abc"), SGPath("abc"));
SG_CHECK_LT(SGPath(""), SGPath("/"));
SG_CHECK_LT(SGPath("A"), SGPath("a"));
SG_CHECK_LE(SGPath(""), SGPath("/"));
SG_CHECK_LE(SGPath("/"), SGPath("/"));
SG_CHECK_GT(SGPath("a"), SGPath("A"));
SG_CHECK_GE(SGPath("a"), SGPath("A"));
SG_CHECK_GE(SGPath("a"), SGPath("a"));
std::vector<SGPath> origVector({
std::string("/zer/gh/tr aze"),
std::string("/abc/def/ttt"),
std::string("/abc/def/ddd"),
std::string("/a"),
std::string("")});
std::vector<SGPath> sortedVector({
std::string(""),
std::string("/a"),
std::string("/abc/def/ddd"),
std::string("/abc/def/ttt"),
std::string("/zer/gh/tr aze")});
std::sort(origVector.begin(), origVector.end());
SG_CHECK_EQUAL_NOSTREAM(origVector, sortedVector);
}
void test_hash_function()
{
std::cout << "Testing the std::hash<SGPath> specialization\n";
const SGPath nullPath{};
const SGPath p{"/abc/def"};
SG_CHECK_EQUAL(std::hash<SGPath>{}(nullPath), std::hash<SGPath>{}(nullPath));
SG_CHECK_EQUAL(std::hash<SGPath>{}(p), std::hash<SGPath>{}(p));
SG_CHECK_NE(std::hash<SGPath>{}(p), std::hash<SGPath>{}(p / "foobar"));
}
int main(int argc, char* argv[])
{
SGPath pa;
@@ -389,12 +436,11 @@ int main(int argc, char* argv[])
SG_CHECK_EQUAL(pp.canWrite(), false);
test_dir();
test_path_dir();
test_path_dir();
test_permissions();
test_update_dir();
test_update_dir();
test_comparisons();
test_hash_function();
cout << "all tests passed OK" << endl;
return 0; // passed

View File

@@ -296,7 +296,10 @@ bool Dir::isEmpty() const
int n = 0;
dirent* d;
while( (d = readdir(dp)) !=NULL && (n < 4) ) n++;
while (n < 3 && (d = readdir(dp)) != nullptr) {
n++;
}
closedir(dp);
return (n == 2); // '.' and '..' always exist

View File

@@ -2,6 +2,7 @@
#include <cstdlib>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/test_macros.hxx>
#include "sg_dir.hxx"
@@ -34,11 +35,35 @@ void test_tempDir()
d.remove();
}
void test_isEmpty()
{
simgear::Dir d = simgear::Dir::tempDir("FlightGear");
SG_VERIFY(!d.isNull() && d.exists() && d.isEmpty());
SGPath f = d.file("some file");
{ sg_ofstream file(f); } // create and close the file
SG_VERIFY(!d.isEmpty());
f.remove();
SG_VERIFY(d.isEmpty());
simgear::Dir subDir{d.file("some subdir")};
subDir.create(0777);
SG_VERIFY(!d.isEmpty());
subDir.remove();
SG_VERIFY(d.isEmpty());
d.remove();
SG_VERIFY(d.isEmpty()); // eek, but that's how it is
}
int main(int argc, char **argv)
{
test_isNull();
test_setRemoveOnDestroy();
test_tempDir();
test_isEmpty();
return EXIT_SUCCESS;
}

View File

@@ -35,9 +35,16 @@
#include <fstream>
#include <cstdlib>
#ifdef _WIN32
# include <direct.h>
#if !defined(SG_WINDOWS)
# include <sys/types.h>
# include <utime.h>
#endif
#if defined(SG_WINDOWS)
# include <direct.h>
# include <sys/utime.h>
#endif
#include "sg_path.hxx"
#include <boost/algorithm/string/case_conv.hpp>
@@ -52,13 +59,13 @@ using simgear::strutils::starts_with;
static const char sgDirPathSep = '/';
static const char sgDirPathSepBad = '\\';
#ifdef _WIN32
#if defined(SG_WINDOWS)
const char SGPath::pathListSep[] = ";"; // this is null-terminated
#else
const char SGPath::pathListSep[] = ":"; // ditto
#endif
#ifdef _WIN32
#if defined(SG_WINDOWS)
#include <ShlObj.h> // for CSIDL
// TODO: replace this include file with the official <versionhelpers.h> header
// included in the Windows 8.1 SDK
@@ -240,44 +247,6 @@ SGPath SGPath::fromUtf8(const std::string& bytes, PermissionChecker p)
return SGPath(bytes, p);
}
SGPath::SGPath(const SGPath& p) :
path(p.path),
_permission_checker(p._permission_checker),
_cached(p._cached),
_rwCached(p._rwCached),
_cacheEnabled(p._cacheEnabled),
_canRead(p._canRead),
_canWrite(p._canWrite),
_exists(p._exists),
_isDir(p._isDir),
_isFile(p._isFile),
_modTime(p._modTime),
_size(p._size)
{
}
SGPath& SGPath::operator=(const SGPath& p)
{
path = p.path;
_permission_checker = p._permission_checker,
_cached = p._cached;
_rwCached = p._rwCached;
_cacheEnabled = p._cacheEnabled;
_canRead = p._canRead;
_canWrite = p._canWrite;
_exists = p._exists;
_isDir = p._isDir;
_isFile = p._isFile;
_modTime = p._modTime;
_size = p._size;
return *this;
}
// destructor
SGPath::~SGPath() {
}
// set path
void SGPath::set( const string& p ) {
path = p;
@@ -780,6 +749,18 @@ bool SGPath::operator!=(const SGPath& other) const
return (path != other.path);
}
bool operator<(const SGPath& lhs, const SGPath& rhs)
{ return lhs.path < rhs.path; }
bool operator>(const SGPath& lhs, const SGPath& rhs)
{ return operator<(rhs, lhs); }
bool operator<=(const SGPath& lhs, const SGPath& rhs)
{ return !operator>(lhs, rhs); }
bool operator>=(const SGPath& lhs, const SGPath& rhs)
{ return !operator<(lhs, rhs); }
//------------------------------------------------------------------------------
bool SGPath::rename(const SGPath& newName)
{
@@ -1074,3 +1055,40 @@ std::string SGPath::fileUrl() const
return {};
}
}
//------------------------------------------------------------------------------
bool SGPath::touch()
{
if (!permissionsAllowsWrite())
{
SG_LOG(SG_IO, SG_WARN, "file touch failed: (" << *this << ")"
" reason: access denied" );
return false;
}
if (!exists()) {
SG_LOG(SG_IO, SG_WARN, "file touch failed: (" << *this << ")"
" reason: missing file");
return false;
}
#if defined(SG_WINDOWS)
auto ws = wstr();
// set this link for docs on behaviour here, about passing nullptr
// https://msdn.microsoft.com/en-us/library/aa273399(v=vs.60).aspx
if (_wutime(ws.c_str(), nullptr) != 0) {
SG_LOG(SG_IO, SG_WARN, "file touch failed: (" << *this << ")"
" reason: _wutime failed with error:" << simgear::strutils::error_string(errno));
return false;
}
#else
if (::utime(path.c_str(), nullptr) != 0) {
SG_LOG(SG_IO, SG_WARN, "file touch failed: (" << *this << ")"
" reason: utime failed with error:" << simgear::strutils::error_string(errno));
return false;
}
#endif
// reset the cache flag so we re-stat() on next request
_cached = false;
return true;
}

View File

@@ -28,12 +28,14 @@
#ifndef _SG_PATH_HXX
#define _SG_PATH_HXX
#include <functional>
#include <string>
#include <cstdlib>
#include <ctime>
#include <sys/types.h>
#include <simgear/compiler.h>
#include <string>
#include <ctime>
#include <simgear/math/sg_types.hxx>
#ifdef _MSC_VER
@@ -65,11 +67,6 @@ public:
/** Default constructor */
explicit SGPath(PermissionChecker validator = NULL);
/** Copy contructor */
SGPath(const SGPath& p);
SGPath& operator=(const SGPath& p);
/**
* Construct a path based on the starting path provided.
* @param p initial path
@@ -87,9 +84,6 @@ public:
const std::string& r,
PermissionChecker validator = NULL );
/** Destructor */
~SGPath();
/**
* Set path to a new value
* @param p new path
@@ -99,6 +93,8 @@ public:
bool operator==(const SGPath& other) const;
bool operator!=(const SGPath& other) const;
// Other comparison operators are declared below
friend bool operator<(const SGPath& lhs, const SGPath& rhs);
void setPermissionChecker(PermissionChecker validator);
PermissionChecker getPermissionChecker() const;
@@ -190,8 +186,8 @@ public:
* Get the path string
* @return path string
*/
std::string str() const { return path; }
std::string utf8Str() const { return path; }
std::string str() const noexcept { return path; }
std::string utf8Str() const noexcept { return path; }
std::string local8BitStr() const;
@@ -286,6 +282,13 @@ public:
*/
std::string fileUrl() const;
/**
* Update the file modification timestamp to be 'now'. The contents will
* not be changed. (Same as POSIX 'touch' command). Will fail if the file
* does not exist or permissions do not allow writing.
*/
bool touch();
enum StandardLocation
{
HOME,
@@ -357,6 +360,24 @@ private:
mutable size_t _size;
};
// Other comparison operators are in the class definition block
bool operator> (const SGPath& lhs, const SGPath& rhs);
bool operator<=(const SGPath& lhs, const SGPath& rhs);
bool operator>=(const SGPath& lhs, const SGPath& rhs);
// Hash function for SGPath
namespace std
{
template<>
struct hash<SGPath>
{
std::size_t operator()(const SGPath& path) const noexcept
{
return std::hash<std::string>{}(path.utf8Str());
}
};
} // of namespace std
/// Output to an ostream
template<typename char_type, typename traits_type>
inline

View File

@@ -29,10 +29,7 @@
#include <cstring> // strerror_r() and strerror_s()
#include <cctype>
#include <cerrno>
#if defined(HAVE_CPP11_CODECVT)
#include <codecvt> // new in C++11
#endif
#include <cassert>
#include "strutils.hxx"
@@ -44,6 +41,8 @@
#if defined(SG_WINDOWS)
#include <windows.h>
#include <codecvt>
#include <locale>
#endif
using std::string;
@@ -538,6 +537,33 @@ namespace simgear {
unsigned long long readNonNegativeInt<unsigned long long, 16>(
const std::string& s);
#endif
// parse a time string ([+/-]%f[:%f[:%f]]) into hours
double readTime(const string& time_in)
{
if (time_in.empty()) {
return 0.0;
}
const bool negativeSign = time_in.front() == '-';
const string_list pieces = split(time_in, ":");
if (pieces.size() > 3) {
throw sg_format_exception("Unable to parse time string, too many pieces", time_in);
}
const int hours = std::abs(to_int(pieces.front()));
int minutes = 0, seconds = 0;
if (pieces.size() > 1) {
minutes = to_int(pieces.at(1));
if (pieces.size() > 2) {
seconds = to_int(pieces.at(2));
}
}
double result = hours + (minutes / 60.0) + (seconds / 3600.0);
return negativeSign ? -result : result;
}
int compare_versions(const string& v1, const string& v2, int maxComponents)
{
@@ -629,25 +655,85 @@ static std::string convertWStringToMultiByte(DWORD encoding, const std::wstring&
std::wstring convertUtf8ToWString(const std::string& a)
{
#ifdef SG_WINDOWS
return convertMultiByteToWString(CP_UTF8, a);
#elif defined(HAVE_CPP11_CODECVT)
#if defined(SG_WINDOWS)
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> ucs2conv;
return ucs2conv.from_bytes(a);
#else
return std::wstring();
assert(sizeof(wchar_t) == 4);
std::wstring result;
int expectedContinuationCount = 0;
wchar_t wc = 0;
for (uint8_t utf8CodePoint : a) {
// ASCII 7-bit range
if (utf8CodePoint <= 0x7f) {
if (expectedContinuationCount != 0) {
throw sg_format_exception();
}
result.push_back(static_cast<wchar_t>(utf8CodePoint));
} else if (expectedContinuationCount > 0) {
if ((utf8CodePoint & 0xC0) != 0x80) {
throw sg_format_exception();
}
wc = (wc << 6) | (utf8CodePoint & 0x3F);
if (--expectedContinuationCount == 0) {
result.push_back(wc);
}
} else {
if ((utf8CodePoint & 0xE0) == 0xC0) {
expectedContinuationCount = 1;
wc = utf8CodePoint & 0x1f;
} else if ((utf8CodePoint & 0xF0) == 0xE0) {
expectedContinuationCount = 2;
wc = utf8CodePoint & 0x0f;
} else if ((utf8CodePoint & 0xF8) == 0xF0) {
expectedContinuationCount = 3;
wc =utf8CodePoint & 0x07;
} else {
// illegal UTF-8 encoding
throw sg_format_exception();
}
}
} // of UTF-8 code point iteration
return result;
#endif
}
std::string convertWStringToUtf8(const std::wstring& w)
{
#ifdef SG_WINDOWS
return convertWStringToMultiByte(CP_UTF8, w);
#elif defined(HAVE_CPP11_CODECVT)
#if defined(SG_WINDOWS)
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> ucs2conv;
return ucs2conv.to_bytes(w);
#else
return std::string();
assert(sizeof(wchar_t) == 4);
std::string result;
for (wchar_t cp : w) {
if (cp <= 0x7f) {
result.push_back(static_cast<uint8_t>(cp));
} else if (cp <= 0x07ff) {
result.push_back(0xC0 | ((cp >> 6) & 0x1f));
result.push_back(0x80 | (cp & 0x3f));
} else if (cp <= 0xffff) {
result.push_back(0xE0 | ((cp >> 12) & 0x0f));
result.push_back(0x80 | ((cp >> 6) & 0x3f));
result.push_back(0x80 | (cp & 0x3f));
} else if (cp < 0x10ffff) {
result.push_back(0xF0 | ((cp >> 18) & 0x07));
result.push_back(0x80 | ((cp >> 12) & 0x3f));
result.push_back(0x80 | ((cp >> 6) & 0x3f));
result.push_back(0x80 | (cp & 0x3f));
} else {
throw sg_format_exception();
}
}
return result;
#endif
}

View File

@@ -209,6 +209,16 @@ namespace simgear {
typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
T readNonNegativeInt(const std::string& s);
/**
* Read a time value, seperated by colons, as a value in hours.
* Allowable input is ([+/-]%f[:%f[:%f]])
* i.e 15:04:35 is parsed as 15 + (04 / 60) + (35 / 2600)
* This code is moved from flightgear's options.cxx where it was called
* parse_time(),
*/
double readTime(const std::string& s);
/**
* Convert a string representing a boolean, to a bool.
* Accepted values include YES, true, 0, 1, false, no, True,

View File

@@ -19,6 +19,7 @@
#include <simgear/compiler.h>
#include <simgear/misc/strutils.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/constants.h>
using std::string;
using std::vector;
@@ -581,6 +582,43 @@ void test_error_string()
SG_CHECK_GT(strutils::error_string(saved_errno).size(), 0);
}
void test_readTime()
{
SG_CHECK_EQUAL_EP(strutils::readTime(""), 0.0);
SG_CHECK_EQUAL_EP(strutils::readTime("11"), 11.0);
SG_CHECK_EQUAL_EP(strutils::readTime("+11"), 11.0);
SG_CHECK_EQUAL_EP(strutils::readTime("-11"), -11.0);
SG_CHECK_EQUAL_EP(strutils::readTime("11:30"), 11.5);
SG_CHECK_EQUAL_EP(strutils::readTime("+11:15"), 11.25);
SG_CHECK_EQUAL_EP(strutils::readTime("-11:45"), -11.75);
const double seconds = 1 / 3600.0;
SG_CHECK_EQUAL_EP(strutils::readTime("11:30:00"), 11.5);
SG_CHECK_EQUAL_EP(strutils::readTime("+11:15:05"), 11.25 + 5 * seconds);
SG_CHECK_EQUAL_EP(strutils::readTime("-11:45:15"), -(11.75 + 15 * seconds));
SG_CHECK_EQUAL_EP(strutils::readTime("0:0:0"), 0);
SG_CHECK_EQUAL_EP(strutils::readTime("0:0:28"), 28 * seconds);
SG_CHECK_EQUAL_EP(strutils::readTime("-0:0:28"), -28 * seconds);
}
void test_utf8Convert()
{
// F, smiley emoticon, Maths summation symbol, section sign
std::wstring a(L"\u0046\U0001F600\u2211\u00A7");
std::string utf8A = strutils::convertWStringToUtf8(a);
SG_VERIFY(utf8A == std::string("F\xF0\x9F\x98\x80\xE2\x88\x91\xC2\xA7"));
std::wstring aRoundTrip = strutils::convertUtf8ToWString(utf8A);
SG_VERIFY(a == aRoundTrip);
}
int main(int argc, char* argv[])
{
test_strip();
@@ -599,6 +637,8 @@ int main(int argc, char* argv[])
test_md5_hex();
test_error_string();
test_propPathMatch();
test_readTime();
test_utf8Convert();
return EXIT_SUCCESS;
}

View File

@@ -787,6 +787,11 @@ void naGCRelease(int key)
naHash_delete(globals->save_hash, naNum(key));
}
int naNumSaved()
{
return naHash_size(globals->save_hash) + naVec_size(globals->save);
}
void naClearSaved()
{
naContext c;

View File

@@ -1,10 +1,13 @@
include (SimGearComponent)
set(HEADERS
cppbind_fwd.hxx
Ghost.hxx
NasalCallContext.hxx
NasalContext.hxx
NasalHash.hxx
NasalMe.hxx
NasalMethodHolder.hxx
NasalObject.hxx
NasalObjectHolder.hxx
NasalString.hxx
@@ -13,11 +16,8 @@ set(HEADERS
)
set(DETAIL_HEADERS
detail/from_nasal_function_templates.hxx
detail/from_nasal_helper.hxx
detail/functor_templates.hxx
detail/nasal_traits.hxx
detail/NasalObject_callMethod_templates.hxx
detail/to_nasal_helper.hxx
)
@@ -52,4 +52,4 @@ add_boost_test(nasal_gc_test
add_boost_test(nasal_num
SOURCES test/nasal_num_test.cxx
LIBRARIES ${TEST_LIBS}
)
)

View File

@@ -24,17 +24,16 @@
#include "NasalObjectHolder.hxx"
#include <simgear/debug/logstream.hxx>
#include <simgear/std/integer_sequence.hxx>
#include <simgear/std/type_traits.hxx>
#include <simgear/structure/SGWeakReferenced.hxx>
#include <simgear/structure/SGWeakPtr.hxx>
#include <boost/bind.hpp>
#include <boost/call_traits.hpp>
#include <boost/function.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/mpl/has_xxx.hpp>
#include <boost/preprocessor/iteration/iterate.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/utility/enable_if.hpp>
#include <map>
@@ -62,10 +61,7 @@ namespace osg
}
template<class T>
inline typename boost::enable_if<
boost::is_pointer<T>,
T
>::type
inline std::enable_if_t<std::is_pointer<T>::value, T>
get_pointer(T ptr)
{
return ptr;
@@ -101,8 +97,8 @@ namespace nasal
class GhostMetadata
{
public:
typedef void(*Deleter)(void*);
typedef std::vector<std::pair<Deleter, void*> > DestroyList;
using Deleter = void(*)(void*);
using DestroyList = std::vector<std::pair<Deleter, void*>>;
static DestroyList _destroy_list;
@@ -154,19 +150,6 @@ namespace nasal
};
BOOST_MPL_HAS_XXX_TRAIT_DEF(element_type)
template<class T>
struct reduced_type
{
typedef typename boost::remove_cv<
typename boost::remove_reference<T>::type
>::type type;
};
template<class T1, class T2>
struct reduced_is_same:
public boost::is_same<typename reduced_type<T1>::type, T2>
{};
}
/** @brief Destroy all ghost queued for deletion.
@@ -178,8 +161,8 @@ namespace nasal
*/
void ghostProcessDestroyList();
typedef SGSharedPtr<internal::MethodHolder> MethodHolderPtr;
typedef SGWeakPtr<internal::MethodHolder> MethodHolderWeakPtr;
using MethodHolderPtr = SGSharedPtr<internal::MethodHolder>;
using MethodHolderWeakPtr = SGWeakPtr<internal::MethodHolder>;
// Dummy template to create shorter and easy to understand compile errors if
// trying to wrap the wrong type as a Ghost.
@@ -187,9 +170,10 @@ namespace nasal
class Ghost
{
public:
BOOST_STATIC_ASSERT(("Ghost can only wrap shared pointer!"
&& is_strong_ref<T>::value
));
static_assert(
is_strong_ref<T>::value,
"Ghost can only wrap shared pointer!"
);
static Ghost& init(const std::string& name);
static bool isInit();
@@ -209,7 +193,7 @@ namespace nasal
* int myMember();
* void doSomethingElse(const nasal::CallContext& ctx);
* }
* typedef boost::shared_ptr<MyClass> MyClassPtr;
* using MyClassPtr = boost::shared_ptr<MyClass>;
*
* std::string myOtherFreeMember(int num);
*
@@ -235,29 +219,25 @@ namespace nasal
* @endcode
*/
template<class T>
class Ghost<T, typename boost::enable_if<is_strong_ref<T> >::type>:
class Ghost<T, std::enable_if_t<is_strong_ref<T>::value>>:
public internal::GhostMetadata
{
// Shared pointer required for Ghost (no weak pointer!)
BOOST_STATIC_ASSERT((is_strong_ref<T>::value));
public:
typedef typename T::element_type raw_type;
typedef typename shared_ptr_traits<T>::strong_ref strong_ref;
typedef typename shared_ptr_traits<T>::weak_ref weak_ref;
typedef naRef (raw_type::*member_func_t)(const CallContext&);
typedef naRef (*free_func_t)(raw_type&, const CallContext&);
typedef boost::function<naRef(raw_type&, naContext)> getter_t;
typedef boost::function<void( raw_type&, naContext, naRef)> setter_t;
typedef boost::function<naRef(raw_type&, const CallContext&)> method_t;
typedef boost::function<bool( raw_type&,
naContext,
const std::string&,
naRef& )> fallback_getter_t;
typedef boost::function<bool( raw_type&,
naContext,
const std::string&,
naRef )> fallback_setter_t;
using raw_type = typename T::element_type;
using strong_ref = typename shared_ptr_traits<T>::strong_ref;
using weak_ref = typename shared_ptr_traits<T>::weak_ref;
using member_func_t = naRef (raw_type::*)(const CallContext&);
using free_func_t = naRef (*)(raw_type&, const CallContext&);
using getter_t = boost::function<naRef(raw_type&, naContext)>;
using setter_t = boost::function<void( raw_type&, naContext, naRef)>;
using method_t = boost::function<naRef(raw_type&, const CallContext&)>;
using fallback_getter_t =
boost::function<bool(raw_type&, naContext, const std::string&, naRef&)>;
using fallback_setter_t =
boost::function<bool(raw_type&, naContext, const std::string&, naRef)>;
template<class Ret, class... Args>
using method_variadic_t = boost::function<Ret (raw_type&, Args...)>;
class MethodHolder:
public internal::MethodHolder
@@ -269,8 +249,8 @@ namespace nasal
protected:
typedef SGSharedPtr<MethodHolder> SharedPtr;
typedef SGWeakPtr<MethodHolder> WeakPtr;
using SharedPtr = SGSharedPtr<MethodHolder>;
using WeakPtr = SGWeakPtr<MethodHolder>;
method_t _method;
@@ -369,7 +349,7 @@ namespace nasal
MethodHolderPtr func;
};
typedef std::map<std::string, member_t> MemberMap;
using MemberMap = std::map<std::string, member_t>;
/**
* Register a new ghost type.
@@ -406,19 +386,18 @@ namespace nasal
* @endcode
*/
template<class BaseGhost>
typename boost::enable_if
<
boost::is_base_of<GhostMetadata, BaseGhost>,
Ghost
>::type&
std::enable_if_t<
std::is_base_of<GhostMetadata, BaseGhost>::value,
Ghost&
>
bases()
{
BOOST_STATIC_ASSERT
((
boost::is_base_of<typename BaseGhost::raw_type, raw_type>::value
));
static_assert(
std::is_base_of<typename BaseGhost::raw_type, raw_type>::value,
"Not a base class!"
);
typedef typename BaseGhost::strong_ref base_ref;
using base_ref = typename BaseGhost::strong_ref;
BaseGhost* base = BaseGhost::getSingletonPtr();
base->addDerived(
@@ -429,17 +408,14 @@ namespace nasal
// Replace any getter that is not available in the current class.
// TODO check if this is the correct behavior of function overriding
for( typename BaseGhost::MemberMap::const_iterator member =
base->_members.begin();
member != base->_members.end();
++member )
for(auto const& base_member: base->_members)
{
if( _members.find(member->first) == _members.end() )
_members[member->first] = member_t
if( _members.find(base_member.first) == _members.end() )
_members[base_member.first] = member_t
(
member->second.getter,
member->second.setter,
member->second.func
base_member.second.getter,
base_member.second.setter,
base_member.second.func
);
}
@@ -464,17 +440,16 @@ namespace nasal
* @endcode
*/
template<class Base>
typename boost::disable_if
<
boost::is_base_of<GhostMetadata, Base>,
Ghost
>::type&
std::enable_if_t<
!std::is_base_of<GhostMetadata, Base>::value,
Ghost&
>
bases()
{
BOOST_STATIC_ASSERT
((
boost::is_base_of<typename Ghost<Base>::raw_type, raw_type>::value
));
static_assert(
std::is_base_of<typename Ghost<Base>::raw_type, raw_type>::value,
"Not a base class!"
);
return bases< Ghost<Base> >();
}
@@ -770,7 +745,7 @@ namespace nasal
/**
* Register anything that accepts an object instance and a
* nasal::CallContext whith automatic conversion of the return type to
* nasal::CallContext with automatic conversion of the return type to
* Nasal.
*
* @code{cpp}
@@ -791,14 +766,73 @@ namespace nasal
return method(name, boost::bind(method_invoker<Ret>, func, _1, _2));
}
// Build dependency for CMake, gcc, etc.
#define SG_DONT_DO_ANYTHING
# include <simgear/nasal/cppbind/detail/functor_templates.hxx>
#undef SG_DONT_DO_ANYTHING
/**
* Bind any callable entity accepting an instance of raw_type and an
* arbitrary number of arguments as method.
*
* The std::index_sequence specifies the order of the arguments
*/
template<class Ret, class... Args, std::size_t... Indices>
Ghost& method( const std::string& name,
const method_variadic_t<Ret, Args...>& func,
std::index_sequence<Indices...> )
{
return method<Ret>
(
name,
typename boost::function<Ret (raw_type&, const CallContext&)>
( boost::bind(
func,
_1,
boost::bind(&Ghost::arg_from_nasal<Args>, _2, Indices)...
))
);
}
#define BOOST_PP_ITERATION_LIMITS (0, 9)
#define BOOST_PP_FILENAME_1 <simgear/nasal/cppbind/detail/functor_templates.hxx>
#include BOOST_PP_ITERATE()
template<class Ret, class... Args>
Ghost& method( const std::string& name,
const method_variadic_t<Ret, Args...>& func )
{
return method(name, func, std::index_sequence_for<Args...>{});
}
/**\
* Bind a member function with an arbitrary number of arguments as method.
*/
template<class Ret, class... Args>
Ghost& method( const std::string& name,
Ret (raw_type::*fn)(Args...) )
{
return method(name, method_variadic_t<Ret, Args...>(fn));
}
template<class Ret, class... Args>
Ghost& method( const std::string& name,
Ret (raw_type::*fn)(Args...) const )
{
return method(name, method_variadic_t<Ret, Args...>(fn));
}
/**
* Bind free function accepting an instance of raw_type and an arbitrary
* number of arguments as method.
*/
template<class Ret, class Type, class... Args>
Ghost& method
(
const std::string& name,
Ret (*fn)(Type, Args ... args)
)
{
static_assert(
std::is_convertible<raw_type&, Type>::value,
//|| std::is_convertible<raw_type*, Type>::value
// TODO check how to do it with pointer...
"First parameter can not be converted from the Ghost raw_type!"
);
return method(name, method_variadic_t<Ret, Args...>(fn));
}
/**
* Create a shared pointer on the heap to handle the reference counting
@@ -806,11 +840,11 @@ namespace nasal
*/
template<class RefType>
static
typename boost::enable_if_c<
boost::is_same<RefType, strong_ref>::value
|| boost::is_same<RefType, weak_ref>::value,
std::enable_if_t<
std::is_same<RefType, strong_ref>::value
|| std::is_same<RefType, weak_ref>::value,
naRef
>::type
>
makeGhost(naContext c, RefType const& ref_ptr)
{
strong_ref ref(ref_ptr);
@@ -848,10 +882,9 @@ namespace nasal
}
// otherwise try the derived classes
for( typename DerivedList::reverse_iterator
derived = getSingletonPtr()->_derived_types.rbegin();
derived != getSingletonPtr()->_derived_types.rend();
++derived )
for( auto derived = getSingletonPtr()->_derived_types.rbegin();
derived != getSingletonPtr()->_derived_types.rend();
++derived )
{
strong_ref ref = (derived->from_nasal)(c, me);
@@ -865,13 +898,10 @@ namespace nasal
if( !naIsVector(na_parents) )
return strong_ref();
typedef std::vector<naRef> naRefs;
naRefs parents = from_nasal<naRefs>(c, na_parents);
for( naRefs::const_iterator parent = parents.begin();
parent != parents.end();
++parent )
auto parents = from_nasal<std::vector<naRef>>(c, na_parents);
for(auto parent: parents)
{
strong_ref ref = fromNasal(c, *parent);
strong_ref ref = fromNasal(c, parent);
if( get_pointer(ref) )
return ref;
}
@@ -923,8 +953,9 @@ namespace nasal
static naGhostType _ghost_type_strong, //!< Stored as shared pointer
_ghost_type_weak; //!< Stored as weak shared pointer
typedef naRef (*to_nasal_t)(naContext, const strong_ref&, bool);
typedef strong_ref (*from_nasal_t)(naContext, naRef);
using to_nasal_t = naRef (*)(naContext, const strong_ref&, bool);
using from_nasal_t = strong_ref (*)(naContext, naRef);
struct DerivedInfo
{
to_nasal_t to_nasal;
@@ -937,7 +968,7 @@ namespace nasal
{}
};
typedef std::vector<DerivedInfo> DerivedList;
using DerivedList = std::vector<DerivedInfo>;
DerivedList _derived_types;
static bool isInstance(naGhostType* ghost_type, bool& is_weak)
@@ -951,13 +982,10 @@ namespace nasal
template<class RefPtr, bool is_weak>
static
typename boost::enable_if_c<
!is_weak,
RefPtr
>::type
std::enable_if_t<!is_weak, RefPtr>
getPtr(void* ptr)
{
typedef shared_ptr_storage<strong_ref> storage_type;
using storage_type = shared_ptr_storage<strong_ref>;
if( ptr )
return storage_type::template get<RefPtr>(
static_cast<typename storage_type::storage_type*>(ptr)
@@ -968,13 +996,13 @@ namespace nasal
template<class RefPtr, bool is_weak>
static
typename boost::enable_if_c<
std::enable_if_t<
is_weak && supports_weak_ref<T>::value,
RefPtr
>::type
>
getPtr(void* ptr)
{
typedef shared_ptr_storage<weak_ref> storage_type;
using storage_type = shared_ptr_storage<weak_ref>;
if( ptr )
return storage_type::template get<RefPtr>(
static_cast<typename storage_type::storage_type*>(ptr)
@@ -985,10 +1013,10 @@ namespace nasal
template<class RefPtr, bool is_weak>
static
typename boost::enable_if_c<
std::enable_if_t<
is_weak && !supports_weak_ref<T>::value,
RefPtr
>::type
>
getPtr(void* ptr)
{
return RefPtr();
@@ -1004,10 +1032,10 @@ namespace nasal
template<class BaseGhost>
static
typename boost::enable_if
< boost::is_polymorphic<typename BaseGhost::raw_type>,
naRef
>::type
std::enable_if_t<
std::is_polymorphic<typename BaseGhost::raw_type>::value,
naRef
>
toNasal( naContext c,
const typename BaseGhost::strong_ref& base_ref,
bool strong )
@@ -1016,10 +1044,10 @@ namespace nasal
// Check first if passed pointer can by converted to instance of class
// this ghost wraps.
if( !boost::is_same
< typename BaseGhost::raw_type,
typename Ghost::raw_type
>::value
if( !std::is_same<
typename BaseGhost::raw_type,
typename Ghost::raw_type
>::value
&& dynamic_cast<const typename Ghost::raw_type*>(ptr) != ptr )
return naNil();
@@ -1038,10 +1066,9 @@ namespace nasal
static_pointer_cast<typename Ghost::raw_type>(base_ref);
// Now check if we can further downcast to one of our derived classes.
for( typename DerivedList::reverse_iterator
derived = getSingletonPtr()->_derived_types.rbegin();
derived != getSingletonPtr()->_derived_types.rend();
++derived )
for( auto derived = getSingletonPtr()->_derived_types.rbegin();
derived != getSingletonPtr()->_derived_types.rend();
++derived )
{
naRef ghost = (derived->to_nasal)(c, ref, strong);
@@ -1058,13 +1085,13 @@ namespace nasal
template<class BaseGhost>
static
typename boost::disable_if
< boost::is_polymorphic<typename BaseGhost::raw_type>,
naRef
>::type
toNasal( naContext c,
const typename BaseGhost::strong_ref& ref,
bool strong )
std::enable_if_t<
!std::is_polymorphic<typename BaseGhost::raw_type>::value,
naRef
>
toNasal( naContext c,
const typename BaseGhost::strong_ref& ref,
bool strong )
{
// For non polymorphic classes there is no possibility to get the actual
// dynamic type, therefore we can only use its static type.
@@ -1087,7 +1114,7 @@ namespace nasal
template<class Ret>
getter_t to_getter(Ret (raw_type::*getter)() const)
{
typedef typename boost::call_traits<Ret>::param_type param_type;
using param_type = typename boost::call_traits<Ret>::param_type;
naRef(*to_nasal_)(naContext, param_type) = &to_nasal;
// Getter signature: naRef(raw_type&, naContext)
@@ -1140,7 +1167,7 @@ namespace nasal
*/
template<class Ret>
static
typename boost::disable_if<boost::is_void<Ret>, naRef>::type
std::enable_if_t<!std::is_void<Ret>::value, naRef>
method_invoker
(
const boost::function<Ret (raw_type&, const CallContext&)>& func,
@@ -1148,7 +1175,7 @@ namespace nasal
const CallContext& ctx
)
{
return (*to_nasal_ptr<Ret>::get())(ctx.c, func(obj, ctx));
return (*to_nasal_ptr<Ret>::get())(ctx.c_ctx(), func(obj, ctx));
};
/**
@@ -1156,7 +1183,7 @@ namespace nasal
*/
template<class Ret>
static
typename boost::enable_if<boost::is_void<Ret>, naRef>::type
std::enable_if_t<std::is_void<Ret>::value, naRef>
method_invoker
(
const boost::function<Ret (raw_type&, const CallContext&)>& func,
@@ -1174,10 +1201,10 @@ namespace nasal
*/
template<class Arg>
static
typename boost::disable_if<
internal::reduced_is_same<Arg, CallContext>,
std::enable_if_t<
!std::is_same<std::remove_cvref_t<Arg>, CallContext>::value,
typename from_nasal_ptr<Arg>::return_type
>::type
>
arg_from_nasal(const CallContext& ctx, size_t index)
{
return ctx.requireArg<Arg>(index);
@@ -1188,19 +1215,21 @@ namespace nasal
*/
template<class Arg>
static
typename boost::enable_if<
internal::reduced_is_same<Arg, CallContext>,
std::enable_if_t<
std::is_same<std::remove_cvref_t<Arg>, CallContext>::value,
typename from_nasal_ptr<Arg>::return_type
>::type
>
arg_from_nasal(const CallContext& ctx, size_t)
{
// Either const CallContext& or CallContext, non-const reference
// does not make sense.
BOOST_STATIC_ASSERT( (!boost::is_same<Arg, CallContext&>::value) );
static_assert(
!boost::is_same<Arg, CallContext&>::value,
"Only const reference and value make sense!");
return ctx;
};
typedef std::unique_ptr<Ghost> GhostPtr;
using GhostPtr = std::unique_ptr<Ghost>;
MemberMap _members;
fallback_getter_t _fallback_getter;
fallback_setter_t _fallback_setter;
@@ -1240,13 +1269,10 @@ namespace nasal
template<bool is_weak>
static
typename boost::enable_if_c<
!is_weak,
naRef
>::type
std::enable_if_t<!is_weak, naRef>
create(naContext c, const strong_ref& ref_ptr)
{
typedef shared_ptr_storage<strong_ref> storage_type;
using storage_type = shared_ptr_storage<strong_ref>;
return naNewGhost2( c,
&Ghost::_ghost_type_strong,
storage_type::ref(ref_ptr) );
@@ -1254,13 +1280,13 @@ namespace nasal
template<bool is_weak>
static
typename boost::enable_if_c<
std::enable_if_t<
is_weak && supports_weak_ref<T>::value,
naRef
>::type
>
create(naContext c, const strong_ref& ref_ptr)
{
typedef shared_ptr_storage<weak_ref> storage_type;
using storage_type = shared_ptr_storage<weak_ref>;
return naNewGhost2( c,
&Ghost::_ghost_type_weak,
storage_type::ref(ref_ptr) );
@@ -1268,10 +1294,10 @@ namespace nasal
template<bool is_weak>
static
typename boost::enable_if_c<
std::enable_if_t<
is_weak && !supports_weak_ref<T>::value,
naRef
>::type
>
create(naContext, const strong_ref&)
{
return naNil();
@@ -1280,7 +1306,7 @@ namespace nasal
template<class Type>
static void destroy(void *ptr)
{
typedef shared_ptr_storage<Type> storage_type;
using storage_type = shared_ptr_storage<Type>;
storage_type::unref(
static_cast<typename storage_type::storage_type*>(ptr)
);
@@ -1322,9 +1348,7 @@ namespace nasal
// return "";
// }
typename MemberMap::iterator member =
getSingletonPtr()->_members.find(key_str);
auto member = getSingletonPtr()->_members.find(key_str);
if( member == getSingletonPtr()->_members.end() )
{
fallback_getter_t fallback_get = getSingletonPtr()->_fallback_getter;
@@ -1374,7 +1398,7 @@ namespace nasal
const std::string key = nasal::from_nasal<std::string>(c, field);
const MemberMap& members = getSingletonPtr()->_members;
typename MemberMap::const_iterator member = members.find(key);
auto member = members.find(key);
if( member == members.end() )
{
fallback_setter_t fallback_set = getSingletonPtr()->_fallback_setter;
@@ -1415,13 +1439,11 @@ namespace nasal
template<class T>
naGhostType
Ghost<T, typename boost::enable_if<is_strong_ref<T> >::type>
::_ghost_type_strong;
Ghost<T, std::enable_if_t<is_strong_ref<T>::value>>::_ghost_type_strong;
template<class T>
naGhostType
Ghost<T, typename boost::enable_if<is_strong_ref<T> >::type>
::_ghost_type_weak;
Ghost<T, std::enable_if_t<is_strong_ref<T>::value>>::_ghost_type_weak;
} // namespace nasal
@@ -1430,15 +1452,13 @@ namespace nasal
* Convert every shared pointer to a ghost.
*/
template<class T>
typename boost::enable_if<
nasal::internal::has_element_type<
typename nasal::internal::reduced_type<T>::type
>,
std::enable_if_t<
nasal::internal::has_element_type<std::remove_cvref_t<T>>::value,
naRef
>::type
>
to_nasal_helper(naContext c, T ptr)
{
typedef typename nasal::shared_ptr_traits<T>::strong_ref strong_ref;
using strong_ref = typename nasal::shared_ptr_traits<T>::strong_ref;
return nasal::Ghost<strong_ref>::makeGhost(c, ptr);
}
@@ -1446,15 +1466,13 @@ to_nasal_helper(naContext c, T ptr)
* Convert nasal ghosts/hashes to shared pointer (of a ghost).
*/
template<class T>
typename boost::enable_if<
nasal::internal::has_element_type<
typename nasal::internal::reduced_type<T>::type
>,
std::enable_if_t<
nasal::internal::has_element_type<std::remove_cvref_t<T>>::value,
T
>::type
>
from_nasal_helper(naContext c, naRef ref, const T*)
{
typedef typename nasal::shared_ptr_traits<T>::strong_ref strong_ref;
using strong_ref = typename nasal::shared_ptr_traits<T>::strong_ref;
return T(nasal::Ghost<strong_ref>::fromNasalChecked(c, ref));
}
@@ -1462,11 +1480,11 @@ from_nasal_helper(naContext c, naRef ref, const T*)
* Convert any pointer to a SGReferenced based object to a ghost.
*/
template<class T>
typename boost::enable_if_c<
boost::is_base_of<SGReferenced, T>::value
|| boost::is_base_of<SGWeakReferenced, T>::value,
std::enable_if_t<
std::is_base_of<SGReferenced, T>::value
|| std::is_base_of<SGWeakReferenced, T>::value,
naRef
>::type
>
to_nasal_helper(naContext c, T* ptr)
{
return nasal::Ghost<SGSharedPtr<T> >::makeGhost(c, SGSharedPtr<T>(ptr));
@@ -1476,20 +1494,14 @@ to_nasal_helper(naContext c, T* ptr)
* Convert nasal ghosts/hashes to pointer (of a SGReferenced based ghost).
*/
template<class T>
typename boost::enable_if_c<
boost::is_base_of<
SGReferenced,
typename boost::remove_pointer<T>::type
>::value
|| boost::is_base_of<
SGWeakReferenced,
typename boost::remove_pointer<T>::type
>::value,
std::enable_if_t<
std::is_base_of<SGReferenced, std::remove_pointer_t<T>>::value
|| std::is_base_of<SGWeakReferenced, std::remove_pointer_t<T>>::value,
T
>::type
>
from_nasal_helper(naContext c, naRef ref, const T*)
{
typedef SGSharedPtr<typename boost::remove_pointer<T>::type> TypeRef;
using TypeRef = SGSharedPtr<std::remove_pointer_t<T>>;
return T(nasal::Ghost<TypeRef>::fromNasalChecked(c, ref));
}
@@ -1497,10 +1509,10 @@ from_nasal_helper(naContext c, naRef ref, const T*)
* Convert any pointer to a osg::Referenced based object to a ghost.
*/
template<class T>
typename boost::enable_if<
boost::is_base_of<osg::Referenced, T>,
std::enable_if_t<
std::is_base_of<osg::Referenced, T>::value,
naRef
>::type
>
to_nasal_helper(naContext c, T* ptr)
{
return nasal::Ghost<osg::ref_ptr<T> >::makeGhost(c, osg::ref_ptr<T>(ptr));
@@ -1510,13 +1522,13 @@ to_nasal_helper(naContext c, T* ptr)
* Convert nasal ghosts/hashes to pointer (of a osg::Referenced based ghost).
*/
template<class T>
typename boost::enable_if<
boost::is_base_of<osg::Referenced, typename boost::remove_pointer<T>::type>,
std::enable_if_t<
std::is_base_of<osg::Referenced, std::remove_pointer_t<T>>::value,
T
>::type
>
from_nasal_helper(naContext c, naRef ref, const T*)
{
typedef osg::ref_ptr<typename boost::remove_pointer<T>::type> TypeRef;
using TypeRef = osg::ref_ptr<std::remove_pointer_t<T>>;
return T(nasal::Ghost<TypeRef>::fromNasalChecked(c, ref));
}

View File

@@ -20,8 +20,7 @@
#ifndef SG_NASAL_CALL_CONTEXT_HXX_
#define SG_NASAL_CALL_CONTEXT_HXX_
#include "from_nasal.hxx"
#include "to_nasal.hxx"
#include "NasalContext.hxx"
namespace nasal
{
@@ -29,11 +28,12 @@ namespace nasal
/**
* Context passed to a function/method being called from Nasal
*/
class CallContext
class CallContext:
public ContextWrapper
{
public:
CallContext(naContext c, naRef me, size_t argc, naRef* args):
c(c),
ContextWrapper(c),
me(me),
argc(argc),
args(args)
@@ -102,28 +102,14 @@ namespace nasal
requireArg(size_t index) const
{
if( index >= argc )
naRuntimeError(c, "Missing required arg #%d", index);
runtimeError("Missing required arg #%d", index);
return from_nasal<T>(args[index]);
}
template<class T>
naRef to_nasal(T arg) const
{
return nasal::to_nasal(c, arg);
}
template<class T>
typename from_nasal_ptr<T>::return_type
from_nasal(naRef ref) const
{
return (*from_nasal_ptr<T>::get())(c, ref);
}
naContext c;
naRef me;
size_t argc;
naRef *args;
naRef me;
size_t argc;
naRef *args;
};
} // namespace nasal

View File

@@ -17,13 +17,79 @@
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "NasalContext.hxx"
#include "NasalHash.hxx"
#include "NasalString.hxx"
#include <cassert>
namespace nasal
{
//----------------------------------------------------------------------------
ContextWrapper::ContextWrapper(naContext ctx):
_ctx(ctx)
{
assert(_ctx);
}
//----------------------------------------------------------------------------
ContextWrapper::operator naContext()
{
return _ctx;
}
//----------------------------------------------------------------------------
naContext ContextWrapper::c_ctx() const
{
return const_cast<naContext>(_ctx);
}
//----------------------------------------------------------------------------
Hash ContextWrapper::newHash()
{
return Hash(_ctx);
}
//----------------------------------------------------------------------------
String ContextWrapper::newString(const char* str)
{
return String(_ctx, str);
}
//----------------------------------------------------------------------------
naRef ContextWrapper::callMethod( Me me,
naRef code,
std::initializer_list<naRef> args )
{
naRef ret = naCallMethodCtx(
_ctx,
code,
me,
args.size(),
const_cast<naRef*>(args.begin()),
naNil() // locals
);
if( const char* error = naGetError(_ctx) )
throw std::runtime_error(error);
return ret;
}
//----------------------------------------------------------------------------
naRef ContextWrapper::newVector(std::initializer_list<naRef> vals)
{
naRef vec = naNewVector(_ctx);
naVec_setsize(_ctx, vec, vals.size());
int i = 0;
for(naRef val: vals)
naVec_set(vec, i++, val);
return vec;
}
//----------------------------------------------------------------------------
Context::Context():
_ctx(naNewContext())
ContextWrapper(naNewContext())
{
}
@@ -32,18 +98,7 @@ namespace nasal
Context::~Context()
{
naFreeContext(_ctx);
}
//----------------------------------------------------------------------------
Context::operator naContext()
{
return _ctx;
}
//----------------------------------------------------------------------------
Hash Context::newHash()
{
return Hash(_ctx);
_ctx = nullptr;
}
} // namespace nasal

View File

@@ -19,28 +19,110 @@
#ifndef SG_NASAL_CONTEXT_HXX_
#define SG_NASAL_CONTEXT_HXX_
#include "NasalHash.hxx"
#include "cppbind_fwd.hxx"
#include "NasalMe.hxx"
#include <boost/call_traits.hpp>
#include <initializer_list>
namespace nasal
{
/**
* Manage lifetime and encapsulate a Nasal context.
* Wraps a nasal ::naContext without taking ownership/managing its lifetime
*/
class Context
class ContextWrapper
{
public:
explicit ContextWrapper(naContext ctx);
operator naContext();
/** Convert to non-const ::naContext for usage with C-APIs */
naContext c_ctx() const;
Hash newHash();
String newString(const char* str);
/** Raise a nasal runtime error */
template<class... Args>
void runtimeError(const char* fmt, Args ... args) const
{
naRuntimeError(c_ctx(), fmt, args...);
}
template<class T>
naRef to_nasal(T arg) const
{
return nasal::to_nasal(_ctx, arg);
}
template<class T, std::size_t N>
naRef to_nasal(const T(&array)[N]) const
{
return nasal::to_nasal(_ctx, array);
}
/** Create a nasal vector filled with the given values */
template<class... Vals>
naRef to_nasal_vec(Vals ... vals)
{
return newVector({to_nasal(vals)...});
}
template<class T>
Me to_me(T arg) const
{
return Me{ to_nasal(arg) };
}
template<class T>
typename from_nasal_ptr<T>::return_type
from_nasal(naRef ref) const
{
return (*from_nasal_ptr<T>::get())(_ctx, ref);
}
naRef callMethod(Me me, naRef code, std::initializer_list<naRef> args);
template<class Ret, class... Args>
Ret callMethod( Me me,
naRef code,
typename boost::call_traits<Args>::param_type ... args )
{
// TODO warn if with Ret == void something different to nil is returned?
return from_nasal<Ret>(callMethod(
me,
code,
{ to_nasal<typename boost::call_traits<Args>::param_type>(args)... }
));
}
protected:
naContext _ctx;
// Not exposed to avoid confusion of intializer_list<naRef> to variadic
// arguments
naRef newVector(std::initializer_list<naRef> vals);
};
/**
* Creates and manages the lifetime of a ::naContext
*/
class Context:
public ContextWrapper
{
public:
Context();
~Context();
operator naContext();
Hash newHash();
protected:
naContext _ctx;
Context(const Context&) = delete;
Context& operator=(const Context&) = delete;
};
} // namespace nasal
#include "from_nasal.hxx"
#include "to_nasal.hxx"
#endif /* SG_NASAL_CONTEXT_HXX_ */

View File

@@ -0,0 +1,44 @@
///@file
//
// Copyright (C) 2018 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_NASAL_ME_HXX_
#define SG_NASAL_ME_HXX_
#include <simgear/nasal/nasal.h>
namespace nasal
{
/**
* Wrap a naRef to indicate it references the self/me object in Nasal method
* calls.
*/
struct Me
{
naRef _ref;
explicit Me(naRef ref = naNil()):
_ref(ref)
{}
operator naRef() { return _ref; }
};
} // namespace nasal
#endif /* SG_NASAL_ME_HXX_ */

View File

@@ -0,0 +1,75 @@
///@file
//
// Copyright (C) 2018 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_NASAL_METHOD_HOLDER_HXX_
#define SG_NASAL_METHOD_HOLDER_HXX_
#include "NasalContext.hxx"
#include "NasalObjectHolder.hxx"
namespace nasal
{
/**
* Hold any callable function in Nasal and call it from C++
*/
template<class Ret, class... Args>
class NasalMethodHolder
{
using Holder = ObjectHolder<SGReferenced>;
public:
NasalMethodHolder(naRef code):
_code(Holder::makeShared(code))
{}
/**
* @brief Call the function with the given arguments
*
* If the first argument is nasal::Me it will be passed as 'me' object and
* not as argument.
*/
Ret operator()(Args ... args)
{
return call(args...);
}
private:
Holder::Ref _code;
template<class... CArgs>
Ret call(Me self, CArgs ... args)
{
nasal::Context ctx;
return ctx.callMethod<Ret, CArgs...>(
self,
_code->get_naRef(),
args...
);
}
template<class... CArgs>
Ret call(CArgs ... args)
{
return call(Me{}, args...);
}
};
} // namespace nasal
#endif /* SG_NASAL_METHOD_HOLDER_HXX_ */

View File

@@ -23,9 +23,6 @@
#include "NasalObjectHolder.hxx"
#include "Ghost.hxx"
#include <boost/preprocessor/iteration/iterate.hpp>
#include <boost/preprocessor/repetition/enum_trailing_binary_params.hpp>
namespace nasal
{
/**
@@ -48,14 +45,21 @@ namespace nasal
bool valid() const;
// Build dependency for CMake, gcc, etc.
#define SG_DONT_DO_ANYTHING
# include <simgear/nasal/cppbind/detail/NasalObject_callMethod_templates.hxx>
#undef SG_DONT_DO_ANYTHING
template<class Ret, class... Args>
Ret callMethod(const std::string& name, Args ... args)
{
if( !_nasal_impl.valid() )
return Ret();
#define BOOST_PP_ITERATION_LIMITS (0, 9)
#define BOOST_PP_FILENAME_1 <simgear/nasal/cppbind/detail/NasalObject_callMethod_templates.hxx>
#include BOOST_PP_ITERATE()
Context ctx;
auto func = get_member<boost::function<Ret (Me, Args...)>>(
ctx, _nasal_impl.get_naRef(), name
);
if( func )
return func(Me(ctx.to_nasal(this)), args...);
return Ret();
}
bool _set(naContext c, const std::string& key, naRef val);
bool _get(naContext c, const std::string& key, naRef& out);

View File

@@ -42,6 +42,8 @@ namespace nasal
{
public:
using Ref = SGSharedPtr<ObjectHolder<Base>>;
/**
* @param obj Object to save
*/
@@ -84,7 +86,7 @@ namespace nasal
*
* @param obj Object to save
*/
static SGSharedPtr<ObjectHolder<Base> > makeShared(naRef obj);
static Ref makeShared(naRef obj);
protected:
naRef _ref;
@@ -139,10 +141,9 @@ namespace nasal
template<class Base>
ObjectHolder<Base>::ObjectHolder(naRef obj):
_ref(obj),
_gc_key(0)
_gc_key(naIsNil(obj) ? 0 : naGCSave(obj))
{
if( !naIsNil(obj) )
naGCSave(obj);
}
//----------------------------------------------------------------------------
@@ -156,10 +157,10 @@ namespace nasal
//----------------------------------------------------------------------------
template<class Base>
SGSharedPtr<ObjectHolder<Base> >
typename ObjectHolder<Base>::Ref
ObjectHolder<Base>::makeShared(naRef obj)
{
return SGSharedPtr<ObjectHolder<Base> >( new ObjectHolder<SGReferenced>(obj) );
return Ref( new ObjectHolder<Base>(obj) );
}
} // namespace nasal

View File

@@ -47,7 +47,7 @@ namespace nasal
*
* @param str Existing Nasal String
*/
String(naRef str);
explicit String(naRef str);
const char* c_str() const;
const char* begin() const;

View File

@@ -0,0 +1,60 @@
///@file
/// Nasal C++ Bindings forward declarations
///
// Copyright (C) 2018 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_NASAL_CPPBIND_FWD_HXX_
#define SG_NASAL_CPPBIND_FWD_HXX_
#include <simgear/nasal/nasal.h>
#include <cstddef>
#include <string>
namespace nasal
{
class bad_nasal_cast;
class CallContext;
class Context;
class ContextWrapper;
class Hash;
struct Me;
class Object;
class String;
template<class, class>
class Ghost;
template<class T>
naRef to_nasal(naContext c, T arg);
template<class T, std::size_t N>
naRef to_nasal(naContext c, const T(&array)[N]);
template<class T>
T from_nasal(naContext c, naRef ref);
template<class Var>
struct from_nasal_ptr;
template<class T>
T get_member(naContext c, naRef obj, const std::string& name);
} // namespace nasal
#endif /* SG_NASAL_CPPBIND_FWD_HXX_ */

View File

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

View File

@@ -1,131 +0,0 @@
#ifndef SG_FROM_NASAL_HELPER_HXX_
# error Nasal cppbind - do not include this file!
#endif
#ifndef SG_DONT_DO_ANYTHING
#define n BOOST_PP_ITERATION()
#ifndef SG_BOOST_FUNCTION_FROM_NASAL_FWD
# define SG_CALL_TRAITS_PARAM(z, n, dummy)\
typename boost::call_traits<A##n>::param_type a##n
# define SG_CALL_ARG(z, n, dummy)\
to_nasal<typename boost::call_traits<A##n>::param_type>(ctx, a##n)
template<
class Ret
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
>
typename boost::disable_if<boost::is_void<Ret>, Ret>::type
callNasalMethod( const ObjectHolder<SGReferenced>* holder,
Me self
BOOST_PP_ENUM_TRAILING(n, SG_CALL_TRAITS_PARAM, 0) )
{
naContext ctx = naNewContext();
#if n
naRef args[n] = {
BOOST_PP_ENUM(n, SG_CALL_ARG, 0)
};
#else
naRef* args = NULL;
#endif
naRef result =
naCallMethodCtx(ctx, holder->get_naRef(), self, n, args, naNil());
const char* error = naGetError(ctx);
std::string error_str(error ? error : "");
Ret r = Ret();
if( !error )
r = from_nasal_helper(ctx, result, static_cast<Ret*>(0));
naFreeContext(ctx);
if( error )
throw std::runtime_error(error_str);
return r;
}
template<
class Ret
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
>
typename boost::enable_if<boost::is_void<Ret>, Ret>::type
callNasalMethod( const ObjectHolder<SGReferenced>* holder,
Me self
BOOST_PP_ENUM_TRAILING(n, SG_CALL_TRAITS_PARAM, 0) )
{
callNasalMethod<
naRef // do not do any conversion and just ignore the return value
// TODO warn if something different to nil is returned?
BOOST_PP_COMMA_IF(n)
BOOST_PP_ENUM_PARAMS(n, A)
>
(
holder,
self
BOOST_PP_ENUM_TRAILING_PARAMS(n, a)
);
}
# undef SG_CALL_TRAITS_PARAM
# undef SG_CALL_ARG
#endif
template<
class Ret
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
>
typename boost::disable_if<
// free function if first argument is not nasal::Me or no argument at all
boost::is_same<BOOST_PP_IF(n, A0, void), Me>,
boost::function<Ret (BOOST_PP_ENUM_PARAMS(n, A))>
>::type
boostFunctionFromNasal(naRef code, Ret (*)(BOOST_PP_ENUM_PARAMS(n, A)))
#ifdef SG_BOOST_FUNCTION_FROM_NASAL_FWD
;
#else
{
return boost::bind
(
&callNasalMethod<Ret BOOST_PP_ENUM_TRAILING_PARAMS(n, A)>,
ObjectHolder<SGReferenced>::makeShared(code),
boost::bind(naNil)
BOOST_PP_COMMA_IF(n)
BOOST_PP_ENUM_SHIFTED_PARAMS(BOOST_PP_INC(n), _)
);
}
#endif
#if n > 0
template<
class Ret
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
>
typename boost::enable_if<
// method if type of first argument is nasal::Me
boost::is_same<A0, Me>,
boost::function<Ret (BOOST_PP_ENUM_PARAMS(n, A))>
>::type
boostFunctionFromNasal(naRef code, Ret (*)(BOOST_PP_ENUM_PARAMS(n, A)))
#ifdef SG_BOOST_FUNCTION_FROM_NASAL_FWD
;
#else
{
return boost::bind
(
&callNasalMethod<
Ret
BOOST_PP_COMMA_IF(BOOST_PP_DEC(n))
BOOST_PP_ENUM_SHIFTED_PARAMS(n, A)
>,
ObjectHolder<SGReferenced>::makeShared(code),
BOOST_PP_ENUM_SHIFTED_PARAMS(BOOST_PP_INC(n), _)
);
}
#endif
#endif
#undef n
#endif // SG_DONT_DO_ANYTHING

View File

@@ -39,7 +39,7 @@ namespace nasal
}
//----------------------------------------------------------------------------
bad_nasal_cast::~bad_nasal_cast() throw()
bad_nasal_cast::~bad_nasal_cast()
{
}

View File

@@ -24,23 +24,17 @@
#include <simgear/math/SGMath.hxx>
#include <simgear/math/SGRect.hxx>
#include <simgear/nasal/nasal.h>
#include <simgear/nasal/cppbind/NasalContext.hxx>
#include <simgear/nasal/cppbind/NasalMe.hxx>
#include <simgear/nasal/cppbind/NasalMethodHolder.hxx>
#include <simgear/nasal/cppbind/NasalObjectHolder.hxx>
#include <simgear/nasal/cppbind/to_nasal.hxx>
#include <simgear/std/type_traits.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
#include <boost/bind.hpp>
#include <boost/call_traits.hpp>
#include <boost/function.hpp>
#include <boost/preprocessor/control/if.hpp>
#include <boost/preprocessor/iteration/iterate.hpp>
#include <boost/preprocessor/repetition/enum_trailing.hpp>
#include <boost/preprocessor/repetition/enum_trailing_params.hpp>
#include <boost/preprocessor/repetition/enum_shifted_params.hpp>
#include <boost/type_traits.hpp>
#include <boost/utility/enable_if.hpp>
#include <array>
#include <string>
#include <vector>
@@ -48,8 +42,6 @@ class SGPath;
namespace nasal
{
class Hash;
class String;
/**
* Thrown when converting a type from/to Nasal has failed
@@ -70,33 +62,25 @@ namespace nasal
*/
explicit bad_nasal_cast(const std::string& msg);
virtual ~bad_nasal_cast() throw();
virtual ~bad_nasal_cast();
protected:
std::string _msg;
};
/**
* Wrap a naRef to indicate it references the self/me object in Nasal method
* calls.
*/
struct Me
{
naRef _ref;
Me(naRef ref):
_ref(ref)
{}
operator naRef() { return _ref; }
};
/**
* Simple pass through for unified handling also of naRef.
*/
inline naRef from_nasal_helper(naContext, naRef ref, const naRef*)
{ return ref; }
/**
* Ignore return value
*/
// TODO show some warning when something is returned but ignored?
inline void from_nasal_helper(naContext, naRef, const void*)
{}
/**
* Convert Nasal string to std::string
*/
@@ -125,52 +109,40 @@ namespace nasal
*/
bool from_nasal_helper(naContext c, naRef ref, const bool*);
namespace detail
{
#define SG_BOOST_FUNCTION_FROM_NASAL_FWD
#define BOOST_PP_ITERATION_LIMITS (0, 9)
#define BOOST_PP_FILENAME_1 <simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx>
#include BOOST_PP_ITERATE()
#undef SG_BOOST_FUNCTION_FROM_NASAL_FWD
}
/**
* Convert a Nasal function to a boost::function with the given signature.
*
* @tparam Sig Signature of returned function (arguments and return value
* are automatically converted using from_nasal/to_nasal)
*/
template<class Sig>
boost::function<Sig>
from_nasal_helper(naContext c, naRef ref, boost::function<Sig>*)
template<class Ret, class... Args>
boost::function<Ret (Args...)>
from_nasal_helper(naContext c, naRef ref, const boost::function<Ret (Args...)>*)
{
if( naIsNil(ref) )
return boost::function<Sig>();
return {};
if( !naIsCode(ref)
&& !naIsCCode(ref)
&& !naIsFunc(ref) )
throw bad_nasal_cast("not a function");
return detail::boostFunctionFromNasal(ref, static_cast<Sig*>(0));
return NasalMethodHolder<Ret, Args...>(ref);
}
template<class Sig>
typename boost::enable_if< boost::is_function<Sig>,
boost::function<Sig>
>::type
from_nasal_helper(naContext c, naRef ref, Sig*)
template<class Ret, class... Args>
boost::function<Ret (Args...)>
from_nasal_helper(naContext c, naRef ref, Ret (*const)(Args...))
{
return from_nasal_helper(c, ref, static_cast<boost::function<Sig>*>(0));
return
from_nasal_helper(c, ref, static_cast<boost::function<Ret (Args...)>*>(0));
}
/**
* Convert a Nasal number to a C++ numeric type
*/
template<class T>
typename boost::enable_if< boost::is_arithmetic<T>,
T
>::type
std::enable_if_t<std::is_arithmetic<T>::value, T>
from_nasal_helper(naContext c, naRef ref, const T*)
{
naRef num = naNumValue(ref);
@@ -199,17 +171,39 @@ namespace nasal
return vec;
}
/**
* Convert a Nasal vector to a std::array
*/
template<class T, std::size_t N>
std::array<T, N>
from_nasal_helper(naContext c, naRef ref, const std::array<T, N>*)
{
if( !naIsVector(ref) )
throw bad_nasal_cast("Not a vector");
if( naVec_size(ref) != N )
throw bad_nasal_cast(
"Expected vector with " + std::to_string(N) + " elements"
);
std::array<T, N> arr;
for(std::size_t i = 0; i < N; ++i)
arr[i] = from_nasal_helper(c, naVec_get(ref, i), static_cast<T*>(0));
return arr;
}
/**
* Convert a Nasal vector of 2 elements to a 2d vector
*/
template<class Vec2>
typename boost::enable_if<is_vec2<Vec2>, Vec2>::type
std::enable_if_t<is_vec2<Vec2>::value, Vec2>
from_nasal_helper(naContext c, naRef ref, const Vec2*)
{
std::vector<double> vec =
from_nasal_helper(c, ref, static_cast<std::vector<double>*>(0));
if( vec.size() != 2 )
throw bad_nasal_cast("Expected vector with two elements");
auto vec =
from_nasal_helper(c, ref, static_cast<std::array<double, 2>*>(0));
return Vec2(vec[0], vec[1]);
}
@@ -219,29 +213,12 @@ namespace nasal
template<class T>
SGRect<T> from_nasal_helper(naContext c, naRef ref, const SGRect<T>*)
{
std::vector<double> vec =
from_nasal_helper(c, ref, static_cast<std::vector<double>*>(0));
if( vec.size() != 4 )
throw bad_nasal_cast("Expected vector with four elements");
auto vec =
from_nasal_helper(c, ref, static_cast<std::array<double, 4>*>(0));
return SGRect<T>(vec[0], vec[1], vec[2], vec[3]);
}
// Helpers for wrapping calls to Nasal functions into boost::function
namespace detail
{
// Dummy include to add a build dependency on this file for gcc/CMake/etc.
#define SG_DONT_DO_ANYTHING
# include <simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx>
#undef SG_DONT_DO_ANYTHING
// Now the actual include (we are limited to 8 arguments (+me) here because
// boost::bind has an upper limit of 9)
#define BOOST_PP_ITERATION_LIMITS (0, 8)
#define BOOST_PP_FILENAME_1 <simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx>
#include BOOST_PP_ITERATE()
}
} // namespace nasal
#endif /* SG_FROM_NASAL_HELPER_HXX_ */

View File

@@ -1,101 +0,0 @@
#ifndef SG_NASAL_GHOST_HXX_
# error Nasal cppbind - do not include this file!
#endif
#ifndef SG_DONT_DO_ANYTHING
#define n BOOST_PP_ITERATION()
#define SG_GHOST_FUNC_TYPE\
boost::function<Ret (raw_type& BOOST_PP_ENUM_TRAILING_PARAMS(n,A))>
/**
* Bind any callable entity accepting an instance of raw_type and an arbitrary
* number of arguments as method.
*/
template<
class Ret
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
>
Ghost& method(const std::string& name, const SG_GHOST_FUNC_TYPE& func)
{
#if defined(SG_GCC_VERSION) && SG_GCC_VERSION < 40407
// The old version of g++ used on Jenkins (16.11.2012) only compiles this
// version.
# define SG_GHOST_REQUIRE_ARG(z, n, dummy)\
boost::bind(&arg_from_nasal<A##n>, _2, n)
#else
// VS (2008, 2010, ... ?) only allow this version.
# define SG_GHOST_REQUIRE_ARG(z, n, dummy)\
boost::bind(&Ghost::arg_from_nasal<A##n>, _2, n)
#endif
return method<Ret>
(
name,
typename boost::function<Ret (raw_type&, const CallContext&)>
( boost::bind(
func,
_1
BOOST_PP_ENUM_TRAILING(n, SG_GHOST_REQUIRE_ARG, 0)
))
);
#undef SG_GHOST_REQUIRE_ARG
}
#define SG_GHOST_MEM_FN(cv)\
/**\
* Bind a member function with an arbitrary number of arguments as method.\
*/\
template<\
class Ret\
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)\
>\
Ghost& method\
(\
const std::string& name,\
Ret (raw_type::*fn)(BOOST_PP_ENUM_PARAMS(n,A)) cv\
)\
{\
return method<\
Ret\
BOOST_PP_ENUM_TRAILING_PARAMS(n,A)\
>(name, SG_GHOST_FUNC_TYPE(fn));\
}
// Work around MSVC warning C4003: not enough actual parameters for macro
// We really do not want to pass a parameter, even if MSVC can not believe it.
#define SG_GHOST_NO_CV
SG_GHOST_MEM_FN(const)
SG_GHOST_MEM_FN(SG_GHOST_NO_CV)
#undef SG_GHOST_MEM_FN
#undef SG_GHOST_NO_CV
/**
* Bind free function accepting an instance of raw_type and an arbitrary
* number of arguments as method.
*/
template<
class Ret,
class Type
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
>
Ghost& method
(
const std::string& name,
Ret (*fn)(Type BOOST_PP_ENUM_TRAILING_PARAMS(n,A))
)
{
BOOST_STATIC_ASSERT
(( boost::is_convertible<raw_type&, Type>::value
//|| boost::is_convertible<raw_type*, Type>::value
// TODO check how to do it with pointer...
));
return method<Ret>(name, SG_GHOST_FUNC_TYPE(fn));
}
#undef n
#undef SG_GHOST_TYPEDEF_FUNC_TYPE
#endif // SG_DONT_DO_ANYTHING

View File

@@ -20,10 +20,7 @@
#ifndef SG_NASAL_TRAITS_HXX_
#define SG_NASAL_TRAITS_HXX_
#include <boost/mpl/logical.hpp>
#include <boost/type_traits/integral_constant.hpp>
#include <boost/type_traits/is_base_of.hpp>
#include <boost/utility/enable_if.hpp>
#include <simgear/std/type_traits.hxx>
// Forward declarations
class SGReferenced;
@@ -56,12 +53,11 @@ namespace osg
namespace nasal
{
template<class T>
struct is_vec2: public boost::integral_constant<bool, false> {};
struct is_vec2: public std::false_type {};
#define SG_MAKE_TRAIT(templ,type,attr)\
template templ\
struct attr< type >:\
public boost::integral_constant<bool, true> {};
struct attr< type >: public std::true_type {};
SG_MAKE_TRAIT(<class T>, SGVec2<T>, is_vec2)
SG_MAKE_TRAIT(<>, osg::Vec2b, is_vec2)
@@ -75,42 +71,34 @@ SG_MAKE_TRAIT(<>, osg::Vec2s, is_vec2)
struct shared_ptr_traits;
template<class T>
struct is_strong_ref:
public boost::integral_constant<bool, false>
{};
struct is_strong_ref: public std::false_type {};
template<class T>
struct is_weak_ref:
public boost::integral_constant<bool, false>
{};
struct is_weak_ref: public std::false_type {};
#define SG_MAKE_SHARED_PTR_TRAIT(strong, weak, intrusive)\
template<class T>\
struct shared_ptr_traits<strong<T> >\
{\
typedef strong<T> strong_ref;\
typedef weak<T> weak_ref;\
typedef T element_type;\
typedef boost::integral_constant<bool, true> is_strong;\
typedef boost::integral_constant<bool, intrusive> is_intrusive;\
using strong_ref = strong<T>;\
using weak_ref = weak<T>;\
using element_type = T;\
using is_strong = std::true_type;\
using is_intrusive = std::bool_constant<intrusive>;\
};\
template<class T>\
struct shared_ptr_traits<weak<T> >\
{\
typedef strong<T> strong_ref;\
typedef weak<T> weak_ref;\
typedef T element_type;\
typedef boost::integral_constant<bool, false> is_strong;\
typedef boost::integral_constant<bool, intrusive> is_intrusive;\
using strong_ref = strong<T>;\
using weak_ref = weak<T>;\
using element_type = T;\
using is_strong = std::false_type;\
using is_intrusive = std::bool_constant<intrusive>;\
};\
template<class T>\
struct is_strong_ref<strong<T> >:\
public boost::integral_constant<bool, true>\
{};\
struct is_strong_ref<strong<T> >: public std::true_type {};\
template<class T>\
struct is_weak_ref<weak<T> >:\
public boost::integral_constant<bool, true>\
{};
struct is_weak_ref<weak<T> >: public std::true_type {};
SG_MAKE_SHARED_PTR_TRAIT(SGSharedPtr, SGWeakPtr, true)
SG_MAKE_SHARED_PTR_TRAIT(osg::ref_ptr, osg::observer_ptr, true)
@@ -119,25 +107,20 @@ SG_MAKE_TRAIT(<>, osg::Vec2s, is_vec2)
#undef SG_MAKE_SHARED_PTR_TRAIT
template<class T>
struct supports_weak_ref:
public boost::integral_constant<bool, true>
{};
struct supports_weak_ref: public std::true_type {};
template<class T>
struct supports_weak_ref<SGSharedPtr<T> >:
public boost::integral_constant<
bool,
boost::is_base_of<SGWeakReferenced, T>::value
>
public std::bool_constant<std::is_base_of<SGWeakReferenced, T>::value>
{};
template<class T>
struct shared_ptr_storage
{
typedef T storage_type;
typedef typename T::element_type element_type;
typedef typename shared_ptr_traits<T>::strong_ref strong_ref;
typedef typename shared_ptr_traits<T>::weak_ref weak_ref;
using storage_type = T;
using element_type = typename T::element_type;
using strong_ref = typename shared_ptr_traits<T>::strong_ref;
using weak_ref = typename shared_ptr_traits<T>::weak_ref;
template<class U>
static storage_type* ref(U ptr)
@@ -151,39 +134,30 @@ SG_MAKE_TRAIT(<>, osg::Vec2s, is_vec2)
template<class U>
static
typename boost::enable_if<
boost::is_same<U, element_type*>,
element_type*
>::type
std::enable_if_t<std::is_same<U, element_type*>::value, element_type*>
get(storage_type* ptr)
{
return get_pointer(*ptr);
}
template<class U>
static
typename boost::enable_if<
boost::mpl::or_<
boost::is_same<U, strong_ref>,
boost::mpl::and_<
boost::is_same<U, weak_ref>,
supports_weak_ref<U>
>
>,
std::enable_if_t<
std::is_same<U, strong_ref>::value
|| (std::is_same<U, weak_ref>::value && supports_weak_ref<U>::value),
U
>::type
>
get(storage_type* ptr)
{
return U(*ptr);
}
template<class U>
static
typename boost::enable_if<
boost::mpl::and_<
boost::is_same<U, weak_ref>,
boost::mpl::not_<supports_weak_ref<U> >
>,
std::enable_if_t<
std::is_same<U, weak_ref>::value && !supports_weak_ref<U>::value,
U
>::type
>
get(storage_type* ptr)
{
return U();
@@ -195,46 +169,37 @@ SG_MAKE_TRAIT(<>, osg::Vec2s, is_vec2)
template<class T>
struct intrusive_ptr_storage
{
typedef typename T::element_type storage_type;
typedef typename T::element_type element_type;
typedef typename shared_ptr_traits<T>::strong_ref strong_ref;
typedef typename shared_ptr_traits<T>::weak_ref weak_ref;
using storage_type = typename T::element_type;
using element_type = typename T::element_type;
using strong_ref = typename shared_ptr_traits<T>::strong_ref;
using weak_ref = typename shared_ptr_traits<T>::weak_ref;
template<class U>
static
typename boost::enable_if<
boost::is_same<U, element_type*>,
element_type*
>::type
std::enable_if_t<std::is_same<U, element_type*>::value, element_type*>
get(storage_type* ptr)
{
return ptr;
}
template<class U>
static
typename boost::enable_if<
boost::mpl::or_<
boost::is_same<U, strong_ref>,
boost::mpl::and_<
boost::is_same<U, weak_ref>,
supports_weak_ref<U>
>
>,
std::enable_if_t<
std::is_same<U, strong_ref>::value
|| (std::is_same<U, weak_ref>::value && supports_weak_ref<U>::value),
U
>::type
>
get(storage_type* ptr)
{
return U(ptr);
}
template<class U>
static
typename boost::enable_if<
boost::mpl::and_<
boost::is_same<U, weak_ref>,
boost::mpl::not_<supports_weak_ref<U> >
>,
std::enable_if_t<
std::is_same<U, weak_ref>::value && !supports_weak_ref<U>::value,
U
>::type
>
get(storage_type* ptr)
{
return U();
@@ -246,8 +211,8 @@ SG_MAKE_TRAIT(<>, osg::Vec2s, is_vec2)
struct shared_ptr_storage<SGSharedPtr<T> >:
public internal::intrusive_ptr_storage<SGSharedPtr<T> >
{
typedef T storage_type;
typedef T element_type;
using storage_type = T;
using element_type = T;
static storage_type* ref(element_type* ptr)
{
@@ -265,8 +230,8 @@ SG_MAKE_TRAIT(<>, osg::Vec2s, is_vec2)
struct shared_ptr_storage<osg::ref_ptr<T> >:
public internal::intrusive_ptr_storage<osg::ref_ptr<T> >
{
typedef T storage_type;
typedef T element_type;
using storage_type = T;
using element_type = T;
static storage_type* ref(element_type* ptr)

View File

@@ -24,13 +24,14 @@
#include <simgear/math/SGMath.hxx>
#include <simgear/math/SGRect.hxx>
#include <simgear/nasal/nasal.h>
#include <simgear/nasal/cppbind/cppbind_fwd.hxx>
#include <simgear/std/type_traits.hxx>
#include <boost/function/function_fwd.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/call_traits.hpp>
#include <boost/type_traits.hpp>
#include <array>
#include <initializer_list>
#include <map>
#include <string>
#include <vector>
@@ -40,8 +41,6 @@ class SGPath;
namespace nasal
{
class CallContext;
class Hash;
typedef boost::function<naRef (CallContext)> free_function_t;
@@ -77,11 +76,66 @@ namespace nasal
naRef to_nasal_helper(naContext c, const free_function_t& func);
namespace detail
{
template<class T>
naRef array_to_nasal(naContext c, const T* arr, size_t size);
}
/**
* Convert a fixed size array to a Nasal vector
*/
template<class T, size_t N>
naRef to_nasal_helper(naContext c, const T(&array)[N])
{
return detail::array_to_nasal(c, array, N);
}
/**
* Convert a fixed size C++ array to a Nasal vector
*/
template<class T, size_t N>
naRef to_nasal_helper(naContext c, const std::array<T, N>& array)
{
return detail::array_to_nasal(c, array.data(), N);
}
/**
* Convert a std::initializer_list to a Nasal vector
*/
template<class T>
naRef to_nasal_helper(naContext c, std::initializer_list<T> list)
{
return detail::array_to_nasal(c, list.begin(), list.size());
}
/**
* Convert std::vector to a Nasal vector
*/
template< template<class T, class Alloc> class Vector,
class T,
class Alloc
>
std::enable_if_t<
std::is_same<Vector<T,Alloc>, std::vector<T,Alloc>>::value,
naRef
>
to_nasal_helper(naContext c, const Vector<T, Alloc>& vec)
{
return detail::array_to_nasal(c, vec.data(), vec.size());
}
/**
* Convert a std::map to a Nasal Hash
*/
template<class Value>
naRef to_nasal_helper(naContext c, const std::map<std::string, Value>& map);
/**
* Convert an enum value to the according numeric value
*/
template<class T>
typename boost::enable_if< boost::is_enum<T>, naRef >::type
std::enable_if_t<std::is_enum<T>::value, naRef>
to_nasal_helper(naContext c, T val)
{
return naNum(val);
@@ -91,7 +145,7 @@ namespace nasal
* Convert a numeric type to Nasal number
*/
template<class T>
typename boost::enable_if< boost::is_arithmetic<T>, naRef >::type
std::enable_if_t<std::is_arithmetic<T>::value, naRef>
to_nasal_helper(naContext c, T num)
{
return naNum(num);
@@ -101,67 +155,40 @@ namespace nasal
* Convert a 2d vector to Nasal vector with 2 elements
*/
template<class Vec2>
typename boost::enable_if<is_vec2<Vec2>, naRef>::type
to_nasal_helper(naContext c, const Vec2& vec);
/**
* Convert a std::map to a Nasal Hash
*/
template<class Value>
naRef to_nasal_helper(naContext c, const std::map<std::string, Value>& map);
/**
* Convert a fixed size array to a Nasal vector
*/
template<class T, size_t N>
naRef to_nasal_helper(naContext c, const T(&array)[N]);
/**
* Convert std::vector to Nasal vector
*/
template< template<class T, class Alloc> class Vector,
class T,
class Alloc
>
typename boost::enable_if< boost::is_same< Vector<T,Alloc>,
std::vector<T,Alloc>
>,
naRef
>::type
to_nasal_helper(naContext c, const Vector<T, Alloc>& vec)
{
naRef ret = naNewVector(c);
naVec_setsize(c, ret, static_cast<int>(vec.size()));
for(int i = 0; i < static_cast<int>(vec.size()); ++i)
naVec_set(ret, i, to_nasal_helper(c, vec[i]));
return ret;
}
//----------------------------------------------------------------------------
template<class Vec2>
typename boost::enable_if<is_vec2<Vec2>, naRef>::type
std::enable_if_t<is_vec2<Vec2>::value, naRef>
to_nasal_helper(naContext c, const Vec2& vec)
{
// We take just double because in Nasal every number is represented as
// double
double nasal_vec[2] = {
return to_nasal_helper(c, {
// We take just double because in Nasal every number is represented as
// double
static_cast<double>(vec[0]),
static_cast<double>(vec[1])
};
return to_nasal_helper(c, nasal_vec);
});
}
/**
* Convert a SGRect to a Nasal vector with position and size of the rect
*/
template<class T>
naRef to_nasal_helper(naContext c, const SGRect<T>& rect)
{
return to_nasal_helper(c, {
static_cast<double>(rect.x()),
static_cast<double>(rect.y()),
static_cast<double>(rect.width()),
static_cast<double>(rect.height())
});
}
//----------------------------------------------------------------------------
template<class T>
naRef to_nasal_helper(naContext c, const SGRect<T>& rect)
naRef detail::array_to_nasal(naContext c, const T* arr, size_t size)
{
std::vector<double> vec(4);
vec[0] = rect.x();
vec[1] = rect.y();
vec[2] = rect.width();
vec[3] = rect.height();
return to_nasal_helper(c, vec);
naRef ret = naNewVector(c);
naVec_setsize(c, ret, static_cast<int>(size));
for(int i = 0; i < static_cast<int>(size); ++i)
naVec_set(ret, i, to_nasal_helper(c, arr[i]));
return ret;
}
//----------------------------------------------------------------------------
@@ -184,17 +211,6 @@ namespace nasal
return hash;
}
//----------------------------------------------------------------------------
template<class T, size_t N>
naRef to_nasal_helper(naContext c, const T(&array)[N])
{
naRef ret = naNewVector(c);
naVec_setsize(c, ret, static_cast<int>(N));
for(int i = 0; i < static_cast<int>(N); ++i)
naVec_set(ret, i, to_nasal_helper(c, array[i]));
return ret;
}
} // namespace nasal
#endif /* SG_TO_NASAL_HELPER_HXX_ */

View File

@@ -21,6 +21,7 @@
#define SG_FROM_NASAL_HXX_
#include <simgear/nasal/cppbind/detail/from_nasal_helper.hxx>
#include <type_traits>
namespace nasal
{
@@ -52,8 +53,8 @@ namespace nasal
template<class Var>
struct from_nasal_ptr
{
typedef typename boost::remove_const
< typename boost::remove_reference<Var>::type
typedef typename std::remove_const
< typename std::remove_reference<Var>::type
>::type return_type;
typedef return_type(*type)(naContext, naRef);

View File

@@ -20,50 +20,29 @@
#ifndef SG_NASAL_TESTCONTEXT_HXX_
#define SG_NASAL_TESTCONTEXT_HXX_
#include <simgear/nasal/cppbind/NasalCallContext.hxx>
#include <simgear/nasal/cppbind/NasalContext.hxx>
class TestContext:
public nasal::CallContext
public nasal::Context
{
public:
TestContext():
CallContext(naNewContext(), naNil(), 0, 0)
{}
~TestContext()
{
naFreeContext(c);
}
void runGC()
{
naFreeContext(c);
naFreeContext(_ctx);
naGC();
c = naNewContext();
_ctx = naNewContext();
}
template<class T>
T from_str(const std::string& str)
template<class T = naRef>
T exec(const std::string& code, nasal::Me me)
{
return from_nasal<T>(to_nasal(str));
return from_nasal<T>(execImpl(code, me));
}
naRef exec(const std::string& code_str, nasal::Me me)
{
int err_line = -1;
naRef code = naParseCode( c, to_nasal("<TextContext::exec>"), 0,
(char*)code_str.c_str(), code_str.length(),
&err_line );
if( !naIsCode(code) )
throw std::runtime_error("Failed to parse code: " + code_str);
return naCallMethod(code, me, 0, 0, naNil());
}
template<class T>
template<class T = naRef>
T exec(const std::string& code)
{
return from_nasal<T>(exec(code, naNil()));
return from_nasal<T>(execImpl(code, nasal::Me{}));
}
template<class T>
@@ -71,6 +50,26 @@ class TestContext:
{
return from_nasal<T>(to_nasal(str));
}
protected:
naRef execImpl(const std::string& code_str, nasal::Me me)
{
int err_line = -1;
naRef code = naParseCode( _ctx, to_nasal("<TextContext::exec>"), 0,
(char*)code_str.c_str(), code_str.length(),
&err_line );
if( !naIsCode(code) )
throw std::runtime_error("Failed to parse code: " + code_str);
naRef ret = naCallMethod(code, me, 0, 0, naNil());
if( char* err = naGetError(_ctx) )
throw std::runtime_error(
"Failed to executed code: " + std::string(err)
);
return ret;
}
};
#endif /* SG_NASAL_TESTCONTEXT_HXX_ */

View File

@@ -1,6 +1,8 @@
#define BOOST_TEST_MODULE cppbind
#include <BoostTestTargetConfig.h>
#include "TestContext.hxx"
#include <simgear/nasal/cppbind/Ghost.hxx>
#include <simgear/nasal/cppbind/NasalHash.hxx>
#include <simgear/nasal/cppbind/NasalString.hxx>
@@ -115,44 +117,89 @@ naRef f_derivedGetX(const Derived& d, naContext c)
}
naRef f_freeFunction(nasal::CallContext c) { return c.requireArg<naRef>(0); }
namespace std
{
template<class T, std::size_t N>
ostream& operator<<(ostream& strm, const array<T, N>& vec)
{
for(auto const& v: vec)
strm << "'" << v << "',";
return strm;
}
}
BOOST_AUTO_TEST_CASE( cppbind_arrays )
{
TestContext ctx;
naRef na_vec = ctx.to_nasal_vec(1., 2., 3.42);
BOOST_REQUIRE( naIsVector(na_vec) );
BOOST_CHECK_EQUAL(ctx.from_nasal<int>(naVec_get(na_vec, 0)), 1);
BOOST_CHECK_EQUAL(ctx.from_nasal<int>(naVec_get(na_vec, 1)), 2);
BOOST_CHECK_EQUAL(ctx.from_nasal<double>(naVec_get(na_vec, 2)), 3.42);
na_vec = ctx.to_nasal(std::initializer_list<double>({1., 2., 3.42}));
BOOST_REQUIRE( naIsVector(na_vec) );
BOOST_CHECK_EQUAL(ctx.from_nasal<int>(naVec_get(na_vec, 0)), 1);
BOOST_CHECK_EQUAL(ctx.from_nasal<int>(naVec_get(na_vec, 1)), 2);
BOOST_CHECK_EQUAL(ctx.from_nasal<double>(naVec_get(na_vec, 2)), 3.42);
using arr_d3_t = std::array<double, 3>;
arr_d3_t std_arr = {1., 2., 3.42};
na_vec = ctx.to_nasal(std_arr);
BOOST_REQUIRE( naIsVector(na_vec) );
BOOST_CHECK_EQUAL(ctx.from_nasal<int>(naVec_get(na_vec, 0)), 1);
BOOST_CHECK_EQUAL(ctx.from_nasal<int>(naVec_get(na_vec, 1)), 2);
BOOST_CHECK_EQUAL(ctx.from_nasal<double>(naVec_get(na_vec, 2)), 3.42);
double d_arr[] = {1., 2., 3.42};
na_vec = ctx.to_nasal(d_arr);
BOOST_REQUIRE( naIsVector(na_vec) );
BOOST_CHECK_EQUAL(ctx.from_nasal<int>(naVec_get(na_vec, 0)), 1);
BOOST_CHECK_EQUAL(ctx.from_nasal<int>(naVec_get(na_vec, 1)), 2);
BOOST_CHECK_EQUAL(ctx.from_nasal<double>(naVec_get(na_vec, 2)), 3.42);
BOOST_CHECK_EQUAL(std_arr, ctx.from_nasal<arr_d3_t>(na_vec));
}
BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
{
naContext c = naNewContext();
TestContext c;
naRef r;
using namespace nasal;
r = to_nasal(c, ENUM_ANOTHER);
BOOST_CHECK_EQUAL(from_nasal<int>(c, r), ENUM_ANOTHER);
r = c.to_nasal(ENUM_ANOTHER);
BOOST_CHECK_EQUAL(c.from_nasal<int>(r), ENUM_ANOTHER);
r = to_nasal(c, "Test");
r = c.to_nasal("Test");
BOOST_CHECK( strncmp("Test", naStr_data(r), naStr_len(r)) == 0 );
BOOST_CHECK_EQUAL(from_nasal<std::string>(c, r), "Test");
BOOST_CHECK_EQUAL(c.from_nasal<std::string>(r), "Test");
r = to_nasal(c, std::string("Test"));
r = c.to_nasal(std::string("Test"));
BOOST_CHECK( strncmp("Test", naStr_data(r), naStr_len(r)) == 0 );
BOOST_CHECK_EQUAL(from_nasal<std::string>(c, r), "Test");
BOOST_CHECK_EQUAL(c.from_nasal<std::string>(r), "Test");
r = to_nasal(c, 42);
r = c.to_nasal(42);
BOOST_CHECK_EQUAL(naNumValue(r).num, 42);
BOOST_CHECK_EQUAL(from_nasal<int>(c, r), 42);
BOOST_CHECK_EQUAL(c.from_nasal<int>(r), 42);
r = to_nasal(c, 4.2f);
r = c.to_nasal(4.2f);
BOOST_CHECK_EQUAL(naNumValue(r).num, 4.2f);
BOOST_CHECK_EQUAL(from_nasal<float>(c, r), 4.2f);
BOOST_CHECK_EQUAL(c.from_nasal<float>(r), 4.2f);
float test_data[3] = {0, 4, 2};
r = to_nasal(c, test_data);
r = c.to_nasal(test_data);
SGVec2f vec(0,2);
r = to_nasal(c, vec);
BOOST_CHECK_EQUAL(from_nasal<SGVec2f>(c, r), vec);
r = c.to_nasal(vec);
BOOST_CHECK_EQUAL(c.from_nasal<SGVec2f>(r), vec);
std::vector<int> std_vec;
r = to_nasal(c, std_vec);
r = c.to_nasal(std_vec);
r = to_nasal(c, "string");
BOOST_CHECK_THROW(from_nasal<int>(c, r), bad_nasal_cast);
r = c.to_nasal("string");
BOOST_CHECK_THROW(c.from_nasal<int>(r), bad_nasal_cast);
Hash hash(c);
hash.set("vec", r);
@@ -171,10 +218,10 @@ BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
it1 = it2;
it3 = it2;
r = to_nasal(c, hash);
r = c.to_nasal(hash);
BOOST_REQUIRE( naIsHash(r) );
simgear::StringMap string_map = from_nasal<simgear::StringMap>(c, r);
simgear::StringMap string_map = c.from_nasal<simgear::StringMap>(r);
BOOST_CHECK_EQUAL(string_map.at("vec"), "string");
BOOST_CHECK_EQUAL(string_map.at("name"), "my-name");
BOOST_CHECK_EQUAL(string_map.at("string"), "blub");
@@ -216,7 +263,7 @@ BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
// passed on to function
typedef boost::function<int (Me, int)> MeIntFunc;
MeIntFunc fmeint = hash.get<MeIntFunc>("func");
BOOST_CHECK_EQUAL(fmeint(naNil(), 5), 5);
BOOST_CHECK_EQUAL(fmeint(Me{}, 5), 5);
//----------------------------------------------------------------------------
// Test exposing classes to Nasal
@@ -260,81 +307,81 @@ BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
Ghost<SGWeakRefBasedPtr>::init("SGWeakRefBasedPtr");
SGWeakRefBasedPtr weak_ptr(new SGWeakReferenceBasedClass());
naRef nasal_ref = to_nasal(c, weak_ptr),
nasal_ptr = to_nasal(c, weak_ptr.get());
naRef nasal_ref = c.to_nasal(weak_ptr),
nasal_ptr = c.to_nasal(weak_ptr.get());
BOOST_REQUIRE( naIsGhost(nasal_ref) );
BOOST_REQUIRE( naIsGhost(nasal_ptr) );
SGWeakRefBasedPtr ptr1 = from_nasal<SGWeakRefBasedPtr>(c, nasal_ref),
ptr2 = from_nasal<SGWeakRefBasedPtr>(c, nasal_ptr);
SGWeakRefBasedPtr ptr1 = c.from_nasal<SGWeakRefBasedPtr>(nasal_ref),
ptr2 = c.from_nasal<SGWeakRefBasedPtr>(nasal_ptr);
BOOST_CHECK_EQUAL(weak_ptr, ptr1);
BOOST_CHECK_EQUAL(weak_ptr, ptr2);
BOOST_REQUIRE( Ghost<BasePtr>::isInit() );
nasal::to_nasal(c, DoubleDerived2Ptr());
c.to_nasal(DoubleDerived2Ptr());
BasePtr d( new Derived );
naRef derived = to_nasal(c, d);
naRef derived = c.to_nasal(d);
BOOST_REQUIRE( naIsGhost(derived) );
BOOST_CHECK_EQUAL( std::string("DerivedPtr"), naGhost_type(derived)->name );
// Get member function from ghost...
naRef thisGetter = naNil();
BOOST_CHECK( naMember_get(c, derived, to_nasal(c, "this"), &thisGetter) );
BOOST_CHECK( naMember_get(c, derived, c.to_nasal("this"), &thisGetter) );
BOOST_CHECK( naIsFunc(thisGetter) );
// ...and check if it really gets passed the correct instance
typedef boost::function<unsigned long (Me)> MemFunc;
MemFunc fGetThis = from_nasal<MemFunc>(c, thisGetter);
MemFunc fGetThis = c.from_nasal<MemFunc>(thisGetter);
BOOST_REQUIRE( fGetThis );
BOOST_CHECK_EQUAL( fGetThis(derived), (unsigned long)d.get() );
BOOST_CHECK_EQUAL( fGetThis(Me{derived}), (unsigned long)d.get() );
BasePtr d2( new DoubleDerived );
derived = to_nasal(c, d2);
derived = c.to_nasal(d2);
BOOST_CHECK( naIsGhost(derived) );
BOOST_CHECK_EQUAL( std::string("DoubleDerivedPtr"),
naGhost_type(derived)->name );
BasePtr d3( new DoubleDerived2 );
derived = to_nasal(c, d3);
derived = c.to_nasal(d3);
BOOST_CHECK( naIsGhost(derived) );
BOOST_CHECK_EQUAL( std::string("DoubleDerived2Ptr"),
naGhost_type(derived)->name );
SGRefBasedPtr ref_based( new SGReferenceBasedClass );
naRef na_ref_based = to_nasal(c, ref_based.get());
naRef na_ref_based = c.to_nasal(ref_based.get());
BOOST_CHECK( naIsGhost(na_ref_based) );
BOOST_CHECK_EQUAL( from_nasal<SGReferenceBasedClass*>(c, na_ref_based),
BOOST_CHECK_EQUAL( c.from_nasal<SGReferenceBasedClass*>(na_ref_based),
ref_based.get() );
BOOST_CHECK_EQUAL( from_nasal<SGRefBasedPtr>(c, na_ref_based), ref_based );
BOOST_CHECK_EQUAL( c.from_nasal<SGRefBasedPtr>(na_ref_based), ref_based );
BOOST_CHECK_EQUAL( from_nasal<BasePtr>(c, derived), d3 );
BOOST_CHECK_NE( from_nasal<BasePtr>(c, derived), d2 );
BOOST_CHECK_EQUAL( from_nasal<DerivedPtr>(c, derived),
BOOST_CHECK_EQUAL( c.from_nasal<BasePtr>(derived), d3 );
BOOST_CHECK_NE( c.from_nasal<BasePtr>(derived), d2 );
BOOST_CHECK_EQUAL( c.from_nasal<DerivedPtr>(derived),
boost::dynamic_pointer_cast<Derived>(d3) );
BOOST_CHECK_EQUAL( from_nasal<DoubleDerived2Ptr>(c, derived),
BOOST_CHECK_EQUAL( c.from_nasal<DoubleDerived2Ptr>(derived),
boost::dynamic_pointer_cast<DoubleDerived2>(d3) );
BOOST_CHECK_THROW( from_nasal<DoubleDerivedPtr>(c, derived), bad_nasal_cast );
BOOST_CHECK_THROW( c.from_nasal<DoubleDerivedPtr>(derived), bad_nasal_cast );
std::map<std::string, BasePtr> instances;
BOOST_CHECK( naIsHash(to_nasal(c, instances)) );
BOOST_CHECK( naIsHash(c.to_nasal(instances)) );
std::map<std::string, DerivedPtr> instances_d;
BOOST_CHECK( naIsHash(to_nasal(c, instances_d)) );
BOOST_CHECK( naIsHash(c.to_nasal(instances_d)) );
std::map<std::string, int> int_map;
BOOST_CHECK( naIsHash(to_nasal(c, int_map)) );
BOOST_CHECK( naIsHash(c.to_nasal(int_map)) );
std::map<std::string, std::vector<int> > int_vector_map;
BOOST_CHECK( naIsHash(to_nasal(c, int_vector_map)) );
BOOST_CHECK( naIsHash(c.to_nasal(int_vector_map)) );
simgear::StringMap dict =
simgear::StringMap("hello", "value")
("key2", "value2");
naRef na_dict = to_nasal(c, dict);
naRef na_dict = c.to_nasal(dict);
BOOST_REQUIRE( naIsHash(na_dict) );
BOOST_CHECK_EQUAL( Hash(na_dict, c).get<std::string>("key2"), "value2" );
@@ -346,65 +393,44 @@ BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
Hash obj(c);
obj.set("parents", parents);
BOOST_CHECK_EQUAL( from_nasal<BasePtr>(c, obj.get_naRef()), d3 );
BOOST_CHECK_EQUAL( c.from_nasal<BasePtr>(obj.get_naRef()), d3 );
// Check recursive parents (aka parent-of-parent)
std::vector<naRef> parents2;
parents2.push_back(obj.get_naRef());
Hash derived_obj(c);
derived_obj.set("parents", parents2);
BOOST_CHECK_EQUAL( from_nasal<BasePtr>(c, derived_obj.get_naRef()), d3 );
BOOST_CHECK_EQUAL( c.from_nasal<BasePtr>(derived_obj.get_naRef()), d3 );
std::vector<naRef> nasal_objects;
nasal_objects.push_back( Ghost<BasePtr>::makeGhost(c, d) );
nasal_objects.push_back( Ghost<BasePtr>::makeGhost(c, d2) );
nasal_objects.push_back( Ghost<BasePtr>::makeGhost(c, d3) );
naRef obj_vec = to_nasal(c, nasal_objects);
naRef obj_vec = c.to_nasal(nasal_objects);
std::vector<BasePtr> objects = from_nasal<std::vector<BasePtr> >(c, obj_vec);
std::vector<BasePtr> objects = c.from_nasal<std::vector<BasePtr> >(obj_vec);
BOOST_CHECK_EQUAL( objects[0], d );
BOOST_CHECK_EQUAL( objects[1], d2 );
BOOST_CHECK_EQUAL( objects[2], d3 );
{
// Calling fallback setter for unset values
const char* src_code = "me.test = 3;";
int errLine = -1;
naRef code = naParseCode( c, to_nasal(c, "source.nas"), 0,
(char*)src_code, strlen(src_code),
&errLine );
ret = naCallMethod(code, derived, 0, 0, naNil());
// Calling fallback setter for unset values
BOOST_CHECK_EQUAL( c.exec<int>("me.test = 3;", Me{derived}), 3 );
BOOST_REQUIRE( !naGetError(c) ); // TODO real error check (this seems to
// always return 0...
BOOST_CHECK_EQUAL( from_nasal<int>(c, ret), 3 );
}
{
// Calling generic (fallback) getter
const char* src_code = "var a = me.get_test;";
int errLine = -1;
naRef code = naParseCode( c, to_nasal(c, "source.nas"), 0,
(char*)src_code, strlen(src_code),
&errLine );
ret = naCallMethod(code, derived, 0, 0, naNil());
BOOST_REQUIRE( !naGetError(c) ); // TODO real error check (this seems to
// always return 0...
BOOST_CHECK_EQUAL( from_nasal<std::string>(c, ret), "generic-get" );
}
// Calling generic (fallback) getter
BOOST_CHECK_EQUAL( c.exec<std::string>("var a = me.get_test;", Me{derived}),
"generic-get" );
//----------------------------------------------------------------------------
// Test nasal::CallContext
//----------------------------------------------------------------------------
int int_vec[] = {1,2,3};
std::map<std::string, std::string> map;
naRef args[] = {
to_nasal(c, std::string("test-arg")),
to_nasal(c, 4),
to_nasal(c, int_vec),
to_nasal(c, map)
c.to_nasal(std::string("test-arg")),
c.to_nasal(4),
c.to_nasal(int_vec),
c.to_nasal(map)
};
CallContext cc(c, naNil(), sizeof(args)/sizeof(args[0]), args);
BOOST_CHECK_EQUAL( cc.requireArg<std::string>(0), "test-arg" );
@@ -419,15 +445,15 @@ BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
BOOST_CHECK( cc.isVector(2) );
BOOST_CHECK( cc.isHash(3) );
naRef args_vec = nasal::to_nasal(c, args);
naRef args_vec = c.to_nasal(args);
BOOST_CHECK( naIsVector(args_vec) );
//----------------------------------------------------------------------------
// Test nasal::String
//----------------------------------------------------------------------------
String string( to_nasal(c, "Test") );
BOOST_CHECK_EQUAL( from_nasal<std::string>(c, string.get_naRef()), "Test" );
String string( c.to_nasal("Test") );
BOOST_CHECK_EQUAL( c.from_nasal<std::string>(string.get_naRef()), "Test" );
BOOST_CHECK_EQUAL( string.c_str(), std::string("Test") );
BOOST_CHECK( string.starts_with(string) );
BOOST_CHECK( string.starts_with(String(c, "T")) );
@@ -453,6 +479,16 @@ BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "Tse"), 2), 3 );
BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "abc")), 0 );
BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "abc"), 20), String::npos );
naFreeContext(c);
}
BOOST_AUTO_TEST_CASE( cppbind_context )
{
nasal::Context ctx;
naRef vec = ctx.to_nasal_vec(1, 2, 3.4, "test");
BOOST_REQUIRE( naIsVector(vec) );
BOOST_CHECK_EQUAL(ctx.from_nasal<int>(naVec_get(vec, 0)), 1);
BOOST_CHECK_EQUAL(ctx.from_nasal<int>(naVec_get(vec, 1)), 2);
BOOST_CHECK_EQUAL(ctx.from_nasal<double>(naVec_get(vec, 2)), 3.4);
BOOST_CHECK_EQUAL(ctx.from_nasal<std::string>(naVec_get(vec, 3)), "test");
}

View File

@@ -1,9 +1,12 @@
#define BOOST_TEST_MODULE cppbind
#include <BoostTestTargetConfig.h>
#include "TestContext.hxx"
#include <simgear/nasal/cppbind/Ghost.hxx>
#include <simgear/nasal/cppbind/NasalContext.hxx>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
@@ -186,3 +189,44 @@ BOOST_AUTO_TEST_CASE( storage_traits )
nasal::shared_ptr_storage<DerivedWeakPtr>::unref(d_weak);
}
BOOST_AUTO_TEST_CASE( bind_methods )
{
struct TestClass
{
int arg1;
std::string arg2;
std::string arg3;
int arg4;
void set(int a1, const std::string& a2, const std::string& a3, int a4)
{
arg1 = a1;
arg2 = a2;
arg3 = a3;
arg4 = a4;
}
};
using TestClassPtr = boost::shared_ptr<TestClass>;
auto set_func = boost::function<
void (TestClass&, int, const std::string&, const std::string&, int)
>(&TestClass::set);
nasal::Ghost<TestClassPtr>::init("TestClass")
.method("set", set_func)
.method("setReverse", set_func, std::index_sequence<3,2,1,0>{});
TestContext ctx;
auto test = boost::make_shared<TestClass>();
ctx.exec("me.set(1, \"s2\", \"s3\", 4);", ctx.to_me(test));
BOOST_CHECK_EQUAL(test->arg1, 1);
BOOST_CHECK_EQUAL(test->arg2, "s2");
BOOST_CHECK_EQUAL(test->arg3, "s3");
BOOST_CHECK_EQUAL(test->arg4, 4);
ctx.exec("me.setReverse(1, \"s2\", \"s3\", 4);", ctx.to_me(test));
BOOST_CHECK_EQUAL(test->arg1, 4);
BOOST_CHECK_EQUAL(test->arg2, "s3");
BOOST_CHECK_EQUAL(test->arg3, "s2");
BOOST_CHECK_EQUAL(test->arg4, 1);
}

View File

@@ -2,6 +2,9 @@
#include <BoostTestTargetConfig.h>
#include "TestContext.hxx"
#include <simgear/nasal/cppbind/NasalObjectHolder.hxx>
#include <iostream>
#include <set>
@@ -22,7 +25,7 @@ static naGhostType ghost_type = {
static naRef createTestGhost(TestContext& c, intptr_t p)
{
active_instances.insert(p);
return naNewGhost(c.c, &ghost_type, (void*)p);
return naNewGhost(c, &ghost_type, (void*)p);
}
//------------------------------------------------------------------------------
@@ -91,3 +94,47 @@ BOOST_AUTO_TEST_CASE( ghost_gc )
BOOST_REQUIRE(active_instances.empty());
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( object_holder_gc )
{
TestContext c;
BOOST_REQUIRE_EQUAL(naNumSaved(), 0);
BOOST_REQUIRE(active_instances.empty());
//-----------------------------------------------
// Put some ghosts in ObjectHolder and check if
// they are saved from gc
naRef g1 = createTestGhost(c, 1),
g2 = createTestGhost(c, 2);
nasal::ObjectHolder<> h1(g1);
BOOST_CHECK_EQUAL(naNumSaved(), 1);
BOOST_CHECK(naIsGhost(h1.get_naRef()));
nasal::ObjectHolder<> h2(g2);
BOOST_CHECK_EQUAL(naNumSaved(), 2);
BOOST_CHECK(naIsGhost(h2.get_naRef()));
c.runGC();
BOOST_CHECK_EQUAL(active_instances.size(), 2);
BOOST_CHECK_EQUAL(naNumSaved(), 2);
h1.reset(naNum(1));
h2.reset(naNum(2));
BOOST_CHECK_EQUAL(naNumSaved(), 2);
//-----------------------------------------------
// Check that the saved objects are released
h1.reset();
BOOST_CHECK_EQUAL(naNumSaved(), 1);
h2.reset();
BOOST_CHECK_EQUAL(naNumSaved(), 0);
c.runGC();
BOOST_CHECK_EQUAL(active_instances.size(), 0);
}

View File

@@ -109,7 +109,7 @@ static naRef f_fmod(naContext c, naRef me, int argc, naRef* args)
naRef b = naNumValue(argc > 1 ? args[1] : naNil());
if(naIsNil(a) || naIsNil(b))
naRuntimeError(c, "non numeric arguments to fmod()");
a.num = fmod(a.num, b.num);
return VALIDATE(a);
}
@@ -119,7 +119,7 @@ static naRef f_clamp(naContext c, naRef me, int argc, naRef* args)
naRef a = naNumValue(argc > 0 ? args[0] : naNil());
naRef min = naNumValue(argc > 1 ? args[1] : naNil());
naRef max = naNumValue(argc > 2 ? args[2] : naNil());
if(naIsNil(a) || naIsNil(min) || naIsNil(max))
naRuntimeError(c, "non numeric arguments to clamp()");
@@ -133,10 +133,10 @@ static naRef f_periodic(naContext c, naRef me, int argc, naRef* args)
naRef a = naNumValue(argc > 0 ? args[0] : naNil());
naRef b = naNumValue(argc > 1 ? args[1] : naNil());
naRef x = naNumValue(argc > 2 ? args[2] : naNil());
if(naIsNil(a) || naIsNil(b) || naIsNil(x))
naRuntimeError(c, "non numeric arguments to periodic()");
range = b.num - a.num;
x.num = x.num - range*floor((x.num - a.num)/range);
// two security checks that can only happen due to roundoff
@@ -145,7 +145,7 @@ static naRef f_periodic(naContext c, naRef me, int argc, naRef* args)
if (b.num <= x.num)
x.num = b.num;
return VALIDATE(x);
// x.num = SGMiscd::normalizePeriodic(a, b, x);
return VALIDATE(x);
}
@@ -156,7 +156,7 @@ static naRef f_round(naContext c, naRef me, int argc, naRef* args)
naRef b = naNumValue(argc > 1 ? args[1] : naNil());
#ifdef _MSC_VER
double x,y;
#endif
#endif
if(naIsNil(a))
naRuntimeError(c, "non numeric arguments to round()");
if (naIsNil(b))
@@ -169,21 +169,31 @@ static naRef f_round(naContext c, naRef me, int argc, naRef* args)
double x = round(a.num / b.num);
#endif
a.num = x * b.num;
return VALIDATE(a);
}
static naRef f_tan(naContext c, naRef me, int argc, naRef* args)
{
naRef a = naNumValue(argc > 0 ? args[0] : naNil());
naRef a = naNumValue(argc > 0 ? args[0] : naNil());
if(naIsNil(a))
naRuntimeError(c, "non numeric arguments to tan()");
a.num = tan(a.num);
return VALIDATE(a);
}
static naRef f_atan(naContext c, naRef me, int argc, naRef* args)
{
naRef a = naNumValue(argc > 0 ? args[0] : naNil());
if(naIsNil(a))
naRuntimeError(c, "non numeric arguments to tan()");
a.num = atan(a.num);
return VALIDATE(a);
}
static naRef f_asin(naContext c, naRef me, int argc, naRef* args)
{
naRef a = naNumValue(argc > 0 ? args[0] : naNil());
@@ -210,15 +220,16 @@ static naCFuncItem funcs[] = {
{ "pow", f_pow },
{ "sqrt", f_sqrt },
{ "atan2", f_atan2 },
{ "atan", f_atan },
{ "floor", f_floor },
{ "ceil", f_ceil },
{ "fmod", f_fmod },
{ "clamp", f_clamp },
{ "periodic", f_periodic },
{ "round", f_round },
{ "tan", f_tan },
{ "periodic", f_periodic },
{ "round", f_round },
{ "tan", f_tan },
{ "acos", f_acos },
{ "asin", f_asin },
{ "asin", f_asin },
{ 0 }
};

View File

@@ -1,3 +1,6 @@
///@file
/// The Nasal scripting language
///
#ifndef _NASAL_H
#define _NASAL_H
#ifdef __cplusplus
@@ -15,12 +18,14 @@ extern "C" {
#define GCC_PURE
#endif
/** Nasal context pointer */
typedef struct Context* naContext;
// The function signature for an extension function:
/** Function signature for an extension function */
typedef naRef (*naCFunction)(naContext ctx, naRef me, int argc, naRef* args);
// The function signature for an extension function with userdata passed back:
/** Function signature for an extension function with @p user_data passed back
*/
typedef naRef (*naCFunctionU)
(naContext ctx, naRef me, int argc, naRef* args, void* user_data);
@@ -58,6 +63,10 @@ int naGCSave(naRef obj);
// by the garbage collector.
void naGCRelease(int key);
// Get the number of currently saved and not yet again released objects
// (saved by naSave or naGCSave)
int naNumSaved();
// Drop all saved references
void naClearSaved();
@@ -254,7 +263,7 @@ void naHash_set(naRef hash, naRef key, naRef val);
void naHash_cset(naRef hash, char* key, naRef val);
void naHash_delete(naRef hash, naRef key);
/**
* Store the keys in ::hash into the vector at ::dst
* Store the keys in @p hash into the vector at @p dst
*
* @see ::naNewVector
*/
@@ -269,25 +278,25 @@ typedef struct naGhostType {
} naGhostType;
/**
* Create a ghost for an object without any attributes. If ::t contains pointers
* to get_member or set_member function they will be ignored.
* Create a ghost for an object without any attributes. If @p t contains
* pointers to get_member or set_member function they will be ignored.
*/
naRef naNewGhost(naContext c, naGhostType* t, void* ghost);
/**
* Create a ghost for an object. This version uses the get_member and set_member
* function pointers in ::t upon trying to get or set a member respectively from
* Nasal.
* function pointers in @p t upon trying to get or set a member respectively
* from Nasal.
*/
naRef naNewGhost2(naContext c, naGhostType* t, void* ghost);
naGhostType* naGhost_type(naRef ghost);
void* naGhost_ptr(naRef ghost);
/**
* Attach a nasal object to the given ghost. Binds the lifetime of @a data to
* the lifetime of the @a ghost.
* Attach a nasal object to the given ghost. Binds the lifetime of @p data to
* the lifetime of the @p ghost.
*/
void naGhost_setData(naRef ghost, naRef data);
/**
* Retrieve the object attached to the @a ghost, previously set with
* Retrieve the object attached to the @p ghost, previously set with
* naGhost_setData().
*/
naRef naGhost_data(naRef ghost);

View File

@@ -151,25 +151,36 @@ public:
std::string u = dl->realUrl();
if (status == Delegate::STATUS_SUCCESS) {
thumbnailCache[u].requestPending = false;
fireDataForThumbnail(u, reinterpret_cast<const uint8_t*>(bytes.data()), bytes.size());
// if this was a network load, rather than a re-load from the disk cache,
// then persist to disk now.
if (strutils::starts_with(request->url(), "http")) {
addToPersistentCache(u, bytes);
}
fireDataForThumbnail(u, reinterpret_cast<const uint8_t*>(bytes.data()), bytes.size());
} else if (status == Delegate::FAIL_HTTP_FORBIDDEN) {
// treat this as rate-limiting failure, at least from some mirrors
// (eg Ibiblio) and retry up to the max count
const int retries = (thumbnailCache[u].retryCount++);
if (retries < 3) {
SG_LOG(SG_IO, SG_INFO, "Download failed for: " << u << ", will retry");
SG_LOG(SG_IO, SG_DEBUG, "Download failed for: " << u << ", will retry");
thumbnailCache[u].requestPending = true;
pendingThumbnails.push_back(u);
}
} else {
// any other failure.
thumbnailCache[u].requestPending = false;
// if this was a cache refresh, let's report the cached data instead
SGPath cachePath = pathInCache(u);
if (cachePath.exists()) {
SG_LOG(SG_IO, SG_WARN, "Download failed for: " << u << ", will use old cached data");
cachePath.touch(); // touch the file so we don't repeat this danxce
// kick a load from the cache
queueLoadFromPersistentCache(u, cachePath);
}
}
downloadNextPendingThumbnail();
@@ -207,40 +218,45 @@ public:
}
}
void addToPersistentCache(const std::string& url, const std::string& imageBytes)
SGPath pathInCache(const std::string& url) const
{
std::string hash = hashForUrl(url);
const auto hash = hashForUrl(url);
// append the correct file suffix
auto pos = url.rfind('.');
if (pos == std::string::npos) {
return;
return SGPath();
}
SGPath cachePath = path / "ThumbnailCache" / (hash + url.substr(pos));
return path / "ThumbnailCache" / (hash + url.substr(pos));
}
void addToPersistentCache(const std::string& url, const std::string& imageBytes)
{
// this will over-write the existing file if we are refreshing,
// since we use 'truncatr' to open the new file
SGPath cachePath = pathInCache(url);
sg_ofstream fstream(cachePath, std::ios::out | std::ios::trunc | std::ios::binary);
fstream.write(imageBytes.data(), imageBytes.size());
fstream.close();
auto it = thumbnailCache.find(url);
assert(it != thumbnailCache.end());
it->second.pathOnDisk = cachePath;
}
bool checkPersistentCache(const std::string& url)
{
std::string hash = hashForUrl(url);
// append the correct file suffix
auto pos = url.rfind('.');
if (pos == std::string::npos) {
return false;
}
SGPath cachePath = path / "ThumbnailCache" / (hash + url.substr(pos));
SGPath cachePath = pathInCache(url);
if (!cachePath.exists()) {
return false;
}
// check age, if it's too old, expire and download again
int age = time(nullptr) - cachePath.modTime();
if (age > SECONDS_PER_DAY * 7) { // cache for seven days
SG_LOG(SG_IO, SG_INFO, "expiring old cached thumbnail " << url);
cachePath.remove();
const int cacheMaxAge = SECONDS_PER_DAY * 7;
if (age > cacheMaxAge) { // cache for seven days
// note we do *not* remove the file data here, since the
// cache refresh might fail
return false;
}
@@ -258,7 +274,7 @@ public:
entry.pathOnDisk = path;
it = thumbnailCache.insert(it, std::make_pair(url, entry));
} else {
assert(it->second.pathOnDisk == path);
assert(it->second.pathOnDisk.isNull() || (it->second.pathOnDisk == path));
}
if (it->second.requestPending) {
@@ -549,12 +565,10 @@ void Root::addDelegate(simgear::pkg::Delegate *aDelegate)
void Root::removeDelegate(simgear::pkg::Delegate *aDelegate)
{
DelegateVec::iterator it = std::find(d->delegates.begin(),
d->delegates.end(), aDelegate);
if (it == d->delegates.end()) {
throw sg_exception("unknown delegate in removeDelegate");
auto it = std::find(d->delegates.begin(), d->delegates.end(), aDelegate);
if (it != d->delegates.end()) {
d->delegates.erase(it);
}
d->delegates.erase(it);
}
void Root::setLocale(const std::string& aLocale)

View File

@@ -18,7 +18,7 @@
#include <simgear_config.h>
#include "PropertyBasedElement.hxx"
#include <boost/algorithm/string/predicate.hpp>
#include <simgear/misc/strutils.hxx>
namespace simgear
{
@@ -130,7 +130,7 @@ namespace simgear
// character that followed it by the same character converted to ASCII
// uppercase.
if( !boost::starts_with(name, DATA_PREFIX) )
if( !strutils::starts_with(name, DATA_PREFIX) )
return std::string();
std::string data_name;

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