Compare commits

..

7 Commits

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

View File

@@ -100,6 +100,7 @@ set(SYSTEM_EXPAT OFF)
endif()
option(SIMGEAR_HEADLESS "Set to ON to build SimGear without GUI/graphics support" OFF)
option(JPEG_FACTORY "Enable JPEG-factory support" OFF)
option(ENABLE_RTI "Set to ON to build SimGear with RTI support" OFF)
option(ENABLE_TESTS "Set to OFF to disable building SimGear's test applications" ON)
option(ENABLE_SOUND "Set to OFF to disable building SimGear's sound support" ON)
@@ -160,12 +161,9 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
endif (MSVC AND MSVC_3RDPARTY_ROOT)
if(APPLE)
find_library(COCOA_LIBRARY Cocoa)
find_library(CORE_SERVICES_LIBRARY CoreServices)
endif()
# Somehow this only works if included before searching for Boost...
include(BoostTestTargets)
find_package(Boost REQUIRED)
set (BOOST_CXX_FLAGS "-DBOOST_MULTI_INDEX_DISABLE_SERIALIZATION -DBOOST_BIMAP_DISABLE_SERIALIZATION")
@@ -181,9 +179,17 @@ else()
message(STATUS "Sound support: ENABLED")
endif(ENABLE_SOUND)
find_package(OpenSceneGraph 3.2.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgUtil)
find_package(OpenSceneGraph 3.0.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgUtil)
endif(SIMGEAR_HEADLESS)
if(JPEG_FACTORY)
message(STATUS "JPEG-factory: ENABLED")
find_package(JPEG REQUIRED)
include_directories(${JPEG_INCLUDE_DIR})
else()
message(STATUS "JPEG-factory: DISABLED")
endif(JPEG_FACTORY)
find_package(ZLIB REQUIRED)
find_package(Threads REQUIRED)
@@ -263,9 +269,7 @@ check_cxx_source_compiles(
if(HAVE_DLFCN_H)
check_library_exists(dl dlerror "" HAVE_DL)
if(HAVE_DL)
set(DL_LIBRARY "dl")
endif()
set(DL_LIBRARY "dl")
endif()
SET(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "add a postfix, usually 'd' on windows")
@@ -338,11 +342,9 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS_C} ${MSVC_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS_CXX} ${MSVC_FLAGS} ${BOOST_CXX_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MSVC_LD_FLAGS}")
# use BEFORE to ensure local directories are used first,
# ahead of system-installed libs
include_directories(BEFORE ${PROJECT_SOURCE_DIR})
include_directories(BEFORE ${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
include_directories(BEFORE ${PROJECT_BINARY_DIR}/simgear)
include_directories(${PROJECT_SOURCE_DIR})
include_directories(${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
include_directories(${PROJECT_BINARY_DIR}/simgear)
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}
${Boost_INCLUDE_DIRS}
@@ -376,7 +378,7 @@ set(TEST_LIBS_INTERNAL_CORE
${WINSOCK_LIBRARY}
${RT_LIBRARY}
${DL_LIBRARY}
${COCOA_LIBRARY})
${CORE_SERVICES_LIBRARY})
set(TEST_LIBS SimGearCore ${TEST_LIBS_INTERNAL_CORE})
if(NOT SIMGEAR_HEADLESS)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

2674
Doxyfile

File diff suppressed because it is too large Load Diff

View File

@@ -122,7 +122,7 @@ target_link_libraries(SimGearCore
${DL_LIBRARY}
${EXPAT_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
${COCOA_LIBRARY})
${CORE_SERVICES_LIBRARY})
if(NOT SIMGEAR_HEADLESS)
target_link_libraries(SimGearScene

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
///@file The canvas for rendering with the 2d API
// The canvas for rendering with the 2d API
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -23,12 +23,9 @@
#include "ODGauge.hxx"
#include <simgear/canvas/elements/CanvasGroup.hxx>
#include <simgear/canvas/layout/Layout.hxx>
#include <simgear/math/SGRect.hxx>
#include <simgear/nasal/cppbind/NasalObject.hxx>
#include <simgear/props/PropertyBasedElement.hxx>
#include <simgear/props/propertyObject.hxx>
#include <osg/NodeCallback>
#include <osg/observer_ptr>
@@ -37,26 +34,22 @@
namespace simgear
{
/// Canvas 2D drawing API
namespace canvas
{
class CanvasMgr;
class MouseEvent;
class Canvas:
public PropertyBasedElement,
public nasal::Object
public PropertyBasedElement
{
public:
enum StatusFlags
{
STATUS_OK,
STATUS_DIRTY = 1,
LAYOUT_DIRTY = STATUS_DIRTY << 1,
MISSING_SIZE_X = LAYOUT_DIRTY << 1,
STATUS_DIRTY = 1,
MISSING_SIZE_X = STATUS_DIRTY << 1,
MISSING_SIZE_Y = MISSING_SIZE_X << 1,
MISSING_SIZE = MISSING_SIZE_X | MISSING_SIZE_Y,
CREATE_FAILED = MISSING_SIZE_Y << 1
};
@@ -127,12 +120,6 @@ namespace canvas
*/
GroupPtr getRootGroup();
/**
* Set the layout of the canvas (the layout will automatically update with
* the viewport size of the canvas)
*/
void setLayout(const LayoutRef& layout);
/**
* Enable rendering for the next frame
*
@@ -144,7 +131,6 @@ namespace canvas
void update(double delta_time_sec);
bool addEventListener(const std::string& type, const EventListener& cb);
bool dispatchEvent(const EventPtr& event);
void setSizeX(int sx);
void setSizeY(int sy);
@@ -160,8 +146,6 @@ namespace canvas
SGRect<int> getViewport() const;
bool handleMouseEvent(const MouseEventPtr& event);
bool propagateEvent( EventPtr const& event,
EventPropagationPath const& path );
virtual void childAdded( SGPropertyNode * parent,
SGPropertyNode * child );
@@ -207,9 +191,7 @@ namespace canvas
_visible;
ODGauge _texture;
GroupPtr _root_group;
LayoutRef _layout;
GroupPtr _root_group;
CullCallbackPtr _cull_callback;
bool _render_always; //<! Used to disable automatic lazy rendering (culling)
@@ -223,6 +205,7 @@ namespace canvas
typedef std::map<std::string, PlacementFactory> PlacementFactoryMap;
static PlacementFactoryMap _placement_factories;
virtual void setSelf(const PropertyBasedElementPtr& self);
void setStatusFlags(unsigned int flags, bool set = true);
private:

View File

@@ -26,7 +26,6 @@ namespace canvas
//----------------------------------------------------------------------------
Event::Event():
type(UNKNOWN),
time(-1),
propagation_stopped(false)
{
@@ -39,13 +38,7 @@ namespace canvas
}
//----------------------------------------------------------------------------
bool Event::canBubble() const
{
return true;
}
//----------------------------------------------------------------------------
int Event::getType() const
Event::Type Event::getType() const
{
return type;
}
@@ -53,7 +46,14 @@ namespace canvas
//----------------------------------------------------------------------------
std::string Event::getTypeString() const
{
return typeToStr(type);
switch( type )
{
# define ENUM_MAPPING(name, str) case name: return str;
# include "CanvasEventTypes.hxx"
# undef ENUM_MAPPING
default:
return "unknown";
}
}
//----------------------------------------------------------------------------
@@ -81,57 +81,23 @@ namespace canvas
}
//----------------------------------------------------------------------------
int Event::getOrRegisterType(const std::string& type_str)
{
int type = strToType(type_str);
if( type == UNKNOWN )
{
// Register new type
TypeMap& type_map = getTypeMap();
type = type_map.size() + 1; // ids start with 1 (after UNKNOWN)
type_map.insert(TypeMap::value_type(type_str, type));
}
return type;
}
//----------------------------------------------------------------------------
int Event::strToType(const std::string& str)
{
TypeMap const& type_map = getTypeMap();
TypeMap::map_by<name>::const_iterator it = type_map.by<name>().find(str);
if( it == type_map.by<name>().end() )
return UNKNOWN;
return it->second;
}
//----------------------------------------------------------------------------
std::string Event::typeToStr(int type)
{
TypeMap const& type_map = getTypeMap();
TypeMap::map_by<id>::const_iterator it = type_map.by<id>().find(type);
if( it == type_map.by<id>().end() )
return "unknown";
return it->second;
}
//----------------------------------------------------------------------------
Event::TypeMap& Event::getTypeMap()
Event::Type Event::strToType(const std::string& str)
{
typedef std::map<std::string, Type> TypeMap;
static TypeMap type_map;
if( type_map.empty() )
{
# define ENUM_MAPPING(type, str)\
type_map.insert(TypeMap::value_type(str, type));
# include "CanvasEventTypes.hxx"
# undef ENUM_MAPPING
# define ENUM_MAPPING(type, str) type_map[ str ] = type;
# include "CanvasEventTypes.hxx"
# undef ENUM_MAPPING
}
return type_map;
TypeMap::const_iterator it = type_map.find(str);
if( it == type_map.end() )
return UNKNOWN;
return it->second;
}
} // namespace canvas

View File

@@ -20,15 +20,13 @@
#define CANVAS_EVENT_HXX_
#include "canvas_fwd.hxx"
#include <boost/bimap.hpp>
namespace simgear
{
namespace canvas
{
class Event:
public SGReferenced
class Event
{
public:
@@ -38,11 +36,11 @@ namespace canvas
# define ENUM_MAPPING(name, str) name,
# include "CanvasEventTypes.hxx"
# undef ENUM_MAPPING
CUSTOM_EVENT ///< all user defined event types share the same id. They
/// are just differentiated by using the type string.
USER_TYPE ///<! first unused id to be used for user defined types (not
/// implemented yet)
};
int type;
Type type;
ElementWeakPtr target,
current_target;
double time;
@@ -54,19 +52,7 @@ namespace canvas
// of the actual event instances.
virtual ~Event();
/**
* Get whether this events support bubbling
*/
virtual bool canBubble() const;
/**
* Set type of event.
*
* If no such type exists it is registered.
*/
void setType(const std::string& type);
int getType() const;
Type getType() const;
std::string getTypeString() const;
ElementWeakPtr getTarget() const;
@@ -76,19 +62,7 @@ namespace canvas
void stopPropagation();
static int getOrRegisterType(const std::string& type);
static int strToType(const std::string& type);
static std::string typeToStr(int type);
protected:
struct name {};
struct id {};
typedef boost::bimaps::bimap<
boost::bimaps::tagged<std::string, name>,
boost::bimaps::tagged<int, id>
> TypeMap;
static TypeMap& getTypeMap();
static Type strToType(const std::string& str);
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -23,9 +23,4 @@ set(SOURCES
)
simgear_scene_component(canvas-elements canvas/elements "${SOURCES}" "${HEADERS}")
simgear_component(canvas-elements/detail canvas/elements/detail "" "${DETAIL_HEADERS}")
add_boost_test(canvas_element
SOURCES canvas_element_test.cpp
LIBRARIES ${TEST_LIBS}
)
simgear_component(canvas-elements/detail canvas/elements/detail "" "${DETAIL_HEADERS}")

View File

@@ -17,9 +17,8 @@
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "CanvasElement.hxx"
#include <simgear/canvas/Canvas.hxx>
#include <simgear/canvas/CanvasEventVisitor.hxx>
#include <simgear/canvas/events/MouseEvent.hxx>
#include <simgear/canvas/MouseEvent.hxx>
#include <simgear/math/SGMisc.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/scene/material/parseBlendFunc.hxx>
@@ -50,50 +49,15 @@ namespace canvas
{
public:
ReferenceFrame _coord_reference;
osg::observer_ptr<osg::Node> _node;
ReferenceFrame _coord_reference;
osg::Matrix _parent_inverse;
RelativeScissor(osg::Node* node = NULL):
_coord_reference(GLOBAL),
_node(node)
{
_width = 0;
_height = 0;
}
/** Copy constructor using CopyOp to manage deep vs shallow copy. */
RelativeScissor( const RelativeScissor& vp,
const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY ):
Scissor(vp, copyop),
_coord_reference(vp._coord_reference),
_node(vp._node)
RelativeScissor():
_coord_reference(GLOBAL)
{}
META_StateAttribute(simgear, RelativeScissor, SCISSOR);
/** Return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs. */
virtual int compare(const StateAttribute& sa) const
{
// check the types are equal and then create the rhs variable
// used by the COMPARE_StateAttribute_Parameter macros below.
COMPARE_StateAttribute_Types(RelativeScissor,sa)
// compare each parameter in turn against the rhs.
COMPARE_StateAttribute_Parameter(_x)
COMPARE_StateAttribute_Parameter(_y)
COMPARE_StateAttribute_Parameter(_width)
COMPARE_StateAttribute_Parameter(_height)
COMPARE_StateAttribute_Parameter(_coord_reference)
COMPARE_StateAttribute_Parameter(_node)
return 0; // passed all the above comparison macros, must be equal.
}
virtual void apply(osg::State& state) const
{
if( _width <= 0 || _height <= 0 )
return;
const osg::Viewport* vp = state.getCurrentViewport();
float w2 = 0.5 * vp->width(),
h2 = 0.5 * vp->height();
@@ -109,28 +73,17 @@ namespace canvas
if( _coord_reference != GLOBAL )
{
osg::Node* ref_obj = _node.get();
model_view.preMult(state.getModelViewMatrix());
if( _coord_reference == PARENT )
{
if( _node->getNumParents() < 1 )
{
SG_LOG(SG_GL, SG_WARN, "RelativeScissor: missing parent.");
return;
}
ref_obj = _node->getParent(0);
}
osg::MatrixList const& parent_matrices = ref_obj->getWorldMatrices();
assert( !parent_matrices.empty() );
model_view.preMult(parent_matrices.front());
model_view.preMult(_parent_inverse);
}
const osg::Vec2 scale( model_view(0,0), model_view(1,1)),
offset(model_view(3,0), model_view(3,1));
// TODO check/warn for rotation?
GLint x = SGMiscf::roundToInt(scale.x() * _x + offset.x()),
y = SGMiscf::roundToInt(scale.y() * _y + offset.y()),
w = SGMiscf::roundToInt(std::fabs(scale.x()) * _width),
@@ -143,26 +96,6 @@ namespace canvas
glScissor(x, y, w, h);
}
bool contains(const osg::Vec2f& pos) const
{
return _x <= pos.x() && pos.x() <= _x + _width
&& _y <= pos.y() && pos.y() <= _y + _height;
}
bool contains( const osg::Vec2f& global_pos,
const osg::Vec2f& parent_pos,
const osg::Vec2f& local_pos ) const
{
switch( _coord_reference )
{
case GLOBAL: return contains(global_pos);
case PARENT: return contains(parent_pos);
case LOCAL: return contains(local_pos);
}
return false;
}
};
//----------------------------------------------------------------------------
@@ -190,6 +123,17 @@ namespace canvas
}
}
//----------------------------------------------------------------------------
void Element::setSelf(const PropertyBasedElementPtr& self)
{
PropertyBasedElement::setSelf(self);
_transform->setUserData
(
new OSGUserData(boost::static_pointer_cast<Element>(self))
);
}
//----------------------------------------------------------------------------
void Element::onDestroy()
{
@@ -202,41 +146,43 @@ namespace canvas
{
parent->removeChild(_transform.get());
}
// Hide in case someone still holds a reference
setVisible(false);
removeListener();
_parent = 0;
_transform = 0;
}
//----------------------------------------------------------------------------
ElementPtr Element::getParent() const
ElementWeakPtr Element::getWeakPtr() const
{
return _parent;
return boost::static_pointer_cast<Element>(_self.lock());
}
//----------------------------------------------------------------------------
CanvasWeakPtr Element::getCanvas() const
ElementPtr Element::getParent()
{
return _canvas;
return _parent ? _parent->getWeakPtr().lock() : ElementPtr();
}
//----------------------------------------------------------------------------
void Element::update(double dt)
{
if( !isVisible() )
if( !_transform->getNodeMask() )
// Don't do anything if element is hidden
return;
// Trigger matrix update
getMatrix();
if( _attributes_dirty & SCISSOR_COORDS )
{
if( _scissor && _scissor->_coord_reference != GLOBAL )
_scissor->_parent_inverse = _transform->getInverseMatrix();
_attributes_dirty &= ~SCISSOR_COORDS;
}
// Update bounding box on manual update (manual updates pass zero dt)
if( dt == 0 && _drawable )
_drawable->getBound();
if( (_attributes_dirty & BLEND_FUNC) && _transform.valid() )
if( _attributes_dirty & BLEND_FUNC )
{
parseBlendFunc(
_transform->getOrCreateStateSet(),
@@ -262,7 +208,17 @@ namespace canvas
"addEventListener(" << _node->getPath() << ", " << type_str << ")"
);
_listener[ Event::getOrRegisterType(type_str) ].push_back(cb);
Event::Type type = Event::strToType(type_str);
if( type == Event::UNKNOWN )
{
SG_LOG( SG_GENERAL,
SG_WARN,
"addEventListener: Unknown event type " << type_str );
return false;
}
_listener[ type ].push_back(cb);
return true;
}
@@ -296,74 +252,30 @@ namespace canvas
}
//----------------------------------------------------------------------------
bool Element::handleEvent(const EventPtr& event)
bool Element::handleEvent(canvas::EventPtr event)
{
ListenerMap::iterator listeners = _listener.find(event->getType());
if( listeners == _listener.end() )
return false;
BOOST_FOREACH(EventListener const& listener, listeners->second)
try
{
listener(event);
}
catch( std::exception const& ex )
{
SG_LOG(
SG_GENERAL,
SG_WARN,
"canvas::Element: event handler error: '" << ex.what() << "'"
);
}
listener(event);
return true;
}
//----------------------------------------------------------------------------
bool Element::dispatchEvent(const EventPtr& event)
{
EventPropagationPath path;
path.push_back( EventTarget(this) );
for( Element* parent = _parent;
parent != NULL;
parent = parent->_parent )
path.push_front( EventTarget(parent) );
CanvasPtr canvas = _canvas.lock();
if( !canvas )
return false;
return canvas->propagateEvent(event, path);
}
//----------------------------------------------------------------------------
bool Element::hitBound( const osg::Vec2f& global_pos,
const osg::Vec2f& parent_pos,
bool Element::hitBound( const osg::Vec2f& pos,
const osg::Vec2f& local_pos ) const
{
if( _scissor && !_scissor->contains(global_pos, parent_pos, local_pos) )
return false;
const osg::Vec3f pos3(parent_pos, 0);
const osg::Vec3f pos3(pos, 0);
// Drawables have a bounding box...
if( _drawable )
return _drawable->getBound().contains(osg::Vec3f(local_pos, 0));
else if( _transform.valid() )
// ... for other elements, i.e. groups only a bounding sphere is available
return _transform->getBound().contains(osg::Vec3f(parent_pos, 0));
// ... for other elements, i.e. groups only a bounding sphere is available
else
return false;
}
//----------------------------------------------------------------------------
void Element::setVisible(bool visible)
{
if( _transform.valid() )
// TODO check if we need another nodemask
_transform->setNodeMask(visible ? 0xffffffff : 0);
return _transform->getBound().contains(osg::Vec3f(pos, 0));
}
//----------------------------------------------------------------------------
@@ -384,18 +296,6 @@ namespace canvas
return _transform.get();
}
//----------------------------------------------------------------------------
osg::Vec2f Element::posToLocal(const osg::Vec2f& pos) const
{
getMatrix();
const osg::Matrix& m = _transform->getInverseMatrix();
return osg::Vec2f
(
m(0, 0) * pos[0] + m(1, 0) * pos[1] + m(3, 0),
m(0, 1) * pos[0] + m(1, 1) * pos[1] + m(3, 1)
);
}
//----------------------------------------------------------------------------
void Element::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
{
@@ -480,9 +380,7 @@ namespace canvas
if( parent == _node )
{
const std::string& name = child->getNameString();
if( boost::starts_with(name, "data-") )
return;
else if( StyleInfo const* style_info = getStyleInfo(name) )
if( StyleInfo const* style_info = getStyleInfo(name) )
{
SGPropertyNode const* style = child;
if( isStyleEmpty(child) )
@@ -495,11 +393,13 @@ namespace canvas
}
else if( name == "update" )
return update(0);
else if( name == "visible" )
// TODO check if we need another nodemask
return _transform->setNodeMask( child->getBoolValue() ? 0xffffffff : 0 );
else if( boost::starts_with(name, "blend-") )
return (void)(_attributes_dirty |= BLEND_FUNC);
}
else if( parent
&& parent->getParent() == _node
else if( parent->getParent() == _node
&& parent->getNameString() == NAME_TRANSFORM )
{
_attributes_dirty |= TRANSFORM;
@@ -519,13 +419,9 @@ namespace canvas
//----------------------------------------------------------------------------
void Element::setClip(const std::string& clip)
{
osg::StateSet* ss = getOrCreateStateSet();
if( !ss )
return;
if( clip.empty() || clip == "auto" )
{
ss->removeAttribute(osg::StateAttribute::SCISSOR);
getOrCreateStateSet()->removeAttribute(osg::StateAttribute::SCISSOR);
_scissor = 0;
return;
}
@@ -572,49 +468,47 @@ namespace canvas
return;
}
if( !_scissor )
_scissor = new RelativeScissor(_transform.get());
_scissor = new RelativeScissor();
// <top>, <right>, <bottom>, <left>
_scissor->x() = SGMiscf::roundToInt(values[3]);
_scissor->y() = SGMiscf::roundToInt(values[0]);
_scissor->width() = SGMiscf::roundToInt(width);
_scissor->height() = SGMiscf::roundToInt(height);
getOrCreateStateSet()->setAttributeAndModes(_scissor);
SGPropertyNode* clip_frame = _node->getChild("clip-frame", 0);
if( clip_frame )
valueChanged(clip_frame);
else
_scissor->_coord_reference = GLOBAL;
ss->setAttributeAndModes(_scissor);
}
//----------------------------------------------------------------------------
void Element::setClipFrame(ReferenceFrame rf)
{
if( _scissor )
{
_scissor->_coord_reference = rf;
_attributes_dirty |= SCISSOR_COORDS;
}
}
//----------------------------------------------------------------------------
osg::BoundingBox Element::getBoundingBox() const
void Element::setBoundingBox(const osg::BoundingBox& bb)
{
if( _drawable )
return _drawable->getBound();
if( _bounding_box.empty() )
{
SGPropertyNode* bb_node = _node->getChild("bounding-box", 0, true);
_bounding_box.resize(4);
_bounding_box[0] = bb_node->getChild("min-x", 0, true);
_bounding_box[1] = bb_node->getChild("min-y", 0, true);
_bounding_box[2] = bb_node->getChild("max-x", 0, true);
_bounding_box[3] = bb_node->getChild("max-y", 0, true);
}
osg::BoundingBox bb;
if( _transform.valid() )
bb.expandBy(_transform->getBound());
return bb;
}
//----------------------------------------------------------------------------
osg::BoundingBox Element::getTightBoundingBox() const
{
return getTransformedBounds(getMatrix());
_bounding_box[0]->setFloatValue(bb._min.x());
_bounding_box[1]->setFloatValue(bb._min.y());
_bounding_box[2]->setFloatValue(bb._max.x());
_bounding_box[3]->setFloatValue(bb._max.y());
}
//----------------------------------------------------------------------------
@@ -634,9 +528,6 @@ namespace canvas
//----------------------------------------------------------------------------
osg::Matrix Element::getMatrix() const
{
if( !_transform )
return osg::Matrix::identity();
if( !(_attributes_dirty & TRANSFORM) )
return _transform->getMatrix();
@@ -696,6 +587,7 @@ namespace canvas
}
_transform->setMatrix(m);
_attributes_dirty &= ~TRANSFORM;
_attributes_dirty |= SCISSOR_COORDS;
return m;
}
@@ -734,8 +626,6 @@ namespace canvas
"PreOrderBin",
osg::StateSet::OVERRIDE_RENDERBIN_DETAILS
);
_transform->setUserData( new OSGUserData(this) );
}
//----------------------------------------------------------------------------
@@ -746,7 +636,6 @@ namespace canvas
addStyle("clip", "", &Element::setClip, false);
addStyle("clip-frame", "", &Element::setClipFrame, false);
addStyle("visible", "", &Element::setVisible, false);
}
//----------------------------------------------------------------------------
@@ -832,12 +721,8 @@ namespace canvas
//----------------------------------------------------------------------------
osg::StateSet* Element::getOrCreateStateSet()
{
if( _drawable.valid() )
return _drawable->getOrCreateStateSet();
if( _transform.valid() )
return _transform->getOrCreateStateSet();
return 0;
return _drawable ? _drawable->getOrCreateStateSet()
: _transform->getOrCreateStateSet();
}
//----------------------------------------------------------------------------

View File

@@ -1,4 +1,4 @@
///@file Interface for 2D Canvas elements
// Interface for 2D Canvas elements
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -41,9 +41,6 @@ namespace simgear
namespace canvas
{
/**
* Baseclass for Elements displayed inside a Canvas.
*/
class Element:
public PropertyBasedElement
{
@@ -72,10 +69,10 @@ namespace canvas
};
struct StyleInfo
{
StyleSetter setter; ///< Function(s) for setting this style
std::string type; ///< Interpolation type
bool inheritable; ///< Whether children can inherit this style from
/// their parents
StyleSetter setter; ///!< Function(s) for setting this style
std::string type; ///!< Interpolation type
bool inheritable; ///!< Whether children can inherit this style from
/// their parents
};
/**
@@ -83,20 +80,22 @@ namespace canvas
*/
enum ReferenceFrame
{
GLOBAL, ///< Global coordinates
PARENT, ///< Coordinates relative to parent coordinate frame
LOCAL ///< Coordinates relative to local coordinates (parent
/// coordinates with local transformations applied)
GLOBAL, ///!< Global coordinates
PARENT, ///!< Coordinates relative to parent coordinate frame
LOCAL ///!< Coordinates relative to local coordinates (parent
/// coordinates with local transformations applied)
};
/**
*
*/
virtual ~Element() = 0;
virtual void setSelf(const PropertyBasedElementPtr& self);
virtual void onDestroy();
ElementPtr getParent() const;
CanvasWeakPtr getCanvas() const;
ElementWeakPtr getWeakPtr() const;
ElementPtr getParent();
/**
* Called every frame to update internal state
@@ -112,37 +111,20 @@ namespace canvas
virtual bool ascend(EventVisitor& visitor);
virtual bool traverse(EventVisitor& visitor);
virtual bool handleEvent(const EventPtr& event);
bool dispatchEvent(const EventPtr& event);
virtual bool handleEvent(canvas::EventPtr event);
/**
*
* @param global_pos Position in global (canvas) coordinate frame
* @param parent_pos Position in parent coordinate frame
* @param local_pos Position in local (element) coordinate frame
*/
virtual bool hitBound( const osg::Vec2f& global_pos,
const osg::Vec2f& parent_pos,
virtual bool hitBound( const osg::Vec2f& pos,
const osg::Vec2f& local_pos ) const;
/**
* Set visibility of the element.
*/
void setVisible(bool visible);
/**
* Get whether the element is visible or hidden.
* Get whether the element is visible or hidden (Can be changed with
* setting property "visible" accordingly).
*/
bool isVisible() const;
osg::MatrixTransform* getMatrixTransform();
osg::MatrixTransform const* getMatrixTransform() const;
/**
* Transform position to local coordinages.
*/
osg::Vec2f posToLocal(const osg::Vec2f& pos) const;
virtual void childAdded( SGPropertyNode * parent,
SGPropertyNode * child );
virtual void childRemoved( SGPropertyNode * parent,
@@ -166,16 +148,9 @@ namespace canvas
void setClipFrame(ReferenceFrame rf);
/**
* Get bounding box (may not be as tight as bounding box returned by
* #getTightBoundingBox)
* Write the given bounding box to the property tree
*/
osg::BoundingBox getBoundingBox() const;
/**
* Get tight bounding box (child points are transformed to elements
* coordinate space before calculating the bounding box).
*/
osg::BoundingBox getTightBoundingBox() const;
void setBoundingBox(const osg::BoundingBox& bb);
/**
* Get bounding box with children/drawables transformed by passed matrix
@@ -199,10 +174,12 @@ namespace canvas
ElementPtr
>::type create( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& style = Style(),
Element* parent = NULL )
const Style& style,
Element* parent )
{
return ElementPtr( new Derived(canvas, node, style, parent) );
ElementPtr el( new Derived(canvas, node, style, parent) );
el->setSelf(el);
return el;
}
protected:
@@ -211,7 +188,8 @@ namespace canvas
{
TRANSFORM = 1,
BLEND_FUNC = TRANSFORM << 1,
LAST_ATTRIBUTE = BLEND_FUNC << 1
SCISSOR_COORDS = BLEND_FUNC << 1,
LAST_ATTRIBUTE = SCISSOR_COORDS << 1
};
enum TransformType
@@ -233,16 +211,17 @@ namespace canvas
osg::observer_ptr<osg::MatrixTransform> _transform;
std::vector<TransformType> _transform_types;
Style _style;
RelativeScissor *_scissor;
Style _style;
std::vector<SGPropertyNode_ptr> _bounding_box;
RelativeScissor *_scissor;
typedef std::vector<EventListener> Listener;
typedef std::map<int, Listener> ListenerMap;
typedef std::map<Event::Type, Listener> ListenerMap;
ListenerMap _listener;
typedef std::map<std::string, StyleInfo> StyleSetters;
static StyleSetters _style_setters;
static StyleSetters _style_setters;
static void staticInit();

View File

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

View File

@@ -56,27 +56,28 @@ namespace canvas
const std::string& id );
template<class T>
SGSharedPtr<T> createChild(const std::string& id = "")
boost::shared_ptr<T> createChild(const std::string& id = "")
{
return dynamic_cast<T*>( createChild(T::TYPE_NAME, id).get() );
return boost::dynamic_pointer_cast<T>( createChild(T::TYPE_NAME, id) );
}
template<class T>
SGSharedPtr<T> getChild(const SGPropertyNode* node)
boost::shared_ptr<T> getChild(const SGPropertyNode* node)
{
return dynamic_cast<T*>( getChild(node).get() );
return boost::dynamic_pointer_cast<T>( getChild(node) );
}
template<class T>
SGSharedPtr<T> getChild(const std::string& id)
boost::shared_ptr<T> getChild(const std::string& id)
{
return dynamic_cast<T*>( getChild(id).get() );
return boost::dynamic_pointer_cast<T>( getChild(id) );
}
template<class T>
SGSharedPtr<T> getOrCreateChild(const std::string& id)
boost::shared_ptr<T> getOrCreateChild(const std::string& id)
{
return dynamic_cast<T*>( getOrCreateChild(T::TYPE_NAME, id).get() );
return
boost::dynamic_pointer_cast<T>( getOrCreateChild(T::TYPE_NAME, id) );
}
/**

View File

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

View File

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

View File

@@ -217,10 +217,9 @@ namespace canvas
state->setClientActiveTextureUnit(0);
state->disableAllVertexArrays();
bool was_blend_enabled = state->getLastAppliedMode(GL_BLEND);
bool was_stencil_enabled = state->getLastAppliedMode(GL_STENCIL_TEST);
osg::StateAttribute const* blend_func =
state->getLastAppliedAttribute(osg::StateAttribute::BLENDFUNC);
glPushAttrib(~0u); // Don't use GL_ALL_ATTRIB_BITS as on my machine it
// eg. doesn't include GL_MULTISAMPLE_BIT
glPushClientAttrib(~0u);
// Initialize/Update the paint
if( _attributes_dirty & STROKE_COLOR )
@@ -270,11 +269,8 @@ namespace canvas
if( err != VG_NO_ERROR )
SG_LOG(SG_GL, SG_ALERT, "vgError: " << err);
// Restore OpenGL state (TODO check if more is needed or integrate
// better with OpenSceneGraph)
if( was_blend_enabled ) glEnable(GL_BLEND);
if( was_stencil_enabled ) glEnable(GL_STENCIL_TEST);
if( blend_func ) blend_func->apply(*state);
glPopAttrib();
glPopClientAttrib();
}
osg::BoundingBox getTransformedBounds(const osg::Matrix& mat) const
@@ -385,11 +381,14 @@ namespace canvas
// vgPathBounds doesn't take stroke width into account
float ext = 0.5 * _stroke_width;
return osg::BoundingBox
osg::BoundingBox bb
(
min[0] - ext, min[1] - ext, -0.1,
min[0] + size[0] + ext, min[1] + size[1] + ext, 0.1
);
_path_element->setBoundingBox(bb);
return bb;
}
private:
@@ -464,12 +463,7 @@ namespace canvas
}
if( _attributes_dirty & BOUNDING_BOX )
{
dirtyBound();
// Recalculate bounding box now (prevent race condition)
getBound();
}
}
struct PathUpdateCallback:

View File

@@ -40,7 +40,6 @@ namespace canvas
void setFill(const std::string& fill);
void setBackgroundColor(const std::string& fill);
SGVec2i sizeForWidth(int w) const;
osg::Vec2 handleHit(const osg::Vec2f& pos);
virtual osg::BoundingBox computeBound() const;
@@ -96,281 +95,6 @@ namespace canvas
setBoundingBoxColor( color );
}
//----------------------------------------------------------------------------
// simplified version of osgText::Text::computeGlyphRepresentation() to
// just calculate the size for a given weight. Glpyh calculations/creating
// is not necessary for this...
SGVec2i Text::TextOSG::sizeForWidth(int w) const
{
if( _text.empty() )
return SGVec2i(0, 0);
osgText::Font* activefont = const_cast<osgText::Font*>(getActiveFont());
if( !activefont )
return SGVec2i(-1, -1);
float max_width_safe = _maximumWidth;
const_cast<TextOSG*>(this)->_maximumWidth = w;
SGRecti bb;
osg::Vec2 startOfLine_coords(0.0f,0.0f);
osg::Vec2 cursor(startOfLine_coords);
osg::Vec2 local(0.0f,0.0f);
unsigned int previous_charcode = 0;
unsigned int line_length = 0;
bool horizontal = _layout != VERTICAL;
bool kerning = true;
float hr = _characterHeight;
float wr = hr / getCharacterAspectRatio();
// osg should really care more about const :-/
osgText::String& text = const_cast<osgText::String&>(_text);
typedef osgText::String::iterator TextIterator;
for( TextIterator itr = text.begin(); itr != text.end(); )
{
// record the start of the current line
TextIterator startOfLine_itr = itr;
// find the end of the current line.
osg::Vec2 endOfLine_coords(cursor);
TextIterator endOfLine_itr =
const_cast<TextOSG*>(this)->computeLastCharacterOnLine(
endOfLine_coords, itr, text.end()
);
line_length = endOfLine_itr - startOfLine_itr;
// Set line position to correct alignment.
switch( _layout )
{
case LEFT_TO_RIGHT:
{
switch( _alignment )
{
// nothing to be done for these
//case LEFT_TOP:
//case LEFT_CENTER:
//case LEFT_BOTTOM:
//case LEFT_BASE_LINE:
//case LEFT_BOTTOM_BASE_LINE:
// break;
case CENTER_TOP:
case CENTER_CENTER:
case CENTER_BOTTOM:
case CENTER_BASE_LINE:
case CENTER_BOTTOM_BASE_LINE:
cursor.x() = (cursor.x() - endOfLine_coords.x()) * 0.5f;
break;
case RIGHT_TOP:
case RIGHT_CENTER:
case RIGHT_BOTTOM:
case RIGHT_BASE_LINE:
case RIGHT_BOTTOM_BASE_LINE:
cursor.x() = cursor.x() - endOfLine_coords.x();
break;
default:
break;
}
break;
}
case RIGHT_TO_LEFT:
{
switch( _alignment )
{
case LEFT_TOP:
case LEFT_CENTER:
case LEFT_BOTTOM:
case LEFT_BASE_LINE:
case LEFT_BOTTOM_BASE_LINE:
cursor.x() = 2 * cursor.x() - endOfLine_coords.x();
break;
case CENTER_TOP:
case CENTER_CENTER:
case CENTER_BOTTOM:
case CENTER_BASE_LINE:
case CENTER_BOTTOM_BASE_LINE:
cursor.x() = cursor.x()
+ (cursor.x() - endOfLine_coords.x()) * 0.5f;
break;
// nothing to be done for these
//case RIGHT_TOP:
//case RIGHT_CENTER:
//case RIGHT_BOTTOM:
//case RIGHT_BASE_LINE:
//case RIGHT_BOTTOM_BASE_LINE:
// break;
default:
break;
}
break;
}
case VERTICAL:
{
switch( _alignment )
{
// TODO: current behaviour top baselines lined up in both cases - need to implement
// top of characters alignment - Question is this necessary?
// ... otherwise, nothing to be done for these 6 cases
//case LEFT_TOP:
//case CENTER_TOP:
//case RIGHT_TOP:
// break;
//case LEFT_BASE_LINE:
//case CENTER_BASE_LINE:
//case RIGHT_BASE_LINE:
// break;
case LEFT_CENTER:
case CENTER_CENTER:
case RIGHT_CENTER:
cursor.y() = cursor.y()
+ (cursor.y() - endOfLine_coords.y()) * 0.5f;
break;
case LEFT_BOTTOM_BASE_LINE:
case CENTER_BOTTOM_BASE_LINE:
case RIGHT_BOTTOM_BASE_LINE:
cursor.y() = cursor.y() - (line_length * _characterHeight);
break;
case LEFT_BOTTOM:
case CENTER_BOTTOM:
case RIGHT_BOTTOM:
cursor.y() = 2 * cursor.y() - endOfLine_coords.y();
break;
default:
break;
}
break;
}
}
if( itr != endOfLine_itr )
{
for(;itr != endOfLine_itr;++itr)
{
unsigned int charcode = *itr;
osgText::Glyph* glyph = activefont->getGlyph(_fontSize, charcode);
if( glyph )
{
float width = (float) (glyph->getWidth()) * wr;
float height = (float) (glyph->getHeight()) * hr;
if( _layout == RIGHT_TO_LEFT )
{
cursor.x() -= glyph->getHorizontalAdvance() * wr;
}
// adjust cursor position w.r.t any kerning.
if( kerning && previous_charcode )
{
switch( _layout )
{
case LEFT_TO_RIGHT:
{
osg::Vec2 delta( activefont->getKerning( previous_charcode,
charcode,
_kerningType ) );
cursor.x() += delta.x() * wr;
cursor.y() += delta.y() * hr;
break;
}
case RIGHT_TO_LEFT:
{
osg::Vec2 delta( activefont->getKerning( charcode,
previous_charcode,
_kerningType ) );
cursor.x() -= delta.x() * wr;
cursor.y() -= delta.y() * hr;
break;
}
case VERTICAL:
break; // no kerning when vertical.
}
}
local = cursor;
osg::Vec2 bearing( horizontal ? glyph->getHorizontalBearing()
: glyph->getVerticalBearing() );
local.x() += bearing.x() * wr;
local.y() += bearing.y() * hr;
// set up the coords of the quad
osg::Vec2 upLeft = local + osg::Vec2(0.f, height);
osg::Vec2 lowLeft = local;
osg::Vec2 lowRight = local + osg::Vec2(width, 0.f);
osg::Vec2 upRight = local + osg::Vec2(width, height);
// move the cursor onto the next character.
// also expand bounding box
switch( _layout )
{
case LEFT_TO_RIGHT:
cursor.x() += glyph->getHorizontalAdvance() * wr;
bb.expandBy(lowLeft.x(), lowLeft.y());
bb.expandBy(upRight.x(), upRight.y());
break;
case VERTICAL:
cursor.y() -= glyph->getVerticalAdvance() * hr;
bb.expandBy(upLeft.x(), upLeft.y());
bb.expandBy(lowRight.x(), lowRight.y());
break;
case RIGHT_TO_LEFT:
bb.expandBy(lowRight.x(), lowRight.y());
bb.expandBy(upLeft.x(), upLeft.y());
break;
}
previous_charcode = charcode;
}
}
// skip over spaces and return.
while( itr != text.end() && *itr == ' ' )
++itr;
if( itr != text.end() && *itr == '\n' )
++itr;
}
else
{
++itr;
}
// move to new line.
switch( _layout )
{
case LEFT_TO_RIGHT:
{
startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing);
cursor = startOfLine_coords;
previous_charcode = 0;
break;
}
case RIGHT_TO_LEFT:
{
startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing);
cursor = startOfLine_coords;
previous_charcode = 0;
break;
}
case VERTICAL:
{
startOfLine_coords.x() += _characterHeight * (1.0 + _lineSpacing)
/ getCharacterAspectRatio();
cursor = startOfLine_coords;
previous_charcode = 0;
break;
}
}
}
const_cast<TextOSG*>(this)->_maximumWidth = max_width_safe;
return bb.size();
}
//----------------------------------------------------------------------------
osg::Vec2 Text::TextOSG::handleHit(const osg::Vec2f& pos)
{
@@ -455,17 +179,18 @@ namespace canvas
osg::BoundingBox Text::TextOSG::computeBound() const
{
osg::BoundingBox bb = osgText::Text::computeBound();
if( !bb.valid() )
return bb;
#if OSG_VERSION_LESS_THAN(3,1,0)
if( bb.valid() )
{
// TODO bounding box still doesn't seem always right (eg. with center
// horizontal alignment not completely accurate)
bb._min.y() += _offset.y();
bb._max.y() += _offset.y();
}
// TODO bounding box still doesn't seem always right (eg. with center
// horizontal alignment not completely accurate)
bb._min.y() += _offset.y();
bb._max.y() += _offset.y();
#endif
_text_element->setBoundingBox(bb);
return bb;
}
@@ -645,18 +370,6 @@ namespace canvas
}
#endif
//----------------------------------------------------------------------------
int Text::heightForWidth(int w) const
{
return _text->sizeForWidth(w).y();
}
//----------------------------------------------------------------------------
int Text::maxWidth() const
{
return _text->sizeForWidth(INT_MAX).x();
}
//----------------------------------------------------------------------------
osg::Vec2 Text::getNearestCursor(const osg::Vec2& pos) const
{

View File

@@ -46,8 +46,6 @@ namespace canvas
void setFont(const char* name);
void setAlignment(const char* align);
int heightForWidth(int w) const;
int maxWidth() const;
osg::Vec2 getNearestCursor(const osg::Vec2& pos) const;
protected:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,171 +0,0 @@
// Basic element for layouting canvas elements
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "LayoutItem.hxx"
#include <simgear/canvas/Canvas.hxx>
namespace simgear
{
namespace canvas
{
const SGVec2i LayoutItem::MAX_SIZE( SGLimits<int>::max(),
SGLimits<int>::max() );
//----------------------------------------------------------------------------
LayoutItem::LayoutItem():
_flags(0),
_size_hint(0, 0),
_min_size(0, 0),
_max_size(MAX_SIZE)
{
invalidate();
}
//----------------------------------------------------------------------------
LayoutItem::~LayoutItem()
{
}
//----------------------------------------------------------------------------
SGVec2i LayoutItem::sizeHint() const
{
if( _flags & SIZE_HINT_DIRTY )
{
_size_hint = sizeHintImpl();
_flags &= ~SIZE_HINT_DIRTY;
}
return _size_hint;
}
//----------------------------------------------------------------------------
SGVec2i LayoutItem::minimumSize() const
{
if( _flags & MINIMUM_SIZE_DIRTY )
{
_min_size = minimumSizeImpl();
_flags &= ~MINIMUM_SIZE_DIRTY;
}
return _min_size;
}
//----------------------------------------------------------------------------
SGVec2i LayoutItem::maximumSize() const
{
if( _flags & MAXIMUM_SIZE_DIRTY )
{
_max_size = maximumSizeImpl();
_flags &= ~MAXIMUM_SIZE_DIRTY;
}
return _max_size;
}
//----------------------------------------------------------------------------
bool LayoutItem::hasHeightForWidth() const
{
return false;
}
//----------------------------------------------------------------------------
int LayoutItem::heightForWidth(int w) const
{
return -1;
}
//------------------------------------------------------------------------------
int LayoutItem::minimumHeightForWidth(int w) const
{
return heightForWidth(w);
}
//----------------------------------------------------------------------------
void LayoutItem::invalidate()
{
_flags |= SIZE_INFO_DIRTY;
invalidateParent();
}
//----------------------------------------------------------------------------
void LayoutItem::invalidateParent()
{
LayoutItemRef parent = _parent.lock();
if( parent )
parent->invalidate();
}
//----------------------------------------------------------------------------
void LayoutItem::setGeometry(const SGRecti& geom)
{
_geometry = geom;
}
//----------------------------------------------------------------------------
SGRecti LayoutItem::geometry() const
{
return _geometry;
}
//----------------------------------------------------------------------------
void LayoutItem::setCanvas(const CanvasWeakPtr& canvas)
{
_canvas = canvas;
}
//----------------------------------------------------------------------------
CanvasPtr LayoutItem::getCanvas() const
{
return _canvas.lock();
}
//----------------------------------------------------------------------------
void LayoutItem::setParent(const LayoutItemWeakRef& parent)
{
_parent = parent;
LayoutItemRef parent_ref = parent.lock();
setCanvas(parent_ref ? parent_ref->_canvas : CanvasWeakPtr());
}
//----------------------------------------------------------------------------
LayoutItemRef LayoutItem::getParent() const
{
return _parent.lock();
}
//----------------------------------------------------------------------------
SGVec2i LayoutItem::sizeHintImpl() const
{
return _size_hint;
}
//----------------------------------------------------------------------------
SGVec2i LayoutItem::minimumSizeImpl() const
{
return _min_size;
}
//----------------------------------------------------------------------------
SGVec2i LayoutItem::maximumSizeImpl() const
{
return _max_size;
}
} // namespace canvas
} // namespace simgear

View File

@@ -1,149 +0,0 @@
///@file Basic element for layouting canvas elements
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_CANVAS_LAYOUT_ITEM_HXX_
#define SG_CANVAS_LAYOUT_ITEM_HXX_
#include <simgear/canvas/canvas_fwd.hxx>
#include <simgear/math/SGMath.hxx>
#include <simgear/math/SGRect.hxx>
#include <simgear/misc/stdint.hxx>
#include <simgear/structure/SGWeakReferenced.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
namespace simgear
{
namespace canvas
{
class LayoutItem;
typedef SGSharedPtr<LayoutItem> LayoutItemRef;
typedef SGWeakPtr<LayoutItem> LayoutItemWeakRef;
/**
* Base class for all layouting elements. Specializations either implement a
* layouting algorithm or a widget.
*/
class LayoutItem:
public virtual SGVirtualWeakReferenced
{
public:
/** Maximum item size (indicating no limit) */
static const SGVec2i MAX_SIZE;
LayoutItem();
virtual ~LayoutItem();
/**
* Get the preferred size of this item.
*/
SGVec2i sizeHint() const;
/**
* Get the minimum amount of the space this item requires.
*/
SGVec2i minimumSize() const;
/**
* Get the maximum amount of space this item can use.
*/
SGVec2i maximumSize() const;
virtual bool hasHeightForWidth() const;
virtual int heightForWidth(int w) const;
virtual int minimumHeightForWidth(int w) const;
/**
* Mark all cached data as invalid and require it to be recalculated.
*/
virtual void invalidate();
/**
* Mark all cached data of parent item as invalid (if it is known)
*/
void invalidateParent();
/**
* Set position and size of this element. For layouts this triggers a
* recalculation of the layout.
*/
virtual void setGeometry(const SGRecti& geom);
/**
* Get position and size of this element.
*/
virtual SGRecti geometry() const;
/**
* Set the canvas this item is attached to.
*/
virtual void setCanvas(const CanvasWeakPtr& canvas);
/**
* Get the canvas this item is attached to.
*/
CanvasPtr getCanvas() const;
/**
* Set the parent layout item (usually this is a layout).
*/
void setParent(const LayoutItemWeakRef& parent);
/**
* Get the parent layout.
*/
LayoutItemRef getParent() const;
/// Called before item is removed from a layout
virtual void onRemove() {}
protected:
friend class Canvas;
enum Flags
{
SIZE_HINT_DIRTY = 1,
MINIMUM_SIZE_DIRTY = SIZE_HINT_DIRTY << 1,
MAXIMUM_SIZE_DIRTY = MINIMUM_SIZE_DIRTY << 1,
SIZE_INFO_DIRTY = SIZE_HINT_DIRTY
| MINIMUM_SIZE_DIRTY
| MAXIMUM_SIZE_DIRTY,
LAST_FLAG = MAXIMUM_SIZE_DIRTY
};
CanvasWeakPtr _canvas;
LayoutItemWeakRef _parent;
SGRecti _geometry;
mutable uint32_t _flags;
mutable SGVec2i _size_hint,
_min_size,
_max_size;
virtual SGVec2i sizeHintImpl() const;
virtual SGVec2i minimumSizeImpl() const;
virtual SGVec2i maximumSizeImpl() const;
};
} // namespace canvas
} // namespace simgear
#endif /* SG_CANVAS_LAYOUT_ITEM_HXX_ */

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,461 +0,0 @@
// Testing canvas layouting system
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#define BOOST_TEST_MODULE canvas_layout
#include <BoostTestTargetConfig.h>
#include "BoxLayout.hxx"
#include "NasalWidget.hxx"
#include <simgear/debug/logstream.hxx>
#include <cstdlib>
//------------------------------------------------------------------------------
struct SetLogLevelFixture
{
SetLogLevelFixture()
{
sglog().set_log_priority(SG_DEBUG);
}
};
BOOST_GLOBAL_FIXTURE(SetLogLevelFixture);
//------------------------------------------------------------------------------
namespace sc = simgear::canvas;
class TestWidget:
public sc::LayoutItem
{
public:
TestWidget( const SGVec2i& min_size,
const SGVec2i& size_hint,
const SGVec2i& max_size = MAX_SIZE )
{
_size_hint = size_hint;
_min_size = min_size;
_max_size = max_size;
}
TestWidget(const TestWidget& rhs)
{
_size_hint = rhs._size_hint;
_min_size = rhs._min_size;
_max_size = rhs._max_size;
}
void setMinSize(const SGVec2i& size) { _min_size = size; }
void setMaxSize(const SGVec2i& size) { _max_size = size; }
void setSizeHint(const SGVec2i& size) { _size_hint = size; }
virtual void setGeometry(const SGRecti& geom) { _geom = geom; }
virtual SGRecti geometry() const { return _geom; }
protected:
SGRecti _geom;
virtual SGVec2i sizeHintImpl() const { return _size_hint; }
virtual SGVec2i minimumSizeImpl() const { return _min_size; }
virtual SGVec2i maximumSizeImpl() const { return _max_size; }
};
class TestWidgetHFW:
public TestWidget
{
public:
TestWidgetHFW( const SGVec2i& min_size,
const SGVec2i& size_hint,
const SGVec2i& max_size = MAX_SIZE ):
TestWidget(min_size, size_hint, max_size)
{
}
virtual bool hasHeightForWidth() const
{
return true;
}
virtual int heightForWidth(int w) const
{
return _size_hint.x() * _size_hint.y() / w;
}
virtual int minimumHeightForWidth(int w) const
{
return _min_size.x() * _min_size.y() / w;
}
};
typedef SGSharedPtr<TestWidget> TestWidgetRef;
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( horizontal_layout )
{
sc::BoxLayout box_layout(sc::BoxLayout::BottomToTop);
box_layout.setSpacing(5);
BOOST_CHECK_EQUAL(box_layout.direction(), sc::BoxLayout::BottomToTop);
BOOST_CHECK_EQUAL(box_layout.spacing(), 5);
box_layout.setDirection(sc::BoxLayout::LeftToRight);
box_layout.setSpacing(9);
BOOST_CHECK_EQUAL(box_layout.direction(), sc::BoxLayout::LeftToRight);
BOOST_CHECK_EQUAL(box_layout.spacing(), 9);
TestWidgetRef fixed_size_widget( new TestWidget( SGVec2i(16, 16),
SGVec2i(16, 16),
SGVec2i(16, 16) ) );
box_layout.addItem(fixed_size_widget);
BOOST_CHECK_EQUAL(box_layout.minimumSize(), SGVec2i(16, 16));
BOOST_CHECK_EQUAL(box_layout.sizeHint(), SGVec2i(16, 16));
BOOST_CHECK_EQUAL(box_layout.maximumSize(), SGVec2i(16, 16));
TestWidgetRef limited_resize_widget( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32),
SGVec2i(256, 64) ) );
box_layout.addItem(limited_resize_widget);
// Combined sizes of both widget plus the padding between them
BOOST_CHECK_EQUAL(box_layout.minimumSize(), SGVec2i(41, 16));
BOOST_CHECK_EQUAL(box_layout.sizeHint(), SGVec2i(57, 32));
BOOST_CHECK_EQUAL(box_layout.maximumSize(), SGVec2i(281, 64));
// Test with different spacing/padding
box_layout.setSpacing(5);
BOOST_CHECK_EQUAL(box_layout.minimumSize(), SGVec2i(37, 16));
BOOST_CHECK_EQUAL(box_layout.sizeHint(), SGVec2i(53, 32));
BOOST_CHECK_EQUAL(box_layout.maximumSize(), SGVec2i(277, 64));
box_layout.setGeometry(SGRecti(0, 0, 128, 32));
// Fixed size for first widget and remaining space goes to second widget
BOOST_CHECK_EQUAL(fixed_size_widget->geometry(), SGRecti(0, 8, 16, 16));
BOOST_CHECK_EQUAL(limited_resize_widget->geometry(), SGRecti(21, 0, 107, 32));
TestWidgetRef stretch_widget( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32),
SGVec2i(128, 32) ) );
box_layout.addItem(stretch_widget, 1);
box_layout.update();
BOOST_CHECK_EQUAL(box_layout.minimumSize(), SGVec2i(58, 16));
BOOST_CHECK_EQUAL(box_layout.sizeHint(), SGVec2i(90, 32));
BOOST_CHECK_EQUAL(box_layout.maximumSize(), SGVec2i(410, 64));
// Due to the stretch factor only the last widget gets additional space. All
// other widgets get the preferred size.
BOOST_CHECK_EQUAL(fixed_size_widget->geometry(), SGRecti(0, 8, 16, 16));
BOOST_CHECK_EQUAL(limited_resize_widget->geometry(), SGRecti(21, 0, 32, 32));
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(58, 0, 70, 32));
// Test stretch factor
TestWidgetRef fast_stretch( new TestWidget(*stretch_widget) );
sc::BoxLayout box_layout_stretch(sc::BoxLayout::LeftToRight);
box_layout_stretch.addItem(stretch_widget, 1);
box_layout_stretch.addItem(fast_stretch, 2);
box_layout_stretch.setGeometry(SGRecti(0,0,128,32));
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(0, 0, 41, 32));
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(46, 0, 82, 32));
box_layout_stretch.setGeometry(SGRecti(0,0,256,32));
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(0, 0, 123, 32));
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(128, 0, 128, 32));
// Test superflous space to padding
box_layout_stretch.setGeometry(SGRecti(0,0,512,32));
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(83, 0, 128, 32));
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(300, 0, 128, 32));
// Test more space then preferred, but less than maximum
{
sc::HBoxLayout hbox;
TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32),
SGVec2i(9999, 32) ) ),
w2( new TestWidget(*w1) );
hbox.addItem(w1);
hbox.addItem(w2);
hbox.setGeometry( SGRecti(0, 0, 256, 32) );
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 126, 32));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(131, 0, 125, 32));
hbox.setStretch(0, 1);
hbox.setStretch(1, 1);
BOOST_CHECK_EQUAL(hbox.stretch(0), 1);
BOOST_CHECK_EQUAL(hbox.stretch(1), 1);
hbox.update();
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 125, 32));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(130, 0, 126, 32));
BOOST_REQUIRE( hbox.setStretchFactor(w1, 2) );
BOOST_REQUIRE( hbox.setStretchFactor(w2, 3) );
BOOST_CHECK_EQUAL(hbox.stretch(0), 2);
BOOST_CHECK_EQUAL(hbox.stretch(1), 3);
hbox.removeItem(w1);
BOOST_CHECK( !hbox.setStretchFactor(w1, 0) );
}
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( spacer_layouting )
{
sc::HBoxLayout hbox;
TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32),
SGVec2i(9999, 9999) ) ),
w2( new TestWidget(*w1) );
hbox.addItem(w1);
hbox.addItem(w2);
hbox.addStretch(1);
BOOST_CHECK_EQUAL(hbox.minimumSize(), SGVec2i(37, 16));
BOOST_CHECK_EQUAL(hbox.sizeHint(), SGVec2i(69, 32));
BOOST_CHECK_EQUAL(hbox.maximumSize(), sc::LayoutItem::MAX_SIZE);
hbox.setGeometry(SGRecti(0, 0, 256, 40));
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 32, 40));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(37, 0, 32, 40));
// now center with increased spacing between both widgets
hbox.insertStretch(0, 1);
hbox.insertSpacing(2, 10);
BOOST_CHECK_EQUAL(hbox.minimumSize(), SGVec2i(47, 16));
BOOST_CHECK_EQUAL(hbox.sizeHint(), SGVec2i(79, 32));
BOOST_CHECK_EQUAL(hbox.maximumSize(), sc::LayoutItem::MAX_SIZE);
hbox.update();
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(88, 0, 32, 40));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(135, 0, 32, 40));
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( vertical_layout)
{
sc::BoxLayout vbox(sc::BoxLayout::TopToBottom);
vbox.setSpacing(7);
TestWidgetRef fixed_size_widget( new TestWidget( SGVec2i(16, 16),
SGVec2i(16, 16),
SGVec2i(16, 16) ) );
TestWidgetRef limited_resize_widget( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32),
SGVec2i(256, 64) ) );
vbox.addItem(fixed_size_widget);
vbox.addItem(limited_resize_widget);
BOOST_CHECK_EQUAL(vbox.minimumSize(), SGVec2i(16, 39));
BOOST_CHECK_EQUAL(vbox.sizeHint(), SGVec2i(32, 55));
BOOST_CHECK_EQUAL(vbox.maximumSize(), SGVec2i(256, 87));
vbox.setGeometry(SGRecti(10, 20, 16, 55));
BOOST_CHECK_EQUAL(fixed_size_widget->geometry(), SGRecti(10, 20, 16, 16));
BOOST_CHECK_EQUAL(limited_resize_widget->geometry(), SGRecti(10, 43, 16, 32));
vbox.setDirection(sc::BoxLayout::BottomToTop);
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( boxlayout_insert_remove )
{
sc::HBoxLayout hbox;
BOOST_CHECK_EQUAL(hbox.count(), 0);
BOOST_CHECK(!hbox.itemAt(0));
BOOST_CHECK(!hbox.takeAt(0));
TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32),
SGVec2i(9999, 32) ) ),
w2( new TestWidget(*w1) );
hbox.addItem(w1);
BOOST_CHECK_EQUAL(hbox.count(), 1);
BOOST_CHECK_EQUAL(hbox.itemAt(0), w1);
hbox.insertItem(0, w2);
BOOST_CHECK_EQUAL(hbox.count(), 2);
BOOST_CHECK_EQUAL(hbox.itemAt(0), w2);
BOOST_CHECK_EQUAL(hbox.itemAt(1), w1);
hbox.removeItem(w2);
BOOST_CHECK_EQUAL(hbox.count(), 1);
BOOST_CHECK_EQUAL(hbox.itemAt(0), w1);
hbox.addItem(w2);
BOOST_CHECK_EQUAL(hbox.count(), 2);
hbox.clear();
BOOST_CHECK_EQUAL(hbox.count(), 0);
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( boxlayout_hfw )
{
TestWidgetRef w1( new TestWidgetHFW( SGVec2i(16, 16),
SGVec2i(32, 32) ) ),
w2( new TestWidgetHFW( SGVec2i(24, 24),
SGVec2i(48, 48) ) );
BOOST_CHECK_EQUAL(w1->heightForWidth(16), 64);
BOOST_CHECK_EQUAL(w1->minimumHeightForWidth(16), 16);
BOOST_CHECK_EQUAL(w2->heightForWidth(24), 96);
BOOST_CHECK_EQUAL(w2->minimumHeightForWidth(24), 24);
TestWidgetRef w_no_hfw( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32) ) );
BOOST_CHECK(!w_no_hfw->hasHeightForWidth());
BOOST_CHECK_EQUAL(w_no_hfw->heightForWidth(16), -1);
BOOST_CHECK_EQUAL(w_no_hfw->minimumHeightForWidth(16), -1);
// horizontal
sc::HBoxLayout hbox;
hbox.setSpacing(5);
hbox.addItem(w1);
hbox.addItem(w2);
BOOST_CHECK_EQUAL(hbox.heightForWidth(45), w2->heightForWidth(24));
BOOST_CHECK_EQUAL(hbox.heightForWidth(85), w2->heightForWidth(48));
hbox.addItem(w_no_hfw);
BOOST_CHECK_EQUAL(hbox.heightForWidth(66), 96);
BOOST_CHECK_EQUAL(hbox.heightForWidth(122), 48);
BOOST_CHECK_EQUAL(hbox.minimumHeightForWidth(66), 24);
BOOST_CHECK_EQUAL(hbox.minimumHeightForWidth(122), 16);
hbox.setGeometry(SGRecti(0, 0, 66, 24));
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 16, 24));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(21, 0, 24, 24));
BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(50, 0, 16, 24));
// vertical
sc::VBoxLayout vbox;
vbox.setSpacing(5);
vbox.addItem(w1);
vbox.addItem(w2);
BOOST_CHECK_EQUAL(vbox.heightForWidth(24), 143);
BOOST_CHECK_EQUAL(vbox.heightForWidth(48), 74);
BOOST_CHECK_EQUAL(vbox.minimumHeightForWidth(24), 39);
BOOST_CHECK_EQUAL(vbox.minimumHeightForWidth(48), 22);
vbox.addItem(w_no_hfw);
BOOST_CHECK_EQUAL(vbox.heightForWidth(24), 180);
BOOST_CHECK_EQUAL(vbox.heightForWidth(48), 111);
BOOST_CHECK_EQUAL(vbox.minimumHeightForWidth(24), 60);
BOOST_CHECK_EQUAL(vbox.minimumHeightForWidth(48), 43);
SGVec2i min_size = vbox.minimumSize(),
size_hint = vbox.sizeHint();
BOOST_CHECK_EQUAL(min_size, SGVec2i(24, 66));
BOOST_CHECK_EQUAL(size_hint, SGVec2i(48, 122));
vbox.setGeometry(SGRecti(0, 0, 24, 122));
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 24, 33));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(0, 38, 24, 47));
BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(0, 90, 24, 32));
// Vertical layouting modifies size hints, so check if they are correctly
// restored
BOOST_CHECK_EQUAL(min_size, vbox.minimumSize());
BOOST_CHECK_EQUAL(size_hint, vbox.sizeHint());
vbox.setGeometry(SGRecti(0, 0, 50, 122));
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 50, 25));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(0, 30, 50, 51));
BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(0, 86, 50, 36));
// Same geometry as before -> should get same widget geometry
// (check internal size hint cache updates correctly)
vbox.setGeometry(SGRecti(0, 0, 24, 122));
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 24, 33));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(0, 38, 24, 47));
BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(0, 90, 24, 32));
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( nasal_widget )
{
naContext c = naNewContext();
naRef me = naNewHash(c);
sc::NasalWidgetRef w( new sc::NasalWidget(me) );
// Default layout sizes (no user set values)
BOOST_CHECK_EQUAL(w->minimumSize(), SGVec2i(16, 16));
BOOST_CHECK_EQUAL(w->sizeHint(), SGVec2i(32, 32));
BOOST_CHECK_EQUAL(w->maximumSize(), sc::LayoutItem::MAX_SIZE);
// Changed layout sizes
w->setLayoutMinimumSize( SGVec2i(2, 12) );
w->setLayoutSizeHint( SGVec2i(3, 13) );
w->setLayoutMaximumSize( SGVec2i(4, 14) );
BOOST_CHECK_EQUAL(w->minimumSize(), SGVec2i(2, 12));
BOOST_CHECK_EQUAL(w->sizeHint(), SGVec2i(3, 13));
BOOST_CHECK_EQUAL(w->maximumSize(), SGVec2i(4, 14));
// User set values (overwrite layout sizes)
w->setMinimumSize( SGVec2i(15, 16) );
w->setSizeHint( SGVec2i(17, 18) );
w->setMaximumSize( SGVec2i(19, 20) );
BOOST_CHECK_EQUAL(w->minimumSize(), SGVec2i(15, 16));
BOOST_CHECK_EQUAL(w->sizeHint(), SGVec2i(17, 18));
BOOST_CHECK_EQUAL(w->maximumSize(), SGVec2i(19, 20));
// Only vertical user set values (layout/default for horizontal hints)
w->setMinimumSize( SGVec2i(0, 21) );
w->setSizeHint( SGVec2i(0, 22) );
w->setMaximumSize( SGVec2i(SGLimits<int>::max(), 23) );
BOOST_CHECK_EQUAL(w->minimumSize(), SGVec2i(2, 21));
BOOST_CHECK_EQUAL(w->sizeHint(), SGVec2i(3, 22));
BOOST_CHECK_EQUAL(w->maximumSize(), SGVec2i(4, 23));
naFreeContext(c);
}

View File

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

View File

@@ -70,7 +70,6 @@ const char* debugClassToString(sgDebugClass c)
case SG_ENVIRONMENT:return "environment";
case SG_SOUND: return "sound";
case SG_NAVAID: return "navaid";
case SG_GUI: return "gui";
default: return "unknown";
}
}
@@ -439,4 +438,4 @@ void requestConsole()
global_privateLogstream->requestConsole();
}
} // of namespace simgear
} // of namespace simgear

View File

@@ -122,28 +122,27 @@ public:
// socket-level errors
virtual void handleError(int error)
{
const char* errStr = strerror(error);
if (!activeRequest)
{
// connection level failure, eg name lookup or routing
// we won't have an active request yet, so let's fail all of the
// requests since we presume it's a systematic failure for
// the host in question
if (error == ENOENT) {
// name lookup failure
// we won't have an active request yet, so the logic below won't
// fire to actually call setFailure. Let's fail all of the requests
BOOST_FOREACH(Request_ptr req, sentRequests) {
req->setFailure(error, errStr);
req->setFailure(error, "hostname lookup failure");
}
BOOST_FOREACH(Request_ptr req, queuedRequests) {
req->setFailure(error, errStr);
req->setFailure(error, "hostname lookup failure");
}
// name lookup failure, abandon all requests on this connection
sentRequests.clear();
queuedRequests.clear();
}
NetChat::handleError(error);
if (activeRequest) {
activeRequest->setFailure(error, errStr);
SG_LOG(SG_IO, SG_INFO, "HTTP socket error");
activeRequest->setFailure(error, "socket error");
activeRequest = NULL;
_contentDecoder.reset();
}
@@ -253,7 +252,6 @@ public:
if (state == STATE_CLOSED) {
if (!connectToHost()) {
return;
}

View File

@@ -39,7 +39,7 @@ Request* Request::done(const Callback& cb)
if( _ready_state == DONE )
cb(this);
else
_cb_done.push_back(cb);
_cb_done = cb;
return this;
}
@@ -50,7 +50,7 @@ Request* Request::fail(const Callback& cb)
if( _ready_state == FAILED )
cb(this);
else
_cb_fail.push_back(cb);
_cb_fail = cb;
return this;
}
@@ -61,7 +61,7 @@ Request* Request::always(const Callback& cb)
if( isComplete() )
cb(this);
else
_cb_always.push_back(cb);
_cb_always = cb;
return this;
}
@@ -272,16 +272,6 @@ std::string Request::hostAndPort() const
return u.substr(schemeEnd + 3, hostEnd - (schemeEnd + 3));
}
//------------------------------------------------------------------------------
std::string Request::responseMime() const
{
std::string content_type = _responseHeaders.get("content-type");
if( content_type.empty() )
return "application/octet-stream";
return content_type.substr(0, content_type.find(';'));
}
//------------------------------------------------------------------------------
void Request::setResponseLength(unsigned int l)
{
@@ -304,9 +294,7 @@ void Request::setFailure(int code, const std::string& reason)
{
_responseStatus = code;
_responseReason = reason;
if( !isComplete() )
setReadyState(FAILED);
setReadyState(FAILED);
}
//------------------------------------------------------------------------------
@@ -321,19 +309,22 @@ void Request::setReadyState(ReadyState state)
onDone();
onAlways();
_cb_done(this);
if( _cb_done )
_cb_done(this);
}
else if( state == FAILED )
{
onFail();
onAlways();
_cb_fail(this);
if( _cb_fail )
_cb_fail(this);
}
else
return;
_cb_always(this);
if( _cb_always )
_cb_always(this);
}
//------------------------------------------------------------------------------
@@ -345,6 +336,9 @@ void Request::abort()
//----------------------------------------------------------------------------
void Request::abort(const std::string& reason)
{
if( isComplete() )
return;
setFailure(-1, reason);
_willClose = true;
}

View File

@@ -3,13 +3,12 @@
#include <map>
#include <simgear/structure/function_list.hxx>
#include <simgear/structure/map.hxx>
#include <simgear/structure/SGReferenced.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
#include <simgear/math/sg_types.hxx>
#include <boost/bind.hpp>
#include <boost/function.hpp>
class SGPropertyNode;
@@ -47,21 +46,15 @@ public:
{ return _request_headers.get(key); }
/**
* Add a handler to be called when the request successfully completes.
* Set the handler to be called when the request successfully completes.
*
* @note If the request is already complete, the handler is called
* immediately.
*/
Request* done(const Callback& cb);
template<class C>
Request* done(C* instance, void (C::*mem_func)(Request*))
{
return done(boost::bind(mem_func, instance, _1));
}
/**
* Add a handler to be called when the request completes or aborts with an
* Set the handler to be called when the request completes or aborts with an
* error.
*
* @note If the request has already failed, the handler is called
@@ -69,27 +62,15 @@ public:
*/
Request* fail(const Callback& cb);
template<class C>
Request* fail(C* instance, void (C::*mem_func)(Request*))
{
return fail(boost::bind(mem_func, instance, _1));
}
/**
* Add a handler to be called when the request either successfully completes
* or fails.
* Set the handler to be called when the request either successfully
* completes or fails.
*
* @note If the request is already complete or has already failed, the
* handler is called immediately.
*/
Request* always(const Callback& cb);
template<class C>
Request* always(C* instance, void (C::*mem_func)(Request*))
{
return always(boost::bind(mem_func, instance, _1));
}
/**
* Set the data for the body of the request. The request is automatically
* send using the POST method.
@@ -118,8 +99,6 @@ public:
StringMap const& responseHeaders() const
{ return _responseHeaders; }
std::string responseMime() const;
virtual int responseCode() const
{ return _responseStatus; }
@@ -224,9 +203,9 @@ private:
unsigned int _responseLength;
unsigned int _receivedBodyBytes;
function_list<Callback> _cb_done,
_cb_fail,
_cb_always;
Callback _cb_done,
_cb_fail,
_cb_always;
ReadyState _ready_state;
bool _willClose;

View File

@@ -82,9 +82,9 @@ int main( int argc, char **argv ) {
group_list pts_v = obj.get_pts_v();
group_list pts_n = obj.get_pts_n();
for ( i = 0; i < (int)pts_v.size(); ++i ) {
material = pt_materials[i];
vertex_index = pts_v[i];
normal_index = pts_n[i];
material = pt_materials[i];
vertex_index = pts_v[i];
normal_index = pts_n[i];
cout << "# usemtl " << material << endl;
cout << "pt ";
for ( j = 0; j < (int)vertex_index.size(); ++j ) {
@@ -97,12 +97,12 @@ int main( int argc, char **argv ) {
string_list tri_materials = obj.get_tri_materials();
group_list tris_v = obj.get_tris_v();
group_list tris_n = obj.get_tris_n();
group_tci_list tris_tc = obj.get_tris_tcs();
group_list tris_tc = obj.get_tris_tc();
for ( i = 0; i < (int)tris_v.size(); ++i ) {
material = tri_materials[i];
vertex_index = tris_v[i];
normal_index = tris_n[i];
tex_index = tris_tc[0][i];
material = tri_materials[i];
vertex_index = tris_v[i];
normal_index = tris_n[i];
tex_index = tris_tc[i];
cout << "# usemtl " << material << endl;
cout << "f ";
for ( j = 0; j < (int)vertex_index.size(); ++j ) {
@@ -120,12 +120,12 @@ int main( int argc, char **argv ) {
string_list strip_materials = obj.get_strip_materials();
group_list strips_v = obj.get_strips_v();
group_list strips_n = obj.get_strips_n();
group_tci_list strips_tc = obj.get_strips_tcs();
group_list strips_tc = obj.get_strips_tc();
for ( i = 0; i < (int)strips_v.size(); ++i ) {
material = strip_materials[i];
vertex_index = strips_v[i];
normal_index = strips_n[i];
tex_index = strips_tc[0][i];
material = strip_materials[i];
vertex_index = strips_v[i];
normal_index = strips_n[i];
tex_index = strips_tc[i];
cout << "# usemtl " << material << endl;
cout << "ts ";
for ( j = 0; j < (int)vertex_index.size(); ++j ) {
@@ -142,12 +142,12 @@ int main( int argc, char **argv ) {
string_list fan_materials = obj.get_fan_materials();
group_list fans_v = obj.get_fans_v();
group_list fans_n = obj.get_fans_n();
group_tci_list fans_tc = obj.get_fans_tcs();
group_list fans_tc = obj.get_fans_tc();
for ( i = 0; i < (int)fans_v.size(); ++i ) {
material = fan_materials[i];
vertex_index = fans_v[i];
normal_index = fans_n[i];
tex_index = fans_tc[0][i];
material = fan_materials[i];
vertex_index = fans_v[i];
normal_index = fans_n[i];
tex_index = fans_tc[i];
cout << "# usemtl " << material << endl;
cout << "tf ";
for ( j = 0; j < (int)vertex_index.size(); ++j ) {

View File

@@ -58,11 +58,9 @@ enum sgObjectTypes {
SG_BOUNDING_SPHERE = 0,
SG_VERTEX_LIST = 1,
SG_COLOR_LIST = 4,
SG_NORMAL_LIST = 2,
SG_TEXCOORD_LIST = 3,
SG_COLOR_LIST = 4,
SG_VA_FLOAT_LIST = 5,
SG_VA_INTEGER_LIST = 6,
SG_POINTS = 9,
@@ -72,32 +70,15 @@ enum sgObjectTypes {
};
enum sgIndexTypes {
SG_IDX_VERTICES = 0x01,
SG_IDX_NORMALS = 0x02,
SG_IDX_COLORS = 0x04,
SG_IDX_TEXCOORDS_0 = 0x08,
SG_IDX_TEXCOORDS_1 = 0x10,
SG_IDX_TEXCOORDS_2 = 0x20,
SG_IDX_TEXCOORDS_3 = 0x40,
};
enum sgVertexAttributeTypes {
// vertex attributes
SG_VA_INTEGER_0 = 0x00000001,
SG_VA_INTEGER_1 = 0x00000002,
SG_VA_INTEGER_2 = 0x00000004,
SG_VA_INTEGER_3 = 0x00000008,
SG_VA_FLOAT_0 = 0x00000100,
SG_VA_FLOAT_1 = 0x00000200,
SG_VA_FLOAT_2 = 0x00000400,
SG_VA_FLOAT_3 = 0x00000800,
SG_IDX_VERTICES = 0x01,
SG_IDX_NORMALS = 0x02,
SG_IDX_COLORS = 0x04,
SG_IDX_TEXCOORDS = 0x08
};
enum sgPropertyTypes {
SG_MATERIAL = 0,
SG_INDEX_TYPES = 1,
SG_VERT_ATTRIBS = 2
SG_INDEX_TYPES = 1
};
@@ -149,17 +130,6 @@ public:
}
}
float readInt()
{
unsigned int* p = reinterpret_cast<unsigned int*>(ptr + offset);
if ( sgIsBigEndian() ) {
sgEndianSwap((uint32_t *) p);
}
offset += sizeof(unsigned int);
return *p;
}
SGVec3d readVec3d()
{
double* p = reinterpret_cast<double*>(ptr + offset);
@@ -232,19 +202,15 @@ template <class T>
static void read_indices(char* buffer,
size_t bytes,
int indexMask,
int vaMask,
int_list& vertices,
int_list& normals,
int_list& colors,
tci_list& texCoords,
vai_list& vas
)
int_list& texCoords)
{
const int indexSize = sizeof(T) * std::bitset<32>((int)indexMask).count();
const int vaSize = sizeof(T) * std::bitset<32>((int)vaMask).count();
const int count = bytes / (indexSize + vaSize);
const int indexSize = sizeof(T) * std::bitset<32>(indexMask).count();
const int count = bytes / indexSize;
// fix endian-ness of the whole lot, if required
// fix endian-ness of the whole lot, if required
if (sgIsBigEndian()) {
int indices = bytes / sizeof(T);
T* src = reinterpret_cast<T*>(buffer);
@@ -258,21 +224,7 @@ static void read_indices(char* buffer,
if (indexMask & SG_IDX_VERTICES) vertices.push_back(*src++);
if (indexMask & SG_IDX_NORMALS) normals.push_back(*src++);
if (indexMask & SG_IDX_COLORS) colors.push_back(*src++);
if (indexMask & SG_IDX_TEXCOORDS_0) texCoords[0].push_back(*src++);
if (indexMask & SG_IDX_TEXCOORDS_1) texCoords[1].push_back(*src++);
if (indexMask & SG_IDX_TEXCOORDS_2) texCoords[2].push_back(*src++);
if (indexMask & SG_IDX_TEXCOORDS_3) texCoords[3].push_back(*src++);
if ( vaMask ) {
if (vaMask & SG_VA_INTEGER_0) vas[0].push_back(*src++);
if (vaMask & SG_VA_INTEGER_1) vas[1].push_back(*src++);
if (vaMask & SG_VA_INTEGER_2) vas[2].push_back(*src++);
if (vaMask & SG_VA_INTEGER_3) vas[3].push_back(*src++);
if (vaMask & SG_VA_FLOAT_0) vas[4].push_back(*src++);
if (vaMask & SG_VA_FLOAT_1) vas[5].push_back(*src++);
if (vaMask & SG_VA_FLOAT_2) vas[6].push_back(*src++);
if (vaMask & SG_VA_FLOAT_3) vas[7].push_back(*src++);
}
if (indexMask & SG_IDX_TEXCOORDS) texCoords.push_back(*src++);
} // of elements in the index
}
@@ -297,19 +249,15 @@ void write_indice(gzFile fp, uint32_t value)
template <class T>
void write_indices(gzFile fp,
unsigned char indexMask,
unsigned int vaMask,
void write_indices(gzFile fp, unsigned char indexMask,
const int_list& vertices,
const int_list& normals,
const int_list& colors,
const tci_list& texCoords,
const vai_list& vas )
const int_list& texCoords)
{
unsigned int count = vertices.size();
const int indexSize = sizeof(T) * std::bitset<32>((int)indexMask).count();
const int vaSize = sizeof(T) * std::bitset<32>((int)vaMask).count();
sgWriteUInt(fp, (indexSize + vaSize) * count);
const int indexSize = sizeof(T) * std::bitset<32>(indexMask).count();
sgWriteUInt(fp, indexSize * count);
for (unsigned int i=0; i < count; ++i) {
write_indice(fp, static_cast<T>(vertices[i]));
@@ -320,45 +268,8 @@ void write_indices(gzFile fp,
if (indexMask & SG_IDX_COLORS) {
write_indice(fp, static_cast<T>(colors[i]));
}
if (indexMask & SG_IDX_TEXCOORDS_0) {
write_indice(fp, static_cast<T>(texCoords[0][i]));
}
if (indexMask & SG_IDX_TEXCOORDS_1) {
write_indice(fp, static_cast<T>(texCoords[1][i]));
}
if (indexMask & SG_IDX_TEXCOORDS_2) {
write_indice(fp, static_cast<T>(texCoords[2][i]));
}
if (indexMask & SG_IDX_TEXCOORDS_3) {
write_indice(fp, static_cast<T>(texCoords[3][i]));
}
if (vaMask) {
if (vaMask & SG_VA_INTEGER_0) {
write_indice(fp, static_cast<T>(vas[0][i]));
}
if (vaMask & SG_VA_INTEGER_1) {
write_indice(fp, static_cast<T>(vas[1][i]));
}
if (vaMask & SG_VA_INTEGER_2) {
write_indice(fp, static_cast<T>(vas[2][i]));
}
if (vaMask & SG_VA_INTEGER_3) {
write_indice(fp, static_cast<T>(vas[3][i]));
}
if (vaMask & SG_VA_FLOAT_0) {
write_indice(fp, static_cast<T>(vas[4][i]));
}
if (vaMask & SG_VA_FLOAT_1) {
write_indice(fp, static_cast<T>(vas[5][i]));
}
if (vaMask & SG_VA_FLOAT_2) {
write_indice(fp, static_cast<T>(vas[6][i]));
}
if (vaMask & SG_VA_FLOAT_3) {
write_indice(fp, static_cast<T>(vas[7][i]));
}
if (indexMask & SG_IDX_TEXCOORDS) {
write_indice(fp, static_cast<T>(texCoords[i]));
}
}
}
@@ -370,15 +281,13 @@ void SGBinObject::read_object( gzFile fp,
int nproperties,
int nelements,
group_list& vertices,
group_list& normals,
group_list& colors,
group_tci_list& texCoords,
group_vai_list& vertexAttribs,
string_list& materials)
group_list& normals,
group_list& colors,
group_list& texCoords,
string_list& materials)
{
unsigned int nbytes;
unsigned int nbytes;
unsigned char idx_mask;
unsigned int vertex_attrib_mask;
int j;
sgSimpleBuffer buf( 32768 ); // 32 Kb
char material[256];
@@ -387,49 +296,26 @@ void SGBinObject::read_object( gzFile fp,
if ( obj_type == SG_POINTS ) {
idx_mask = SG_IDX_VERTICES;
} else {
idx_mask = (char)(SG_IDX_VERTICES | SG_IDX_TEXCOORDS_0);
idx_mask = (char)(SG_IDX_VERTICES | SG_IDX_TEXCOORDS);
}
vertex_attrib_mask = 0;
for ( j = 0; j < nproperties; ++j ) {
char prop_type;
sgReadChar( fp, &prop_type );
sgReadUInt( fp, &nbytes );
buf.resize(nbytes);
char *ptr = buf.get_ptr();
switch( prop_type )
{
case SG_MATERIAL:
sgReadBytes( fp, nbytes, ptr );
if (nbytes > 255) {
nbytes = 255;
}
strncpy( material, ptr, nbytes );
material[nbytes] = '\0';
break;
case SG_INDEX_TYPES:
if (nbytes == 1) {
sgReadChar( fp, (char *)&idx_mask );
} else {
sgReadBytes( fp, nbytes, ptr );
}
break;
case SG_VERT_ATTRIBS:
if (nbytes == 4) {
sgReadUInt( fp, &vertex_attrib_mask );
} else {
sgReadBytes( fp, nbytes, ptr );
}
break;
default:
sgReadBytes( fp, nbytes, ptr );
SG_LOG(SG_IO, SG_ALERT, "Found UNKNOWN property type with nbytes == " << nbytes << " mask is " << (int)idx_mask );
break;
sgReadBytes( fp, nbytes, ptr );
if ( prop_type == SG_MATERIAL ) {
if (nbytes > 255) {
nbytes = 255;
}
strncpy( material, ptr, nbytes );
material[nbytes] = '\0';
// cout << "material type = " << material << endl;
} else if ( prop_type == SG_INDEX_TYPES ) {
idx_mask = ptr[0];
//cout << std::hex << "index mask:" << idx_mask << std::dec << endl;
}
}
@@ -437,7 +323,7 @@ void SGBinObject::read_object( gzFile fp,
throw sg_exception("Error reading object properties");
}
size_t indexCount = std::bitset<32>((int)idx_mask).count();
size_t indexCount = std::bitset<32>(idx_mask).count();
if (indexCount == 0) {
throw sg_exception("object index mask has no bits set");
}
@@ -459,20 +345,17 @@ void SGBinObject::read_object( gzFile fp,
int_list vs;
int_list ns;
int_list cs;
tci_list tcs;
vai_list vas;
int_list tcs;
if (version >= 10) {
read_indices<uint32_t>(ptr, nbytes, idx_mask, vertex_attrib_mask, vs, ns, cs, tcs, vas );
read_indices<uint32_t>(ptr, nbytes, idx_mask, vs, ns, cs, tcs);
} else {
read_indices<uint16_t>(ptr, nbytes, idx_mask, vertex_attrib_mask, vs, ns, cs, tcs, vas );
read_indices<uint16_t>(ptr, nbytes, idx_mask, vs, ns, cs, tcs);
}
vertices.push_back( vs );
normals.push_back( ns );
colors.push_back( cs );
texCoords.push_back( tcs );
vertexAttribs.push_back( vas );
materials.push_back( material );
} // of element iteration
}
@@ -497,29 +380,25 @@ bool SGBinObject::read_bin( const string& file ) {
pts_v.clear();
pts_n.clear();
pts_c.clear();
pts_tcs.clear();
pts_vas.clear();
pts_tc.clear();
pt_materials.clear();
tris_v.clear();
tris_n.clear();
tris_c.clear();
tris_tcs.clear();
tris_vas.clear();
tris_tc.clear();
tri_materials.clear();
strips_v.clear();
strips_n.clear();
strips_c.clear();
strips_tcs.clear();
strips_vas.clear();
strips_tc.clear();
strip_materials.clear();
fans_v.clear();
fans_n.clear();
fans_c.clear();
fans_tcs.clear();
fans_vas.clear();
fans_tc.clear();
fan_materials.clear();
gzFile fp;
@@ -540,9 +419,10 @@ bool SGBinObject::read_bin( const string& file ) {
sgReadUInt( fp, &header );
if ( ((header & 0xFF000000) >> 24) == 'S' &&
((header & 0x00FF0000) >> 16) == 'G' ) {
// cout << "Good header" << endl;
// read file version
version = (header & 0x0000FFFF);
// cout << "File version = " << version << endl;
} else {
// close the file before we return
gzclose(fp);
@@ -580,7 +460,7 @@ bool SGBinObject::read_bin( const string& file ) {
nobjects = v;
}
SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin Total objects to read = " << nobjects);
//cout << "Total objects to read = " << nobjects << endl;
if ( sgReadError() ) {
throw sg_io_exception("Error reading BTG file header", sg_location(file));
@@ -609,10 +489,9 @@ bool SGBinObject::read_bin( const string& file ) {
nelements = v;
}
SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin object " << i <<
" = " << (int)obj_type << " props = " << nproperties <<
" elements = " << nelements);
//cout << "object " << i << " = " << (int)obj_type << " props = "
// << nproperties << " elements = " << nelements << endl;
if ( obj_type == SG_BOUNDING_SPHERE ) {
// read bounding sphere properties
read_properties( fp, nproperties );
@@ -642,7 +521,7 @@ bool SGBinObject::read_bin( const string& file ) {
wgs84_nodes.reserve( count );
for ( k = 0; k < count; ++k ) {
SGVec3f v = buf.readVec3f();
// extend from float to double, hmmm
// extend from float to double, hmmm
wgs84_nodes.push_back( SGVec3d(v[0], v[1], v[2]) );
}
}
@@ -702,60 +581,23 @@ bool SGBinObject::read_bin( const string& file ) {
texcoords.push_back( buf.readVec2f() );
}
}
} else if ( obj_type == SG_VA_FLOAT_LIST ) {
// read vertex attribute (float) properties
read_properties( fp, nproperties );
// read vertex attribute list elements
for ( j = 0; j < nelements; ++j ) {
sgReadUInt( fp, &nbytes );
buf.resize( nbytes );
buf.reset();
char *ptr = buf.get_ptr();
sgReadBytes( fp, nbytes, ptr );
int count = nbytes / (sizeof(float));
va_flt.reserve(count);
for ( k = 0; k < count; ++k ) {
va_flt.push_back( buf.readFloat() );
}
}
} else if ( obj_type == SG_VA_INTEGER_LIST ) {
// read vertex attribute (integer) properties
read_properties( fp, nproperties );
// read vertex attribute list elements
for ( j = 0; j < nelements; ++j ) {
sgReadUInt( fp, &nbytes );
buf.resize( nbytes );
buf.reset();
char *ptr = buf.get_ptr();
sgReadBytes( fp, nbytes, ptr );
int count = nbytes / (sizeof(unsigned int));
va_int.reserve(count);
for ( k = 0; k < count; ++k ) {
va_int.push_back( buf.readInt() );
}
}
} else if ( obj_type == SG_POINTS ) {
// read point elements
read_object( fp, SG_POINTS, nproperties, nelements,
pts_v, pts_n, pts_c, pts_tcs,
pts_vas, pt_materials );
pts_v, pts_n, pts_c, pts_tc, pt_materials );
} else if ( obj_type == SG_TRIANGLE_FACES ) {
// read triangle face properties
read_object( fp, SG_TRIANGLE_FACES, nproperties, nelements,
tris_v, tris_n, tris_c, tris_tcs,
tris_vas, tri_materials );
tris_v, tris_n, tris_c, tris_tc, tri_materials );
} else if ( obj_type == SG_TRIANGLE_STRIPS ) {
// read triangle strip properties
read_object( fp, SG_TRIANGLE_STRIPS, nproperties, nelements,
strips_v, strips_n, strips_c, strips_tcs,
strips_vas, strip_materials );
strips_v, strips_n, strips_c, strips_tc,
strip_materials );
} else if ( obj_type == SG_TRIANGLE_FANS ) {
// read triangle fan properties
read_object( fp, SG_TRIANGLE_FANS, nproperties, nelements,
fans_v, fans_n, fans_c, fans_tcs,
fans_vas, fan_materials );
fans_v, fans_n, fans_c, fans_tc, fan_materials );
} else {
// unknown object type, just skip
read_properties( fp, nproperties );
@@ -810,13 +652,9 @@ unsigned int SGBinObject::count_objects(const string_list& materials)
return result;
}
void SGBinObject::write_objects(gzFile fp, int type,
const group_list& verts,
const group_list& normals,
const group_list& colors,
const group_tci_list& texCoords,
const group_vai_list& vertexAttribs,
const string_list& materials)
void SGBinObject::write_objects(gzFile fp, int type, const group_list& verts,
const group_list& normals, const group_list& colors,
const group_list& texCoords, const string_list& materials)
{
if (verts.empty()) {
return;
@@ -828,77 +666,45 @@ void SGBinObject::write_objects(gzFile fp, int type,
while (start < materials.size()) {
m = materials[start];
// find range of objects with identical material, write out as a single object
// find range of objects with identical material, write out as a single object
for (end = start+1; (end < materials.size()) && (m == materials[end]); ++end) {}
// calc the number of elements
const int count = end - start;
// calc the number of properties
unsigned int va_mask = 0;
unsigned int va_count = vertexAttribs.size();
for ( unsigned int va=0; va<va_count; va++ ) {
if ( !vertexAttribs[va].empty() && !vertexAttribs[va].front().empty() ) {
va_mask |= ( 1 << va );
}
}
if ( va_mask ) {
write_header(fp, type, 3, count);
} else {
write_header(fp, type, 2, count);
}
write_header(fp, type, 2, count);
// properties
// material property
sgWriteChar( fp, (char)SG_MATERIAL ); // property
sgWriteUInt( fp, m.length() ); // nbytes
sgWriteBytes( fp, m.length(), m.c_str() );
// index mask property
unsigned char idx_mask = 0;
if ( !verts.empty() && !verts[start].empty()) idx_mask |= SG_IDX_VERTICES;
if ( !normals.empty() && !normals[start].empty()) idx_mask |= SG_IDX_NORMALS;
if ( !colors.empty() && !colors[start].empty()) idx_mask |= SG_IDX_COLORS;
if ( !texCoords.empty() && !texCoords[start][0].empty()) idx_mask |= SG_IDX_TEXCOORDS_0;
if ( !texCoords.empty() && !texCoords[start][1].empty()) idx_mask |= SG_IDX_TEXCOORDS_1;
if ( !texCoords.empty() && !texCoords[start][2].empty()) idx_mask |= SG_IDX_TEXCOORDS_2;
if ( !texCoords.empty() && !texCoords[start][3].empty()) idx_mask |= SG_IDX_TEXCOORDS_3;
if ( !verts.empty() && !verts.front().empty()) idx_mask |= SG_IDX_VERTICES;
if ( !normals.empty() && !normals.front().empty()) idx_mask |= SG_IDX_NORMALS;
if ( !colors.empty() && !colors.front().empty()) idx_mask |= SG_IDX_COLORS;
if ( !texCoords.empty() && !texCoords.front().empty()) idx_mask |= SG_IDX_TEXCOORDS;
if (idx_mask == 0) {
SG_LOG(SG_IO, SG_ALERT, "SGBinObject::write_objects: object with material:"
<< m << "has no indices set");
}
sgWriteChar( fp, (char)SG_INDEX_TYPES ); // property
sgWriteUInt( fp, 1 ); // nbytes
sgWriteChar( fp, idx_mask );
// vertex attribute property
if (va_mask != 0) {
sgWriteChar( fp, (char)SG_VERT_ATTRIBS ); // property
sgWriteUInt( fp, 4 ); // nbytes
sgWriteChar( fp, va_mask );
}
// cout << "material:" << m << ", count =" << count << endl;
// elements
for (unsigned int i=start; i < end; ++i) {
const int_list& va(verts[i]);
const int_list& na((idx_mask & SG_IDX_NORMALS) ? normals[i] : emptyList);
const int_list& ca((idx_mask & SG_IDX_COLORS) ? colors[i] : emptyList);
// pass the whole texcoord array - we'll figure out which indicies to write
// in write_indices
const tci_list& tca( texCoords[i] );
// pass the whole vertex array - we'll figure out which indicies to write
// in write_indices
const vai_list& vaa( vertexAttribs[i] );
const int_list& tca((idx_mask & SG_IDX_TEXCOORDS) ? texCoords[i] : emptyList);
if (version == 7) {
write_indices<uint16_t>(fp, idx_mask, va_mask, va, na, ca, tca, vaa);
write_indices<uint16_t>(fp, idx_mask, va, na, ca, tca);
} else {
write_indices<uint32_t>(fp, idx_mask, va_mask, va, na, ca, tca, vaa);
write_indices<uint32_t>(fp, idx_mask, va, na, ca, tca);
}
}
@@ -943,6 +749,7 @@ bool SGBinObject::write_bin_file(const SGPath& file)
SGPath file2(file);
file2.create_dir( 0755 );
cout << "Output file = " << file.str() << endl;
gzFile fp;
if ( (fp = gzopen( file.c_str(), "wb9" )) == NULL ) {
@@ -952,19 +759,19 @@ bool SGBinObject::write_bin_file(const SGPath& file)
sgClearWriteError();
SG_LOG(SG_IO, SG_DEBUG, "points size = " << pts_v.size()
<< " pt_materials = " << pt_materials.size() );
SG_LOG(SG_IO, SG_DEBUG, "triangles size = " << tris_v.size()
<< " tri_materials = " << tri_materials.size() );
SG_LOG(SG_IO, SG_DEBUG, "strips size = " << strips_v.size()
<< " strip_materials = " << strip_materials.size() );
SG_LOG(SG_IO, SG_DEBUG, "fans size = " << fans_v.size()
<< " fan_materials = " << fan_materials.size() );
cout << "points size = " << pts_v.size() << " pt_materials = "
<< pt_materials.size() << endl;
cout << "triangles size = " << tris_v.size() << " tri_materials = "
<< tri_materials.size() << endl;
cout << "strips size = " << strips_v.size() << " strip_materials = "
<< strip_materials.size() << endl;
cout << "fans size = " << fans_v.size() << " fan_materials = "
<< fan_materials.size() << endl;
SG_LOG(SG_IO, SG_DEBUG, "nodes = " << wgs84_nodes.size() );
SG_LOG(SG_IO, SG_DEBUG, "colors = " << colors.size() );
SG_LOG(SG_IO, SG_DEBUG, "normals = " << normals.size() );
SG_LOG(SG_IO, SG_DEBUG, "tex coords = " << texcoords.size() );
cout << "nodes = " << wgs84_nodes.size() << endl;
cout << "colors = " << colors.size() << endl;
cout << "normals = " << normals.size() << endl;
cout << "tex coords = " << texcoords.size() << endl;
version = 10;
bool shortMaterialsRanges =
@@ -996,8 +803,7 @@ bool SGBinObject::write_bin_file(const SGPath& file)
nobjects += count_objects(strip_materials);
nobjects += count_objects(fan_materials);
SG_LOG(SG_IO, SG_DEBUG, "total top level objects = " << nobjects);
cout << "total top level objects = " << nobjects << endl;
if (version == 7) {
sgWriteUShort( fp, (uint16_t) nobjects );
} else {
@@ -1043,10 +849,10 @@ bool SGBinObject::write_bin_file(const SGPath& file)
sgWriteVec2( fp, texcoords[i]);
}
write_objects(fp, SG_POINTS, pts_v, pts_n, pts_c, pts_tcs, pts_vas, pt_materials);
write_objects(fp, SG_TRIANGLE_FACES, tris_v, tris_n, tris_c, tris_tcs, tris_vas, tri_materials);
write_objects(fp, SG_TRIANGLE_STRIPS, strips_v, strips_n, strips_c, strips_tcs, strips_vas, strip_materials);
write_objects(fp, SG_TRIANGLE_FANS, fans_v, fans_n, fans_c, fans_tcs, fans_vas, fan_materials);
write_objects(fp, SG_POINTS, pts_v, pts_n, pts_c, pts_tc, pt_materials);
write_objects(fp, SG_TRIANGLE_FACES, tris_v, tris_n, tris_c, tris_tc, tri_materials);
write_objects(fp, SG_TRIANGLE_STRIPS, strips_v, strips_n, strips_c, strips_tc, strip_materials);
write_objects(fp, SG_TRIANGLE_FANS, fans_v, fans_n, fans_c, fans_tc, fan_materials);
// close the file
gzclose(fp);
@@ -1146,7 +952,7 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
}
// cout << "group = " << start << " to " << end - 1 << endl;
SGSphered d( SGVec3d(0.0, 0.0, 0.0), -1.0 );
SGSphered d;
for ( i = start; i < end; ++i ) {
for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
d.expandBy(wgs84_nodes[ tris_v[i][j] ]);
@@ -1166,7 +972,7 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
for ( i = start; i < end; ++i ) {
fprintf(fp, "f");
for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
fprintf(fp, " %d/%d", tris_v[i][j], tris_tcs[i][0][j] );
fprintf(fp, " %d/%d", tris_v[i][j], tris_tc[i][j] );
}
fprintf(fp, "\n");
}
@@ -1195,7 +1001,7 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
// cout << "group = " << start << " to " << end - 1 << endl;
SGSphered d( SGVec3d(0.0, 0.0, 0.0), -1.0 );
SGSphered d;
for ( i = start; i < end; ++i ) {
for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
d.expandBy(wgs84_nodes[ tris_v[i][j] ]);
@@ -1215,7 +1021,7 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
for ( i = start; i < end; ++i ) {
fprintf(fp, "ts");
for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
fprintf(fp, " %d/%d", strips_v[i][j], strips_tcs[i][0][j] );
fprintf(fp, " %d/%d", strips_v[i][j], strips_tc[i][j] );
}
fprintf(fp, "\n");
}
@@ -1255,27 +1061,3 @@ void SGBinObject::read_properties(gzFile fp, int nproperties)
}
}
bool SGBinObject::add_point( const SGBinObjectPoint& pt )
{
// add the point info
pt_materials.push_back( pt.material );
pts_v.push_back( pt.v_list );
pts_n.push_back( pt.n_list );
pts_c.push_back( pt.c_list );
return true;
}
bool SGBinObject::add_triangle( const SGBinObjectTriangle& tri )
{
// add the triangle info and keep lists aligned
tri_materials.push_back( tri.material );
tris_v.push_back( tri.v_list );
tris_n.push_back( tri.n_list );
tris_c.push_back( tri.c_list );
tris_tcs.push_back( tri.tc_list );
tris_vas.push_back( tri.va_list );
return true;
}

View File

@@ -36,79 +36,16 @@
#include <vector>
#include <string>
#include <boost/array.hpp>
#define MAX_TC_SETS (4)
#define MAX_VAS (8)
// I really want to pass around fixed length arrays, as the size
// has to be hardcoded
// but it's a C++0x feature use boost in its absence
typedef boost::array<int_list, MAX_TC_SETS> tci_list;
typedef boost::array<int_list, MAX_VAS> vai_list;
/** STL Structure used to store (integer index) object information */
/** STL Structure used to store object information */
typedef std::vector < int_list > group_list;
typedef group_list::iterator group_list_iterator;
typedef group_list::const_iterator const_group_list_iterator;
/** STL Structure used to store (tc index) object information */
typedef std::vector < tci_list > group_tci_list;
typedef group_tci_list::iterator group_tci_list_iterator;
typedef group_tci_list::const_iterator const_group_tci_list_iterator;
/** STL Structure used to store (va index) object information */
typedef std::vector < vai_list > group_vai_list;
typedef group_vai_list::iterator group_vai_list_iterator;
typedef group_vai_list::const_iterator const_group_vai_list_iterator;
// forward decls
class SGBucket;
class SGPath;
class SGBinObjectPoint {
public:
std::string material;
int_list v_list;
int_list n_list;
int_list c_list;
void clear( void ) {
material = "";
v_list.clear();
n_list.clear();
c_list.clear();
};
};
class SGBinObjectTriangle {
public:
std::string material;
int_list v_list;
int_list n_list;
int_list c_list;
tci_list tc_list;
vai_list va_list;
void clear( void ) {
material = "";
v_list.clear();
n_list.clear();
c_list.clear();
for ( unsigned int i=0; i<MAX_TC_SETS; i++ ) {
tc_list[i].clear();
}
for ( unsigned int i=0; i<MAX_VAS; i++ ) {
va_list[i].clear();
}
};
};
/**
* A class to manipulate the simgear 3d object format.
* This class provides functionality to both read and write the binary format.
@@ -149,40 +86,34 @@ private:
SGVec3d gbs_center;
float gbs_radius;
std::vector<SGVec3d> wgs84_nodes; // vertex list
std::vector<SGVec4f> colors; // color list
std::vector<SGVec3f> normals; // normal list
std::vector<SGVec2f> texcoords; // texture coordinate list
std::vector<float> va_flt; // vertex attribute list (floats)
std::vector<int> va_int; // vertex attribute list (ints)
group_list pts_v; // points vertex index
group_list pts_n; // points normal index
group_list pts_c; // points color index
group_tci_list pts_tcs; // points texture coordinates ( up to 4 sets )
group_vai_list pts_vas; // points vertex attributes ( up to 8 sets )
string_list pt_materials; // points materials
std::vector<SGVec3d> wgs84_nodes; // vertex list
std::vector<SGVec4f> colors; // color list
std::vector<SGVec3f> normals; // normal list
std::vector<SGVec2f> texcoords; // texture coordinate list
group_list tris_v; // triangles vertex index
group_list tris_n; // triangles normal index
group_list tris_c; // triangles color index
group_tci_list tris_tcs; // triangles texture coordinates ( up to 4 sets )
group_vai_list tris_vas; // triangles vertex attributes ( up to 8 sets )
string_list tri_materials; // triangles materials
group_list pts_v; // points vertex index
group_list pts_n; // points normal index
group_list pts_c; // points color index
group_list pts_tc; // points texture coordinate index
string_list pt_materials; // points materials
group_list strips_v; // tristrips vertex index
group_list strips_n; // tristrips normal index
group_list strips_c; // tristrips color index
group_tci_list strips_tcs; // tristrips texture coordinates ( up to 4 sets )
group_vai_list strips_vas; // tristrips vertex attributes ( up to 8 sets )
string_list strip_materials; // tristrips materials
group_list tris_v; // triangles vertex index
group_list tris_n; // triangles normal index
group_list tris_c; // triangles color index
group_list tris_tc; // triangles texture coordinate index
string_list tri_materials; // triangles materials
group_list fans_v; // fans vertex index
group_list fans_n; // fans normal index
group_list fans_c; // fans color index
group_tci_list fans_tcs; // fanss texture coordinates ( up to 4 sets )
group_vai_list fans_vas; // fans vertex attributes ( up to 8 sets )
string_list fan_materials; // fans materials
group_list strips_v; // tristrips vertex index
group_list strips_n; // tristrips normal index
group_list strips_c; // tristrips color index
group_list strips_tc; // tristrips texture coordinate index
string_list strip_materials;// tristrips materials
group_list fans_v; // fans vertex index
group_list fans_n; // fans normal index
group_list fans_c; // fans color index
group_list fans_tc; // fans texture coordinate index
string_list fan_materials; // fans materials
void read_properties(gzFile fp, int nproperties);
@@ -193,23 +124,17 @@ private:
group_list& vertices,
group_list& normals,
group_list& colors,
group_tci_list& texCoords,
group_vai_list& vertexAttribs,
group_list& texCoords,
string_list& materials);
void write_header(gzFile fp, int type, int nProps, int nElements);
void write_objects(gzFile fp,
int type,
const group_list& verts,
const group_list& normals,
const group_list& colors,
const group_tci_list& texCoords,
const group_vai_list& vertexAttribs,
const string_list& materials);
void write_objects(gzFile fp, int type, const group_list& verts,
const group_list& normals, const group_list& colors,
const group_list& texCoords, const string_list& materials);
unsigned int count_objects(const string_list& materials);
public:
public:
inline unsigned short get_version() const { return version; }
inline const SGVec3d& get_gbs_center() const { return gbs_center; }
@@ -218,8 +143,10 @@ public:
inline float get_gbs_radius() const { return gbs_radius; }
inline void set_gbs_radius( float r ) { gbs_radius = r; }
inline const std::vector<SGVec3d>& get_wgs84_nodes() const { return wgs84_nodes; }
inline void set_wgs84_nodes( const std::vector<SGVec3d>& n ) { wgs84_nodes = n; }
inline const std::vector<SGVec3d>& get_wgs84_nodes() const
{ return wgs84_nodes; }
inline void set_wgs84_nodes( const std::vector<SGVec3d>& n )
{ wgs84_nodes = n; }
inline const std::vector<SGVec4f>& get_colors() const { return colors; }
inline void set_colors( const std::vector<SGVec4f>& c ) { colors = c; }
@@ -230,38 +157,51 @@ public:
inline const std::vector<SGVec2f>& get_texcoords() const { return texcoords; }
inline void set_texcoords( const std::vector<SGVec2f>& t ) { texcoords = t; }
// Points API
bool add_point( const SGBinObjectPoint& pt );
inline const group_list& get_pts_v() const { return pts_v; }
inline const group_list& get_pts_n() const { return pts_n; }
inline const group_tci_list& get_pts_tcs() const { return pts_tcs; }
inline const group_vai_list& get_pts_vas() const { return pts_vas; }
inline void set_pts_v( const group_list& g ) { pts_v = g; }
inline const group_list& get_pts_n() const { return pts_n; }
inline void set_pts_n( const group_list& g ) { pts_n = g; }
inline const group_list& get_pts_c() const { return pts_c; }
inline void set_pts_c( const group_list& g ) { pts_c = g; }
inline const group_list& get_pts_tc() const { return pts_tc; }
inline void set_pts_tc( const group_list& g ) { pts_tc = g; }
inline const string_list& get_pt_materials() const { return pt_materials; }
inline void set_pt_materials( const string_list& s ) { pt_materials = s; }
// Triangles API
bool add_triangle( const SGBinObjectTriangle& tri );
inline const group_list& get_tris_v() const { return tris_v; }
inline void set_tris_v( const group_list& g ) { tris_v = g; }
inline const group_list& get_tris_n() const { return tris_n; }
inline void set_tris_n( const group_list& g ) { tris_n = g; }
inline const group_list& get_tris_c() const { return tris_c; }
inline const group_tci_list& get_tris_tcs() const { return tris_tcs; }
inline const group_vai_list& get_tris_vas() const { return tris_vas; }
inline void set_tris_c( const group_list& g ) { tris_c = g; }
inline const group_list& get_tris_tc() const { return tris_tc; }
inline void set_tris_tc( const group_list& g ) { tris_tc = g; }
inline const string_list& get_tri_materials() const { return tri_materials; }
inline void set_tri_materials( const string_list& s ) { tri_materials = s; }
// Strips API (deprecated - read only)
inline const group_list& get_strips_v() const { return strips_v; }
inline void set_strips_v( const group_list& g ) { strips_v = g; }
inline const group_list& get_strips_n() const { return strips_n; }
inline void set_strips_n( const group_list& g ) { strips_n = g; }
inline const group_list& get_strips_c() const { return strips_c; }
inline const group_tci_list& get_strips_tcs() const { return strips_tcs; }
inline const group_vai_list& get_strips_vas() const { return strips_vas; }
inline const string_list& get_strip_materials() const { return strip_materials; }
inline void set_strips_c( const group_list& g ) { strips_c = g; }
// Fans API (deprecated - read only )
inline const group_list& get_strips_tc() const { return strips_tc; }
inline void set_strips_tc( const group_list& g ) { strips_tc = g; }
inline const string_list& get_strip_materials() const { return strip_materials; }
inline void set_strip_materials( const string_list& s ) { strip_materials = s; }
inline const group_list& get_fans_v() const { return fans_v; }
inline void set_fans_v( const group_list& g ) { fans_v = g; }
inline const group_list& get_fans_n() const { return fans_n; }
inline void set_fans_n( const group_list& g ) { fans_n = g; }
inline const group_list& get_fans_c() const { return fans_c; }
inline const group_tci_list& get_fans_tcs() const { return fans_tcs; }
inline const group_vai_list& get_fans_vas() const { return fans_vas; }
inline void set_fans_c( const group_list& g ) { fans_c = g; }
inline const group_list& get_fans_tc() const { return fans_tc; }
inline void set_fans_tc( const group_list& g ) { fans_tc = g; }
inline const string_list& get_fan_materials() const { return fan_materials; }
inline void set_fan_materials( const string_list& s ) { fan_materials = s; }
/**
* Read a binary file object and populate the provided structures.

View File

@@ -103,15 +103,6 @@ int_list make_tri(int maxIndex)
return r;
}
tci_list make_tri_tcs(int maxIndex)
{
tci_list tci;
tci[0].push_back(random() % maxIndex);
tci[0].push_back(random() % maxIndex);
tci[0].push_back(random() % maxIndex);
return tci;
}
void compareTris(const SGBinObject& a, const SGBinObject& b)
{
unsigned int count = a.get_tri_materials().size();
@@ -122,31 +113,32 @@ void compareTris(const SGBinObject& a, const SGBinObject& b)
COMPARE(a.get_tri_materials()[i], b.get_tri_materials()[i]);
const int_list& tA(a.get_tris_tcs()[i][0]);
const int_list& tB(b.get_tris_tcs()[i][0]);
const int_list& tA(a.get_tris_tc()[i]);
const int_list& tB(b.get_tris_tc()[i]);
VERIFY(tA == tB);
}
}
void generate_tris(SGBinObject& b, int count)
{
group_list v, n;
group_tci_list tc;
group_list v, n, tc;
string_list materials;
int maxVertices = b.get_wgs84_nodes().size();
int maxNormals = b.get_normals().size();
int maxTCs = b.get_texcoords().size();
SGBinObjectTriangle sgboTri;
for (int t=0; t<count; ++t) {
sgboTri.material = "material1";
sgboTri.v_list = make_tri(maxVertices);
sgboTri.n_list = make_tri(maxNormals);
sgboTri.tc_list[0] = make_tri(maxTCs);
b.add_triangle( sgboTri );
v.push_back(make_tri(maxVertices));
n.push_back(make_tri(maxNormals));
tc.push_back(make_tri(maxTCs));
materials.push_back("material1");
}
b.set_tris_v(v);
b.set_tris_n(n);
b.set_tris_tc(tc);
b.set_tri_materials(materials);
}
void test_basic()

View File

@@ -19,13 +19,10 @@
# include <simgear_config.h>
#endif
#include <simgear/misc/test_macros.hxx>
#include <cstdlib>
#include <iostream>
#include "SGMath.hxx"
#include "SGRect.hxx"
#include "sg_random.h"
template<typename T>
@@ -271,33 +268,6 @@ MatrixTest(void)
return true;
}
template<typename T>
void doRectTest()
{
SGRect<T> rect(10, 15, 20, 25);
COMPARE(rect.x(), 10)
COMPARE(rect.y(), 15)
COMPARE(rect.width(), 20)
COMPARE(rect.height(), 25)
COMPARE(rect.pos(), SGVec2<T>(10, 15))
COMPARE(rect.size(), SGVec2<T>(20, 25))
COMPARE(rect.l(), 10)
COMPARE(rect.t(), 15)
COMPARE(rect.r(), 30)
COMPARE(rect.b(), 40)
VERIFY(rect == rect)
VERIFY(rect == SGRect<T>(10, 15, 20, 25))
VERIFY(rect != SGRect<T>(11, 15, 20, 25))
VERIFY(rect.contains(10, 15))
VERIFY(!rect.contains(9, 15))
VERIFY(rect.contains(9, 15, 1))
}
bool
GeodesyTest(void)
{
@@ -381,10 +351,6 @@ main(void)
if (!MatrixTest<double>())
return EXIT_FAILURE;
// Do rect tests
doRectTest<int>();
doRectTest<double>();
// Check geodetic/geocentric/cartesian conversions
if (!GeodesyTest())
return EXIT_FAILURE;

View File

@@ -34,7 +34,7 @@ class SGRect
}
explicit SGRect(const SGVec2<T>& pt):
SGRect(const SGVec2<T>& pt):
_min(pt),
_max(pt)
{
@@ -88,15 +88,11 @@ class SGRect
T y() const { return _min.y(); }
T width() const { return _max.x() - _min.x(); }
T height() const { return _max.y() - _min.y(); }
SGVec2<T> const& pos() const { return _min; }
SGVec2<T> size() const { return SGVec2<T>(width(), height()); }
void setX(T x) { T w = width(); _min.x() = x; _max.x() = x + w; }
void setY(T y) { T h = height(); _min.y() = y; _max.y() = y + h; }
void setWidth(T w) { _max.x() = _min.x() + w; }
void setHeight(T h) { _max.y() = _min.y() + h; }
void setPos(const SGVec2<T>& p) { setX(p.x()); setY(p.y()); }
void setSize(const SGVec2<T>& s) { setWidth(s.x()); setHeight(s.y()); }
T l() const { return _min.x(); }
T r() const { return _max.x(); }
@@ -113,18 +109,6 @@ class SGRect
void setTop(T t) { _min.y() = t; }
void setBottom(T b) { _max.y() = b; }
/**
* Expand rectangle to include the given position
*/
void expandBy(T x, T y)
{
if( x < _min.x() ) _min.x() = x;
if( x > _max.x() ) _max.x() = x;
if( y < _min.y() ) _min.y() = y;
if( y > _max.y() ) _max.y() = y;
}
/**
* Move rect by vector
*/
@@ -145,17 +129,6 @@ class SGRect
return *this;
}
bool operator==(const SGRect<T>& rhs) const
{
return _min == rhs._min
&& _max == rhs._max;
}
bool operator!=(const SGRect<T>& rhs) const
{
return !(*this == rhs);
}
bool contains(T x, T y) const
{
return _min.x() <= x && x <= _max.x()
@@ -203,8 +176,4 @@ std::basic_ostream<char_type, traits_type>&
operator<<(std::basic_ostream<char_type, traits_type>& s, const SGRect<T>& rect)
{ return s << "min = " << rect.getMin() << ", max = " << rect.getMax(); }
typedef SGRect<int> SGRecti;
typedef SGRect<float> SGRectf;
typedef SGRect<double> SGRectd;
#endif /* SG_RECT_HXX_ */

View File

@@ -16,7 +16,7 @@
//
#ifndef SGTriangle_H
#define SGTriangle_H
#define SGTrianlge_H
template<typename T>
class SGTriangle {

View File

@@ -5,8 +5,6 @@ set(HEADERS
CSSBorder.hxx
ListDiff.hxx
ResourceManager.hxx
SimpleMarkdown.hxx
SVGpreserveAspectRatio.hxx
interpolator.hxx
make_new.hxx
sg_dir.hxx
@@ -24,8 +22,6 @@ set(HEADERS
set(SOURCES
CSSBorder.cxx
ResourceManager.cxx
SimpleMarkdown.cxx
SVGpreserveAspectRatio.cxx
interpolator.cxx
sg_dir.cxx
sg_path.cxx
@@ -37,10 +33,6 @@ set(SOURCES
gzcontainerfile.cxx
)
if (APPLE)
list(APPEND SOURCES CocoaHelpers.mm)
endif()
simgear_component(misc misc "${SOURCES}" "${HEADERS}")
if(ENABLE_TESTS)
@@ -53,6 +45,10 @@ add_executable(test_tabbed_values tabbed_values_test.cxx)
add_test(tabbed_values ${EXECUTABLE_OUTPUT_PATH}/test_tabbed_values)
target_link_libraries(test_tabbed_values ${TEST_LIBS})
add_executable(test_strings strutils_test.cxx )
add_test(strings ${EXECUTABLE_OUTPUT_PATH}/test_strings)
target_link_libraries(test_strings ${TEST_LIBS})
add_executable(test_streams sgstream_test.cxx )
add_test(streams ${EXECUTABLE_OUTPUT_PATH}/test_streams)
target_link_libraries(test_streams ${TEST_LIBS})
@@ -62,23 +58,3 @@ add_test(path ${EXECUTABLE_OUTPUT_PATH}/test_path)
target_link_libraries(test_path ${TEST_LIBS})
endif(ENABLE_TESTS)
add_boost_test(SimpleMarkdown
SOURCES SimpleMarkdown_test.cxx
LIBRARIES ${TEST_LIBS}
)
add_boost_test(SVGpreserveAspectRatio
SOURCES SVGpreserveAspectRatio_test.cxx
LIBRARIES ${TEST_LIBS}
)
add_boost_test(strutils
SOURCES strutils_test.cxx
LIBRARIES ${TEST_LIBS}
)
add_boost_test(utf8tolatin1
SOURCES utf8tolatin1_test.cxx
LIBRARIES ${TEST_LIBS}
)

View File

@@ -1,41 +0,0 @@
#include <Cocoa/Cocoa.h>
#include <Foundation/NSAutoreleasePool.h>
#include <simgear/misc/sg_path.hxx>
namespace {
class CocoaAutoreleasePool
{
public:
CocoaAutoreleasePool()
{
pool = [[NSAutoreleasePool alloc] init];
}
~CocoaAutoreleasePool()
{
[pool release];
}
private:
NSAutoreleasePool* pool;
};
} // of anonyous namespace
SGPath appleSpecialFolder(int dirType, int domainMask, const SGPath& def)
{
CocoaAutoreleasePool ap;
NSFileManager* fm = [NSFileManager defaultManager];
NSURL* pathUrl = [fm URLForDirectory:dirType
inDomain:domainMask
appropriateForURL:Nil
create:YES
error:nil];
if (!pathUrl) {
return def;;
}
return SGPath([[pathUrl path] UTF8String], def.getPermissionChecker());
}

View File

@@ -1,192 +0,0 @@
// Parse and represent SVG preserveAspectRatio attribute
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "SVGpreserveAspectRatio.hxx"
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/strutils.hxx>
#include <boost/tokenizer.hpp>
namespace simgear
{
//----------------------------------------------------------------------------
SVGpreserveAspectRatio::SVGpreserveAspectRatio():
_align_x(ALIGN_NONE),
_align_y(ALIGN_NONE),
_meet(true)
{
}
//----------------------------------------------------------------------------
SVGpreserveAspectRatio::Align SVGpreserveAspectRatio::alignX() const
{
return _align_x;
}
//----------------------------------------------------------------------------
SVGpreserveAspectRatio::Align SVGpreserveAspectRatio::alignY() const
{
return _align_y;
}
//----------------------------------------------------------------------------
bool SVGpreserveAspectRatio::scaleToFill() const
{
return (_align_x == ALIGN_NONE) && (_align_y == ALIGN_NONE);
}
//----------------------------------------------------------------------------
bool SVGpreserveAspectRatio::scaleToFit() const
{
return !scaleToFill() && _meet;
}
//----------------------------------------------------------------------------
bool SVGpreserveAspectRatio::scaleToCrop() const
{
return !scaleToFill() && !_meet;
}
//----------------------------------------------------------------------------
bool SVGpreserveAspectRatio::meet() const
{
return _meet;
}
//----------------------------------------------------------------------------
bool
SVGpreserveAspectRatio::operator==(const SVGpreserveAspectRatio& rhs) const
{
return (_align_x == rhs._align_x)
&& (_align_y == rhs._align_y)
&& (_meet == rhs._meet || scaleToFill());
}
//----------------------------------------------------------------------------
SVGpreserveAspectRatio SVGpreserveAspectRatio::parse(const std::string& str)
{
SVGpreserveAspectRatio ret;
enum
{
PARSE_defer,
PARSE_align,
PARSE_meetOrSlice,
PARSE_done,
PARSE_error
} parse_state = PARSE_defer;
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
const boost::char_separator<char> del(" \t\n");
tokenizer tokens(str.begin(), str.end(), del);
for( tokenizer::const_iterator tok = tokens.begin();
tok != tokens.end()
&& parse_state != PARSE_error;
++tok )
{
const std::string& cur_tok = tok.current_token();
switch( parse_state )
{
case PARSE_defer:
if( cur_tok == "defer" )
{
SG_LOG( SG_GENERAL,
SG_INFO,
"SVGpreserveAspectRatio: 'defer' is ignored." );
parse_state = PARSE_align;
break;
}
// fall through
case PARSE_align:
if( cur_tok == "none" )
{
ret._align_x = ALIGN_NONE;
ret._align_y = ALIGN_NONE;
}
else if( cur_tok.length() == 8 )
{
if( strutils::starts_with(cur_tok, "xMin") )
ret._align_x = ALIGN_MIN;
else if( strutils::starts_with(cur_tok, "xMid") )
ret._align_x = ALIGN_MID;
else if( strutils::starts_with(cur_tok, "xMax") )
ret._align_x = ALIGN_MAX;
else
{
parse_state = PARSE_error;
break;
}
if( strutils::ends_with(cur_tok, "YMin") )
ret._align_y = ALIGN_MIN;
else if( strutils::ends_with(cur_tok, "YMid") )
ret._align_y = ALIGN_MID;
else if( strutils::ends_with(cur_tok, "YMax") )
ret._align_y = ALIGN_MAX;
else
{
parse_state = PARSE_error;
break;
}
}
else
{
parse_state = PARSE_error;
break;
}
parse_state = PARSE_meetOrSlice;
break;
case PARSE_meetOrSlice:
if( cur_tok == "meet" )
ret._meet = true;
else if( cur_tok == "slice" )
ret._meet = false;
else
{
parse_state = PARSE_error;
break;
}
parse_state = PARSE_done;
break;
case PARSE_done:
SG_LOG( SG_GENERAL,
SG_WARN,
"SVGpreserveAspectRatio: Ignoring superfluous token"
" '" << cur_tok << "'" );
break;
default:
break;
}
}
if( parse_state == PARSE_error )
{
SG_LOG( SG_GENERAL,
SG_WARN,
"SVGpreserveAspectRatio: Failed to parse: '" << str << "'" );
return SVGpreserveAspectRatio();
}
return ret;
}
} // namespace simgear

View File

@@ -1,67 +0,0 @@
///@file Parse and represent SVG preserveAspectRatio attribute
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_SVG_PRESERVE_ASPECT_RATIO_HXX_
#define SG_SVG_PRESERVE_ASPECT_RATIO_HXX_
#include <string>
namespace simgear
{
/**
* SVG preserveAspectRatio attribute
*
* @see http://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
*/
class SVGpreserveAspectRatio
{
public:
enum Align
{
ALIGN_NONE,
ALIGN_MIN,
ALIGN_MID,
ALIGN_MAX
};
SVGpreserveAspectRatio();
Align alignX() const;
Align alignY() const;
bool scaleToFill() const;
bool scaleToFit() const;
bool scaleToCrop() const;
bool meet() const;
bool operator==(const SVGpreserveAspectRatio& rhs) const;
static SVGpreserveAspectRatio parse(const std::string& str);
private:
Align _align_x,
_align_y;
bool _meet; //!< uniform scale to fit, if true
// uniform scale to fill+crop, if false
};
} // namespace simgear
#endif /* SG_SVG_PRESERVE_ASPECT_RATIO_HXX_ */

View File

@@ -1,60 +0,0 @@
/// Unit tests for SVGpreserveAspectRatio
#define BOOST_TEST_MODULE misc
#include <BoostTestTargetConfig.h>
#include "SVGpreserveAspectRatio.hxx"
namespace simgear
{
std::ostream& operator<<( std::ostream& strm,
const SVGpreserveAspectRatio& ar )
{
strm << "[ align_x=" << ar.alignX() <<
", align_y=" << ar.alignY() <<
", meet=" << ar.meet() <<
"]";
return strm;
}
}
BOOST_AUTO_TEST_CASE( parse_attribute )
{
using simgear::SVGpreserveAspectRatio;
SVGpreserveAspectRatio ar = SVGpreserveAspectRatio::parse("none");
BOOST_CHECK( ar.scaleToFill() );
BOOST_CHECK( !ar.scaleToFit() );
BOOST_CHECK( !ar.scaleToCrop() );
BOOST_CHECK_EQUAL( ar.alignX(), SVGpreserveAspectRatio::ALIGN_NONE );
BOOST_CHECK_EQUAL( ar.alignY(), SVGpreserveAspectRatio::ALIGN_NONE );
SVGpreserveAspectRatio ar_meet = SVGpreserveAspectRatio::parse("none meet");
SVGpreserveAspectRatio ar_slice = SVGpreserveAspectRatio::parse("none slice");
BOOST_CHECK_EQUAL( ar, ar_meet );
BOOST_CHECK_EQUAL( ar, ar_slice );
ar_meet = SVGpreserveAspectRatio::parse("xMidYMid meet");
BOOST_CHECK( !ar_meet.scaleToFill() );
BOOST_CHECK( ar_meet.scaleToFit() );
BOOST_CHECK( !ar_meet.scaleToCrop() );
BOOST_CHECK_EQUAL( ar_meet.alignX(), SVGpreserveAspectRatio::ALIGN_MID );
BOOST_CHECK_EQUAL( ar_meet.alignY(), SVGpreserveAspectRatio::ALIGN_MID );
ar_slice = SVGpreserveAspectRatio::parse("xMidYMid slice");
BOOST_CHECK( !ar_slice.scaleToFill() );
BOOST_CHECK( !ar_slice.scaleToFit() );
BOOST_CHECK( ar_slice.scaleToCrop() );
BOOST_CHECK_EQUAL( ar_slice.alignX(), SVGpreserveAspectRatio::ALIGN_MID );
BOOST_CHECK_EQUAL( ar_slice.alignY(), SVGpreserveAspectRatio::ALIGN_MID );
BOOST_CHECK_NE(ar_meet, ar_slice);
// defer is ignored, meet is default
ar_meet = SVGpreserveAspectRatio::parse("defer xMinYMin");
BOOST_CHECK( !ar_meet.scaleToFill() );
BOOST_CHECK( ar_meet.scaleToFit() );
BOOST_CHECK( !ar_meet.scaleToCrop() );
BOOST_CHECK_EQUAL( ar_meet.alignX(), SVGpreserveAspectRatio::ALIGN_MIN );
BOOST_CHECK_EQUAL( ar_meet.alignY(), SVGpreserveAspectRatio::ALIGN_MIN );
}

View File

@@ -1,104 +0,0 @@
// Really simple markdown parser
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "SimpleMarkdown.hxx"
namespace simgear
{
// White space
static bool isSpace(const char c)
{
return c == ' ' || c == '\t';
}
// Unordered list item
static bool isULItem(const char c)
{
return c == '*' || c == '+' || c == '-';
}
//----------------------------------------------------------------------------
std::string SimpleMarkdown::parse(const std::string& src)
{
std::string out;
int num_space = 0,
num_newline = 0;
bool line_empty = true;
for( std::string::const_iterator it = src.begin();
it != src.end();
++it )
{
if( isSpace(*it) )
{
++num_space;
}
else if( *it == '\n' )
{
// Two or more whitespace at end of line -> line break
if( !line_empty && num_space >= 2 )
{
out.push_back('\n');
line_empty = true;
}
++num_newline;
num_space = 0;
}
else
{
// Remove all new lines before first printable character
if( out.empty() )
num_newline = 0;
// Two or more new lines (aka. at least one empty line) -> new paragraph
if( num_newline >= 2 )
{
out.append("\n\n");
line_empty = true;
num_newline = 0;
}
// Replace unordered list item markup at begin of line with bullet
// (TODO multilevel lists, indent multiple lines, etc.)
if( (line_empty || num_newline) && isULItem(*it) )
{
if( num_newline < 2 )
out.push_back('\n');
out.append("\xE2\x80\xA2");
}
else
{
// Collapse multiple whitespace
if( !line_empty && (num_space || num_newline) )
out.push_back(' ');
out.push_back(*it);
}
line_empty = false;
num_space = 0;
num_newline = 0;
}
}
return out;
}
} // namespace simgear

View File

@@ -1,39 +0,0 @@
///@file Really simple markdown parser
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SIMPLE_MARKDOWN_HXX_
#define SIMPLE_MARKDOWN_HXX_
#include <string>
namespace simgear
{
/**
* Really simple mardown parser. Currently just paragraphs, new lines and
* one level of unordered lists are supported.
*
* @see http://en.wikipedia.org/wiki/Markdown
*/
class SimpleMarkdown
{
public:
static std::string parse(const std::string& src);
};
} // namespace simgear
#endif /* SIMPLE_MARKDOWN_HXX_ */

View File

@@ -1,34 +0,0 @@
/// Unit tests for simple markdown parser
#define BOOST_TEST_MODULE misc
#include <BoostTestTargetConfig.h>
#include "SimpleMarkdown.hxx"
BOOST_AUTO_TEST_CASE( basic_markdown )
{
std::string const src(
" \n"
"\n"
"blub\n"
"* \tlist item\n"
"* another\t item\n"
"\n"
"test blubt\n"
" aha \n"
"asd\n"
" * 2nd list, another item\n"
" * 2nd list, one more..."
);
std::string const out(
"blub\n"
"\xE2\x80\xA2 list item\n"
"\xE2\x80\xA2 another item\n"
"\n"
"test blubt aha\n"
"asd\n"
"\xE2\x80\xA2 2nd list, another item\n"
"\xE2\x80\xA2 2nd list, one more..."
);
BOOST_CHECK_EQUAL(simgear::SimpleMarkdown::parse(src), out);
}

View File

@@ -41,20 +41,6 @@ void test_dir()
}
cout << temp.path().modTime() << endl;
std::cout << "Standard Locations:"
<< "\n - Home: " << SGPath::standardLocation(SGPath::HOME)
<< "\n - Desktop: " << SGPath::standardLocation(SGPath::DESKTOP)
<< "\n - Downloads: " << SGPath::standardLocation(SGPath::DOWNLOADS)
<< "\n - Documents: " << SGPath::standardLocation(SGPath::DOCUMENTS)
<< "\n - Pictures: " << SGPath::standardLocation(SGPath::PICTURES)
<< std::endl;
VERIFY( !SGPath::standardLocation(SGPath::HOME ).isNull() );
VERIFY( !SGPath::standardLocation(SGPath::DESKTOP ).isNull() );
VERIFY( !SGPath::standardLocation(SGPath::DOWNLOADS).isNull() );
VERIFY( !SGPath::standardLocation(SGPath::DOCUMENTS).isNull() );
VERIFY( !SGPath::standardLocation(SGPath::PICTURES ).isNull() );
}
SGPath::Permissions validateNone(const SGPath&)
@@ -192,12 +178,12 @@ int main(int argc, char* argv[])
pp.append("./test-dir/file.txt");
COMPARE(pp.create_dir(0700), -3);
pp.setPermissionChecker(&validateRead);
pp.setPermissonChecker(&validateRead);
COMPARE(pp.canRead(), true);
COMPARE(pp.canWrite(), false);
COMPARE(pp.create_dir(0700), -3);
pp.setPermissionChecker(&validateWrite);
pp.setPermissonChecker(&validateWrite);
COMPARE(pp.canRead(), false);
COMPARE(pp.canWrite(), true);

View File

@@ -56,9 +56,10 @@ static const char sgSearchPathSep = ':';
#endif
#ifdef _WIN32
#include <ShlObj.h> // for CSIDL
static SGPath pathForCSIDL(int csidl, const SGPath& def)
static SGPath pathForCSIDL(int csidl, const SGPath::PermissonChecker& checker)
{
typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, LPSTR, int, BOOL);
static GetSpecialFolderPath SHGetSpecialFolderPath = NULL;
@@ -70,63 +71,17 @@ static SGPath pathForCSIDL(int csidl, const SGPath& def)
}
if (!SHGetSpecialFolderPath){
return def;
return SGPath();
}
char path[MAX_PATH];
if (SHGetSpecialFolderPath(0, path, csidl, false)) {
return SGPath(path, def.getPermissionChecker());
return SGPath(path, checker);
}
return def;
return SGPath();
}
#elif __APPLE__
// defined in CocoaHelpers.mm
SGPath appleSpecialFolder(int dirType, int domainMask, const SGPath& def);
#else
static SGPath getXDGDir( const std::string& name,
const SGPath& def,
const std::string& fallback )
{
// http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
// $XDG_CONFIG_HOME defines the base directory relative to which user specific
// configuration files should be stored. If $XDG_CONFIG_HOME is either not set
// or empty, a default equal to $HOME/.config should be used.
const SGPath user_dirs = SGPath::fromEnv( "XDG_CONFIG_HOME",
SGPath::home() / ".config")
/ "user-dirs.dirs";
// Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped
// homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an absolute
// path. No other format is supported.
const std::string XDG_ID = "XDG_" + name + "_DIR=\"";
std::ifstream user_dirs_file( user_dirs.c_str() );
std::string line;
while( std::getline(user_dirs_file, line).good() )
{
if( !starts_with(line, XDG_ID) || *line.rbegin() != '"' )
continue;
// Extract dir from XDG_<name>_DIR="<dir>"
line = line.substr(XDG_ID.length(), line.length() - XDG_ID.length() - 1 );
const std::string HOME = "$HOME";
if( starts_with(line, HOME) )
return SGPath::home(def)
/ simgear::strutils::unescape(line.substr(HOME.length()));
return SGPath(line, def.getPermissionChecker());
}
if( def.isNull() )
return SGPath::home(def) / fallback;
return def;
}
#endif
// For windows, replace "\" by "/".
@@ -148,7 +103,7 @@ SGPath::fix()
// default constructor
SGPath::SGPath(PermissionChecker validator)
SGPath::SGPath(PermissonChecker validator)
: path(""),
_permission_checker(validator),
_cached(false),
@@ -159,7 +114,7 @@ SGPath::SGPath(PermissionChecker validator)
// create a path based on "path"
SGPath::SGPath( const std::string& p, PermissionChecker validator )
SGPath::SGPath( const std::string& p, PermissonChecker validator )
: path(p),
_permission_checker(validator),
_cached(false),
@@ -172,7 +127,7 @@ SGPath::SGPath( const std::string& p, PermissionChecker validator )
// create a path based on "path" and a "subpath"
SGPath::SGPath( const SGPath& p,
const std::string& r,
PermissionChecker validator )
PermissonChecker validator )
: path(p.path),
_permission_checker(validator),
_cached(false),
@@ -228,14 +183,14 @@ void SGPath::set( const string& p ) {
}
//------------------------------------------------------------------------------
void SGPath::setPermissionChecker(PermissionChecker validator)
void SGPath::setPermissonChecker(PermissonChecker validator)
{
_permission_checker = validator;
_rwCached = false;
}
//------------------------------------------------------------------------------
SGPath::PermissionChecker SGPath::getPermissionChecker() const
SGPath::PermissonChecker SGPath::getPermissonChecker() const
{
return _permission_checker;
}
@@ -379,7 +334,6 @@ string SGPath::complete_lower_extension() const
}
}
//------------------------------------------------------------------------------
void SGPath::validate() const
{
if (_cached && _cacheEnabled) {
@@ -424,7 +378,6 @@ void SGPath::validate() const
_cached = true;
}
//------------------------------------------------------------------------------
void SGPath::checkAccess() const
{
if( _rwCached && _cacheEnabled )
@@ -477,23 +430,14 @@ bool SGPath::isFile() const
return _exists && _isFile;
}
//------------------------------------------------------------------------------
#ifdef _WIN32
# define sgMkDir(d,m) _mkdir(d)
#else
# define sgMkDir(d,m) mkdir(d,m)
#endif
int SGPath::create_dir(mode_t mode)
{
if( !canWrite() )
{
SG_LOG( SG_IO,
SG_WARN, "Error creating directory for '" << str() << "'"
" reason: access denied" );
return -3;
}
int SGPath::create_dir( mode_t mode ) {
string_list dirlist = sgPathSplit(dir());
if ( dirlist.empty() )
return -1;
@@ -510,31 +454,37 @@ int SGPath::create_dir(mode_t mode)
i = 2;
}
#endif
struct stat info;
int r;
for(; (r = stat(dir.c_str(), &info)) == 0 && i < path_elements.size(); ++i)
dir.append(path_elements[i]);
if( r == 0 )
return 0; // Directory already exists
for(;;)
{
if( sgMkDir(dir.c_str(), mode) )
{
SG_LOG( SG_IO,
SG_ALERT, "Error creating directory: (" << dir.str() << ")" );
return -2;
struct stat info;
int r;
for(; ( r = stat( dir.c_str(), &info ) ) == 0 && i < path_elements.size(); i++) {
dir.append(path_elements[i]);
}
else
SG_LOG(SG_IO, SG_DEBUG, "Directory created: " << dir.str());
if ( r == 0 ) {
return 0; // Directory already exists
}
for(;;)
{
if( !dir.canWrite() )
{
SG_LOG( SG_IO,
SG_WARN, "Error creating directory: (" << dir.str() << ")" <<
" reason: access denied" );
return -3;
}
else if( sgMkDir(dir.c_str(), mode) )
{
SG_LOG( SG_IO,
SG_ALERT, "Error creating directory: (" << dir.str() << ")" );
return -2;
}
if( i >= path_elements.size() )
return 0;
if( i >= path_elements.size() )
return 0;
dir.append(path_elements[i++]);
}
dir.append(path_elements[i++]);
}
return 0;
return 0;
}
string_list sgPathBranchSplit( const string &dirpath ) {
@@ -697,56 +647,6 @@ bool SGPath::rename(const SGPath& newName)
return true;
}
//------------------------------------------------------------------------------
SGPath SGPath::standardLocation(StandardLocation type, const SGPath& def)
{
switch(type)
{
case HOME:
return home(def);
#ifdef _WIN32
case DESKTOP:
return pathForCSIDL(CSIDL_DESKTOPDIRECTORY, def);
case DOWNLOADS:
// TODO use KnownFolders
// http://msdn.microsoft.com/en-us/library/bb776911%28v=vs.85%29.aspx
if( !def.isNull() )
return def;
return pathForCSIDL(CSIDL_DESKTOPDIRECTORY, def);
case DOCUMENTS:
return pathForCSIDL(CSIDL_MYDOCUMENTS, def);
case PICTURES:
return pathForCSIDL(CSIDL_MYPICTURES, def);
#elif __APPLE__
// since this is C++, we can't include NSPathUtilities.h to access the enum
// values, so hard-coding them here (they are stable, don't worry)
case DOWNLOADS:
return appleSpecialFolder(15, 1, def);
case DESKTOP:
return appleSpecialFolder(12, 1, def);
case DOCUMENTS:
return appleSpecialFolder(9, 1, def);
case PICTURES:
return appleSpecialFolder(19, 1, def);
#else
case DESKTOP:
return getXDGDir("DESKTOP", def, "Desktop");
case DOWNLOADS:
return getXDGDir("DOWNLOADS", def, "Downloads");
case DOCUMENTS:
return getXDGDir("DOCUMENTS", def, "Documents");
case PICTURES:
return getXDGDir("PICTURES", def, "Pictures");
#endif
default:
SG_LOG( SG_GENERAL,
SG_WARN,
"SGPath::standardLocation() unhandled type: " << type );
return def;
}
}
//------------------------------------------------------------------------------
SGPath SGPath::fromEnv(const char* name, const SGPath& def)
{
@@ -771,19 +671,84 @@ SGPath SGPath::home(const SGPath& def)
}
#endif
#ifdef _WIN32
//------------------------------------------------------------------------------
SGPath SGPath::desktop(const SGPath& def)
{
return standardLocation(DESKTOP, def);
SGPath r = pathForCSIDL(CSIDL_DESKTOPDIRECTORY, def._permission_checker);
if (!r.isNull()) {
return r;
}
SG_LOG(SG_GENERAL, SG_ALERT, "SGPath::desktop() failed, bad" );
return def;
}
//------------------------------------------------------------------------------
SGPath SGPath::documents(const SGPath& def)
{
return standardLocation(DOCUMENTS, def);
SGPath r = pathForCSIDL(CSIDL_MYDOCUMENTS, def._permission_checker);
if (!r.isNull()) {
return r;
}
SG_LOG(SG_GENERAL, SG_ALERT, "SGPath::documents() failed, bad" );
return def;
}
#elif __APPLE__
#include <CoreServices/CoreServices.h>
//------------------------------------------------------------------------------
SGPath SGPath::desktop(const SGPath& def)
{
FSRef ref;
OSErr err = FSFindFolder(kUserDomain, kDesktopFolderType, false, &ref);
if (err)
return def;
unsigned char path[1024];
if (FSRefMakePath(&ref, path, 1024) != noErr)
return def;
return SGPath((const char*) path, def._permission_checker);
}
#else
//------------------------------------------------------------------------------
SGPath SGPath::desktop(const SGPath& def)
{
// http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
// $XDG_CONFIG_HOME defines the base directory relative to which user specific
// configuration files should be stored. If $XDG_CONFIG_HOME is either not set
// or empty, a default equal to $HOME/.config should be used.
const SGPath user_dirs = fromEnv("XDG_CONFIG_HOME", home() / ".config")
/ "user-dirs.dirs";
// Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped
// homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an absolute
// path. No other format is supported.
const std::string DESKTOP = "XDG_DESKTOP_DIR=\"";
std::ifstream user_dirs_file( user_dirs.c_str() );
std::string line;
while( std::getline(user_dirs_file, line).good() )
{
if( !starts_with(line, DESKTOP) || *line.rbegin() != '"' )
continue;
// Extract dir from XDG_DESKTOP_DIR="<dir>"
line = line.substr(DESKTOP.length(), line.length() - DESKTOP.length() - 1 );
const std::string HOME = "$HOME";
if( starts_with(line, HOME) )
return home(def) / simgear::strutils::unescape(line.substr(HOME.length()));
return SGPath(line, def._permission_checker);
}
return home(def) / "Desktop";
}
#endif
std::string SGPath::realpath() const
{
#if (defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED <= 1050)

View File

@@ -57,10 +57,10 @@ public:
bool read : 1;
bool write : 1;
};
typedef Permissions (*PermissionChecker)(const SGPath&);
typedef Permissions (*PermissonChecker)(const SGPath&);
/** Default constructor */
explicit SGPath(PermissionChecker validator = NULL);
explicit SGPath(PermissonChecker validator = NULL);
/** Copy contructor */
SGPath(const SGPath& p);
@@ -71,7 +71,7 @@ public:
* Construct a path based on the starting path provided.
* @param p initial path
*/
SGPath( const std::string& p, PermissionChecker validator = NULL );
SGPath( const std::string& p, PermissonChecker validator = NULL );
/**
* Construct a path based on the starting path provided and a relative subpath
@@ -80,7 +80,7 @@ public:
*/
SGPath( const SGPath& p,
const std::string& r,
PermissionChecker validator = NULL );
PermissonChecker validator = NULL );
/** Destructor */
~SGPath();
@@ -95,8 +95,8 @@ public:
bool operator==(const SGPath& other) const;
bool operator!=(const SGPath& other) const;
void setPermissionChecker(PermissionChecker validator);
PermissionChecker getPermissionChecker() const;
void setPermissonChecker(PermissonChecker validator);
PermissonChecker getPermissonChecker() const;
/**
* Set if file information (exists, type, mod-time) is cached or
@@ -206,12 +206,9 @@ public:
/**
* Create the designated directory.
*
* @param mode Permissions. See:
* http://en.wikipedia.org/wiki/File_system_permissions#Numeric_notation
* @return 0 on success, or <0 on failure.
*/
int create_dir(mode_t mode = 0755);
int create_dir(mode_t mode);
/**
* Check if reading file is allowed. Readabilty does not imply the existance
@@ -260,18 +257,6 @@ public:
*/
bool rename(const SGPath& newName);
enum StandardLocation
{
HOME,
DESKTOP,
DOWNLOADS,
DOCUMENTS,
PICTURES
};
static SGPath standardLocation( StandardLocation type,
const SGPath& def = SGPath() );
/**
* Get a path stored in the environment variable with the given \a name.
*
@@ -295,7 +280,6 @@ public:
* Get path to the user's documents directory
*/
static SGPath documents(const SGPath& def = SGPath());
private:
void fix();
@@ -304,7 +288,7 @@ private:
void checkAccess() const;
std::string path;
PermissionChecker _permission_checker;
PermissonChecker _permission_checker;
mutable bool _cached : 1;
mutable bool _rwCached : 1;

View File

@@ -20,7 +20,7 @@ int main()
"third line ends with both\r\n"
"fourth line as well\r\n"
"fifth line is another CR/LF line\r\n"
"end of test\r\n", 158);
"end of test\r\n", 1024);
f.close();
}

View File

@@ -27,7 +27,6 @@
#include "strutils.hxx"
#include <simgear/debug/logstream.hxx>
#include <simgear/package/md5.h>
using std::string;
using std::vector;
@@ -54,11 +53,8 @@ namespace simgear {
size_t len = get_length (p);
if (len == 1) return *p;
value_type res = static_cast<unsigned char> ( *p & (0xff >> (len + 1))) << ((len - 1) * 6 );
for (--len; len; --len) {
value_type next_byte = static_cast<unsigned char> (*(++p)) - 0x80;
if (next_byte & 0xC0) return 0x00ffffff; // invalid UTF-8
res |= next_byte << ((len - 1) * 6);
}
for (--len; len; --len)
res |= (static_cast<unsigned char> (*(++p)) - 0x80) << ((len - 1) * 6);
return res;
}
@@ -66,7 +62,6 @@ namespace simgear {
string s_latin1;
for (string::iterator p = s_utf8.begin(); p != s_utf8.end(); ++p) {
value_type value = get_value<string::iterator&>(p);
if (value > 0x10ffff) return s_utf8; // invalid UTF-8: guess that the input was already Latin-1
if (value > 0xff) SG_LOG(SG_IO, SG_WARN, "utf8ToLatin1: wrong char value: " << value);
s_latin1 += static_cast<char>(value);
}
@@ -402,32 +397,8 @@ std::string convertWindowsLocal8BitToUtf8(const std::string& a)
#endif
}
//------------------------------------------------------------------------------
std::string md5(const unsigned char* data, size_t num)
{
SG_MD5_CTX md5_ctx;
SG_MD5Init(&md5_ctx);
SG_MD5Update(&md5_ctx, data, num);
unsigned char digest[MD5_DIGEST_LENGTH];
SG_MD5Final(digest, &md5_ctx);
return encodeHex(digest, MD5_DIGEST_LENGTH);
}
//------------------------------------------------------------------------------
std::string md5(const char* data, size_t num)
{
return md5(reinterpret_cast<const unsigned char*>(data), num);
}
//------------------------------------------------------------------------------
std::string md5(const std::string& str)
{
return md5(reinterpret_cast<const unsigned char*>(str.c_str()), str.size());
}
//------------------------------------------------------------------------------
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
@@ -507,24 +478,28 @@ void decodeBase64(const std::string& encoded_string, std::vector<unsigned char>&
}
}
//------------------------------------------------------------------------------
const char hexChar[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
std::string encodeHex(const std::string& bytes)
{
return encodeHex(
reinterpret_cast<const unsigned char*>(bytes.c_str()),
bytes.size()
);
std::string hex;
size_t count = bytes.size();
for (unsigned int i=0; i<count;++i) {
unsigned char c = bytes[i];
hex.push_back(hexChar[c >> 4]);
hex.push_back(hexChar[c & 0x0f]);
}
return hex;
}
std::string encodeHex(const unsigned char* rawBytes, unsigned int length)
{
std::string hex(length * 2, '\0');
std::string hex;
for (unsigned int i=0; i<length;++i) {
unsigned char c = *rawBytes++;
hex[i * 2] = hexChar[c >> 4];
hex[i * 2 + 1] = hexChar[c & 0x0f];
hex.push_back(hexChar[c >> 4]);
hex.push_back(hexChar[c & 0x0f]);
}
return hex;

View File

@@ -176,13 +176,6 @@ namespace simgear {
WCharVec convertUtf8ToWString(const std::string& a);
#endif
/**
* Get md5 hash of raw data.
*/
std::string md5(const unsigned char* data, size_t num);
std::string md5(const char* data, size_t num);
std::string md5(const std::string& str);
/**
* convert base-64 encoded data to raw bytes (possibly with embedded
* NULs). Throws an exception if input data is not base64, or is

View File

@@ -1,80 +1,84 @@
/// Unit tests for function inside strutils package
#define BOOST_TEST_MODULE misc
#include <BoostTestTargetConfig.h>
////////////////////////////////////////////////////////////////////////
// Test harness.
////////////////////////////////////////////////////////////////////////
#include <simgear/compiler.h>
#include <iostream>
#include "strutils.hxx"
namespace strutils = simgear::strutils;
using std::cout;
using std::cerr;
using std::endl;
BOOST_AUTO_TEST_CASE( strutils_functions )
using namespace simgear::strutils;
#define COMPARE(a, b) \
if ((a) != (b)) { \
cerr << "failed:" << #a << " != " << #b << endl; \
exit(1); \
}
#define VERIFY(a) \
if (!(a)) { \
cerr << "failed:" << #a << endl; \
exit(1); \
}
int main (int ac, char ** av)
{
std::string a("abcd");
BOOST_CHECK_EQUAL(strutils::strip(a), a);
BOOST_CHECK_EQUAL(strutils::strip(" a "), "a");
BOOST_CHECK_EQUAL(strutils::lstrip(" a "), "a ");
BOOST_CHECK_EQUAL(strutils::rstrip("\ta "), "\ta");
std::string a("abcd");
COMPARE(strip(a), a);
COMPARE(strip(" a "), "a");
COMPARE(lstrip(" a "), "a ");
COMPARE(rstrip("\ta "), "\ta");
// check internal spacing is preserved
COMPARE(strip("\t \na \t b\r \n "), "a \t b");
VERIFY(starts_with("banana", "ban"));
VERIFY(!starts_with("abanana", "ban"));
VERIFY(starts_with("banana", "banana")); // pass - string starts with itself
VERIFY(!starts_with("ban", "banana")); // fail - original string is prefix of
VERIFY(ends_with("banana", "ana"));
VERIFY(ends_with("foo.text", ".text"));
VERIFY(!ends_with("foo.text", ".html"));
COMPARE(simplify("\ta\t b \nc\n\r \r\n"), "a b c");
COMPARE(simplify("The quick - brown dog!"), "The quick - brown dog!");
COMPARE(simplify("\r\n \r\n \t \r"), "");
COMPARE(to_int("999"), 999);
COMPARE(to_int("0000000"), 0);
COMPARE(to_int("-10000"), -10000);
VERIFY(compare_versions("1.0.12", "1.1") < 0);
VERIFY(compare_versions("1.1", "1.0.12") > 0);
VERIFY(compare_versions("10.6.7", "10.6.7") == 0);
VERIFY(compare_versions("2.0", "2.0.99") < 0);
VERIFY(compare_versions("99", "99") == 0);
VERIFY(compare_versions("99", "98") > 0);
// since we compare numerically, leasing zeros shouldn't matter
VERIFY(compare_versions("0.06.7", "0.6.07") == 0);
string_list la = split("zero one two three four five");
COMPARE(la[2], "two");
COMPARE(la[5], "five");
COMPARE(la.size(), 6);
string_list lb = split("alpha:beta:gamma:delta", ":", 2);
COMPARE(lb.size(), 3);
COMPARE(lb[0], "alpha");
COMPARE(lb[1], "beta");
COMPARE(lb[2], "gamma:delta");
std::string j = join(la, "&");
COMPARE(j, "zero&one&two&three&four&five");
// check internal spacing is preserved
BOOST_CHECK_EQUAL(strutils::strip("\t \na \t b\r \n "), "a \t b");
COMPARE(unescape("\\ \\n\\t\\x41\\117a"), " \n\tAOa");
BOOST_CHECK(strutils::starts_with("banana", "ban"));
BOOST_CHECK(!strutils::starts_with("abanana", "ban"));
BOOST_CHECK(strutils::starts_with("banana", "banana")); // pass - string starts with itself
BOOST_CHECK(!strutils::starts_with("ban", "banana")); // fail - original string is prefix of
BOOST_CHECK(strutils::ends_with("banana", "ana"));
BOOST_CHECK(strutils::ends_with("foo.text", ".text"));
BOOST_CHECK(!strutils::ends_with("foo.text", ".html"));
BOOST_CHECK_EQUAL(strutils::simplify("\ta\t b \nc\n\r \r\n"), "a b c");
BOOST_CHECK_EQUAL(strutils::simplify("The quick - brown dog!"), "The quick - brown dog!");
BOOST_CHECK_EQUAL(strutils::simplify("\r\n \r\n \t \r"), "");
BOOST_CHECK_EQUAL(strutils::to_int("999"), 999);
BOOST_CHECK_EQUAL(strutils::to_int("0000000"), 0);
BOOST_CHECK_EQUAL(strutils::to_int("-10000"), -10000);
string_list la = strutils::split("zero one two three four five");
BOOST_CHECK_EQUAL(la[2], "two");
BOOST_CHECK_EQUAL(la[5], "five");
BOOST_CHECK_EQUAL(la.size(), 6);
string_list lb = strutils::split("alpha:beta:gamma:delta", ":", 2);
BOOST_CHECK_EQUAL(lb.size(), 3);
BOOST_CHECK_EQUAL(lb[0], "alpha");
BOOST_CHECK_EQUAL(lb[1], "beta");
BOOST_CHECK_EQUAL(lb[2], "gamma:delta");
std::string j = strutils::join(la, "&");
BOOST_CHECK_EQUAL(j, "zero&one&two&three&four&five");
BOOST_CHECK_EQUAL(strutils::unescape("\\ \\n\\t\\x41\\117a"), " \n\tAOa");
}
BOOST_AUTO_TEST_CASE( compare_versions )
{
BOOST_CHECK_LT(strutils::compare_versions("1.0.12", "1.1"), 0);
BOOST_CHECK_GT(strutils::compare_versions("1.1", "1.0.12"), 0);
BOOST_CHECK_EQUAL(strutils::compare_versions("10.6.7", "10.6.7"), 0);
BOOST_CHECK_LT(strutils::compare_versions("2.0", "2.0.99"), 0);
BOOST_CHECK_EQUAL(strutils::compare_versions("99", "99"), 0);
BOOST_CHECK_GT(strutils::compare_versions("99", "98"), 0);
// since we compare numerically, leasing zeros shouldn't matter
BOOST_CHECK_EQUAL(strutils::compare_versions("0.06.7", "0.6.07"), 0);
}
BOOST_AUTO_TEST_CASE( md5_hex )
{
// hex encoding
unsigned char raw_data[] = {0x0f, 0x1a, 0xbc, 0xd2, 0xe3, 0x45, 0x67, 0x89};
const std::string& hex_data =
strutils::encodeHex(raw_data, sizeof(raw_data)/sizeof(raw_data[0]));
BOOST_REQUIRE_EQUAL(hex_data, "0f1abcd2e3456789");
BOOST_REQUIRE_EQUAL(strutils::encodeHex("abcde"), "6162636465");
// md5
BOOST_CHECK_EQUAL(strutils::md5("test"), "098f6bcd4621d373cade4e832627b4f6");
cout << "all tests passed successfully!" << endl;
return 0;
}

View File

@@ -2,37 +2,18 @@
#ifndef SG_MISC_TEST_MACROS_HXX
#define SG_MISC_TEST_MACROS_HXX
#include <cmath> // for fabs()
#define COMPARE(a, b) \
if ((a) != (b)) { \
std::cerr << "failed:" << #a << " != " << #b << std::endl; \
std::cerr << "\tgot:'" << a << "'" << std::endl; \
std::cerr << "\tat:" << __FILE__ << ":" << __LINE__ << std::endl; \
exit(1); \
}
#define VERIFY(a) \
if (!(a)) { \
std::cerr << "failed:" << #a << std::endl; \
std::cerr << "\tat:" << __FILE__ << ":" << __LINE__ << std::endl; \
exit(1); \
}
#define COMPARE_EP(a, b) \
if (fabs(a - b) > SG_EPSILON) { \
std::cerr << "failed with epsilon:" << #a << " != " << #b << std::endl; \
std::cerr << "\tgot:'" << a << "'" << std::endl; \
std::cerr << "\tat:" << __FILE__ << ":" << __LINE__ << std::endl; \
exit(1); \
}
#define COMPARE_EP2(a, b, ep) \
if (fabs(a - b) > ep) { \
std::cerr << "failed with epsilon:" << #a << " != " << #b << std::endl; \
std::cerr << "\tgot:'" << a << "'" << std::endl; \
std::cerr << "\tat:" << __FILE__ << ":" << __LINE__ << std::endl; \
exit(1); \
}
#endif // of SG_MISC_TEST_MACROS_HXX

View File

@@ -1,26 +0,0 @@
/// Unit tests for utf8ToLatin1 conversion function
#define BOOST_TEST_MODULE misc
#include <BoostTestTargetConfig.h>
#include "strutils.hxx"
#include <string>
BOOST_AUTO_TEST_CASE( utf8_latin1_conversion )
{
std::string utf8_string1 = "Zweibr\u00FCcken";
//valid UTF-8, convertible to Latin-1
std::string latin1_string1 = "Zweibr\374cken";
//Latin-1, not valid UTF-8
std::string utf8_string2 = "\u600f\U00010143";
//valid UTF-8, out of range for Latin-1
std::string output_string1u = simgear::strutils::utf8ToLatin1(utf8_string1);
BOOST_CHECK_EQUAL(output_string1u, latin1_string1);
std::string output_string1l = simgear::strutils::utf8ToLatin1(latin1_string1);
BOOST_CHECK_EQUAL(output_string1l, latin1_string1);
std::string output_string3 = simgear::strutils::utf8ToLatin1(utf8_string2);
//we don't check the result of this one as there is no right answer,
//just make sure it doesn't crash/hang
}

View File

@@ -71,9 +71,7 @@ static double numify(naContext ctx, naRef o)
else if(IS_NIL(o)) ERR(ctx, "nil used in numeric context");
else if(!IS_STR(o)) ERR(ctx, "non-scalar in numeric context");
else if(naStr_tonum(o, &n)) return n;
else naRuntimeError( ctx,
"non-numeric string in numeric context: '%s'",
naStr_data(o) );
else ERR(ctx, "non-numeric string in numeric context");
return 0;
}
@@ -594,9 +592,6 @@ static naRef run(naContext ctx)
case OP_LTE: BINOP(l <= r ? 1 : 0); break;
case OP_GT: BINOP(l > r ? 1 : 0); break;
case OP_GTE: BINOP(l >= r ? 1 : 0); break;
case OP_BIT_AND: BINOP((int)l & (int)r); break;
case OP_BIT_OR: BINOP((int)l | (int)r); break;
case OP_BIT_XOR: BINOP((int)l ^ (int)r); break;
#undef BINOP
case OP_EQ: case OP_NEQ:
@@ -610,9 +605,6 @@ static naRef run(naContext ctx)
case OP_NEG:
STK(1) = naNum(-numify(ctx, STK(1)));
break;
case OP_BIT_NEG:
STK(1) = naNum(~(int)numify(ctx, STK(1)));
break;
case OP_NOT:
STK(1) = naNum(boolify(ctx, STK(1)) ? 0 : 1);
break;

View File

@@ -26,8 +26,7 @@ enum {
OP_MEMBER, OP_SETMEMBER, OP_LOCAL, OP_SETLOCAL, OP_NEWVEC, OP_VAPPEND,
OP_NEWHASH, OP_HAPPEND, OP_MARK, OP_UNMARK, OP_BREAK, OP_SETSYM, OP_DUP2,
OP_INDEX, OP_BREAK2, OP_PUSHEND, OP_JIFTRUE, OP_JIFNOT, OP_FCALLH,
OP_MCALLH, OP_XCHG2, OP_UNPACK, OP_SLICE, OP_SLICE2, OP_BIT_AND, OP_BIT_OR,
OP_BIT_XOR, OP_BIT_NEG
OP_MCALLH, OP_XCHG2, OP_UNPACK, OP_SLICE, OP_SLICE2
};
struct Frame {

View File

@@ -7,7 +7,6 @@
// These are more sensical predicate names in most contexts in this file
#define LEFT(tok) ((tok)->children)
#define RIGHT(tok) ((tok)->lastChild)
#define UNARY(tok) (LEFT(tok) && LEFT(tok) == RIGHT(tok))
#define BINARY(tok) (LEFT(tok) && RIGHT(tok) && LEFT(tok)->next == RIGHT(tok))
// Forward references for recursion
@@ -166,15 +165,6 @@ static int defArg(struct Parser* p, struct Token* t)
RIGHT(t)->num *= -1;
return defArg(p, RIGHT(t));
}
if(t->type == TOK_CAT && RIGHT(t) &&
RIGHT(t)->type == TOK_LITERAL && !RIGHT(t)->str)
{
/* default arguments are constants, but "~1" parses as two
* tokens, so we have to subset the expression generator for that
* case */
RIGHT(t)->num = ~(int)RIGHT(t)->num;
return defArg(p, RIGHT(t));
}
return findConstantIndex(p, t);
}
@@ -644,15 +634,11 @@ static void genExpr(struct Parser* p, struct Token* t)
else genExpr(p, LEFT(t));
break;
case TOK_LBRA:
if(UNARY(t)) {
emit(p, OP_NEWVEC);
genList(p, LEFT(t), 1);
}
else if(BINARY(t)) {
if(BINARY(t)) {
genExtract(p, t);
} else {
// forbid usage as 'vec[]'
naParseError(p, "missing index or slice expression(s)", t->line);
emit(p, OP_NEWVEC);
genList(p, LEFT(t), 1);
}
break;
case TOK_LCURL:
@@ -687,21 +673,6 @@ static void genExpr(struct Parser* p, struct Token* t)
genExpr(p, RIGHT(t)); // unary negation (see also TOK_MINUS!)
emit(p, OP_NEG);
break;
case TOK_CAT:
if(BINARY(t)) {
genBinOp(OP_CAT, p, t); // string concatenation
} else if(RIGHT(t) && RIGHT(t)->type == TOK_LITERAL && !RIGHT(t)->str) {
RIGHT(t)->num = ~(int)RIGHT(t)->num; // Pre-negate constants
genScalarConstant(p, RIGHT(t));
} else {
genExpr(p, RIGHT(t)); // unary, bitwise negation
emit(p, OP_BIT_NEG);
}
break;
case TOK_BIT_NEG:
genExpr(p, RIGHT(t)); // unary, bitwise negation (see also TOK_CAT!)
emit(p, OP_BIT_NEG);
break;
case TOK_DOT:
genExpr(p, LEFT(t));
if(!RIGHT(t) || RIGHT(t)->type != TOK_SYMBOL)
@@ -714,12 +685,10 @@ static void genExpr(struct Parser* p, struct Token* t)
case TOK_AND: case TOK_OR:
genShortCircuit(p, t);
break;
case TOK_BIT_AND:genBinOp(OP_BIT_AND, p, t); break;
case TOK_BIT_OR: genBinOp(OP_BIT_OR, p, t); break;
case TOK_BIT_XOR:genBinOp(OP_BIT_XOR, p, t); break;
case TOK_MUL: genBinOp(OP_MUL, p, t); break;
case TOK_PLUS: genBinOp(OP_PLUS, p, t); break;
case TOK_DIV: genBinOp(OP_DIV, p, t); break;
case TOK_CAT: genBinOp(OP_CAT, p, t); break;
case TOK_LT: genBinOp(OP_LT, p, t); break;
case TOK_LTE: genBinOp(OP_LTE, p, t); break;
case TOK_EQ: genBinOp(OP_EQ, p, t); break;
@@ -731,9 +700,6 @@ static void genExpr(struct Parser* p, struct Token* t)
case TOK_MULEQ: genEqOp(OP_MUL, p, t); break;
case TOK_DIVEQ: genEqOp(OP_DIV, p, t); break;
case TOK_CATEQ: genEqOp(OP_CAT, p, t); break;
case TOK_BIT_ANDEQ: genEqOp(OP_BIT_AND, p, t); break;
case TOK_BIT_OREQ: genEqOp(OP_BIT_OR, p, t); break;
case TOK_BIT_XOREQ: genEqOp(OP_BIT_XOR, p, t); break;
default:
naParseError(p, "parse error", t->line);
};

View File

@@ -4,7 +4,6 @@ set(HEADERS
Ghost.hxx
NasalCallContext.hxx
NasalHash.hxx
NasalObject.hxx
NasalObjectHolder.hxx
NasalString.hxx
from_nasal.hxx
@@ -20,10 +19,9 @@ set(DETAIL_HEADERS
)
set(SOURCES
Ghost.cxx
NasalHash.cxx
NasalObjectHolder.cxx
NasalString.cxx
NasalObject.cxx
detail/from_nasal_helper.cxx
detail/to_nasal_helper.cxx
)
@@ -35,14 +33,4 @@ if(ENABLE_TESTS)
add_executable(test_cppbind cppbind_test.cxx)
add_test(cppbind ${EXECUTABLE_OUTPUT_PATH}/test_cppbind)
target_link_libraries(test_cppbind ${TEST_LIBS})
endif(ENABLE_TESTS)
add_boost_test(cppbind_ghost
SOURCES cppbind_test_ghost.cxx
LIBRARIES ${TEST_LIBS}
)
add_boost_test(nasal_num
SOURCES nasal_num_test.cxx
LIBRARIES ${TEST_LIBS}
)
endif(ENABLE_TESTS)

View File

@@ -1,108 +0,0 @@
// Expose C++ objects to Nasal as ghosts
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "Ghost.hxx"
namespace nasal
{
namespace internal
{
//--------------------------------------------------------------------------
GhostMetadata::DestroyList GhostMetadata::_destroy_list;
//--------------------------------------------------------------------------
void GhostMetadata::addNasalBase(const naRef& parent)
{
assert( naIsHash(parent) );
_parents.push_back(parent);
}
//--------------------------------------------------------------------------
bool GhostMetadata::isBaseOf(naGhostType* ghost_type, bool& is_weak) const
{
if( ghost_type == _ghost_type_strong_ptr )
{
is_weak = false;
return true;
}
if( ghost_type == _ghost_type_weak_ptr )
{
is_weak = true;
return true;
}
for( DerivedList::const_iterator derived = _derived_classes.begin();
derived != _derived_classes.end();
++derived )
{
if( (*derived)->isBaseOf(ghost_type, is_weak) )
return true;
}
return false;
}
//--------------------------------------------------------------------------
GhostMetadata::GhostMetadata( const std::string& name,
const naGhostType* ghost_type_strong,
const naGhostType* ghost_type_weak ):
_name_strong(name),
_name_weak(name + " (weak ref)"),
_ghost_type_strong_ptr(ghost_type_strong),
_ghost_type_weak_ptr(ghost_type_weak)
{
}
//--------------------------------------------------------------------------
void GhostMetadata::addDerived(const GhostMetadata* derived)
{
assert(derived);
_derived_classes.push_back(derived);
SG_LOG
(
SG_NASAL,
SG_INFO,
"Ghost::addDerived: " << _name_strong << " -> "
<< derived->_name_strong
);
}
//--------------------------------------------------------------------------
naRef GhostMetadata::getParents(naContext c)
{
return nasal::to_nasal(c, _parents);
}
} // namespace internal
//----------------------------------------------------------------------------
void ghostProcessDestroyList()
{
using internal::GhostMetadata;
typedef GhostMetadata::DestroyList::const_iterator destroy_iterator;
for( destroy_iterator it = GhostMetadata::_destroy_list.begin();
it != GhostMetadata::_destroy_list.end();
++it )
it->first(it->second);
GhostMetadata::_destroy_list.clear();
}
} // namespace nasal

File diff suppressed because it is too large Load Diff

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