diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b5bd14..22bfbad 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,32 +1,29 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.8) project(LianSearch) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O2 -Wall -Wextra") -set(CMAKE_EXE_LINKER_FLAGS "-static -static-libgcc -static-libstdc++") -set(SOURCE_FILES - main.cpp - tinyxml/tinyxmlparser.cpp - tinyxml/tinyxmlerror.cpp - tinyxml/tinyxml.cpp - tinyxml/tinystr.cpp - liansearch.cpp - openlist.cpp - config.cpp - map.cpp - mission.cpp - xmllogger.cpp) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wall") -set(HEADER_FILES - tinyxml/tinyxml.h - tinyxml/tinystr.h - searchresult.h - gl_const.h - liansearch.h - node.h - openlist.h - config.h - map.h - logger.h - mission.h - search.h - xmllogger.h) -add_executable(LianSearch ${SOURCE_FILES} ${HEADER_FILES}) +add_subdirectory(contrib) + +add_executable(LianSearch + src/main.cpp + src/liansearch.cpp + src/structures/search_tree.cpp + src/structures/config.cpp + src/structures/map.cpp + src/structures/mission.cpp + src/log/logger.cpp + src/log/xml_utils.cpp + + src/structures/search_result.h + src/log/gl_const.h + src/liansearch.h + src/structures/node.h + src/structures/search_tree.h + src/structures/config.h + src/structures/map.h + src/log/logger.h + src/structures/mission.h + src/log/xml_utils.h) +target_link_libraries(LianSearch PUBLIC tinyxml) +target_include_directories(LianSearch PUBLIC contrib) diff --git a/README.md b/README.md index fcd592a..5d44f98 100644 --- a/README.md +++ b/README.md @@ -16,17 +16,15 @@ Algorithm supports XML files as input and output format. Input file contains map ## Getting Started -To go and try this algorithm you can use QtCreator or CMake. -Both `.pro` and `CMakeLists` files are available in the repository. +To go and try this algorithm you can use CMake. +`CMakeLists` file is available in the repository. -Notice, that project uses C++11 standart. Make sure that your compiler supports it. +Notice, that project uses C++20 standard. Make sure that your compiler supports it. These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. ### Prerequisites -**[Qt Creator](https://info.qt.io/download-qt-for-device-creation?hsCtaTracking=c80600ba-f2ea-45ed-97ef-6949c1c4c236%7C643bd8f4-2c59-4c4c-ba1a-4aaa05b51086)** — a cross-platform C++, JavaScript and QML integrated development environment which is part of the SDK for the Qt GUI Application development framework. - **[CMake](https://cmake.org/)** — an open-source, cross-platform family of tools designed to build, test and package software. ### Installing @@ -83,10 +81,10 @@ Input file should contain: * `` — defines the level of detalization of log-file. Default value is "1". Possible values: - "0" or "none" — log-file is not created. - - "0.5" or "tiny" — All the input data is copied to the log-file plus short `` is appended. `` contains info of the path length, number of steps, elapsed time, etc. - - "1" or "short" — 0.5 - log plus `` is appended. It looks like `` but cells forming the path are marked by "\*" instead of "0". The following tags are also appended: `` and ``. `` is the sequence of coordinates of cells forming the path. `` is the sequence of sections forming the path. - - "1.5" or "medium" — 1 - log plus the information (explicit enumeration) on last iteration's OPEN and CLOSE lists. - - "2" or "full" — 1 - log plus OPEN and CLOSE lists are written into the log-file after each step of the algorithm. Can make log-files really huge and also slow down the whole algorithm. + - "2" or "short" — All the input data is copied to the log-file plus short `` is appended. `` contains info of the path length, number of steps, elapsed time, etc. + - "3" or "high" — 0.5 - log plus `` is appended. It looks like `` but cells forming the path are marked by "\*" instead of "0". The following tags are also appended: `` and ``. `` is the sequence of coordinates of cells forming the path. `` is the sequence of sections forming the path. + - "4" or "medium" — 1 - log plus the information (explicit enumeration) on last iteration's OPEN and CLOSE lists. + - "5" or "full" — 1 - log plus OPEN and CLOSE lists are written into the log-file after each step of the algorithm. Can make log-files really huge and also slow down the whole algorithm. In order to understand differences between output files with different `` tags there are examples of each in [samples](https://github.com/PathPlanning/LIAN/tree/master/examples) folder. diff --git a/config.cpp b/config.cpp deleted file mode 100755 index a280a90..0000000 --- a/config.cpp +++ /dev/null @@ -1,299 +0,0 @@ -#include "config.h" - -Config::Config() : N(-1), searchParams(nullptr) {} - -Config::Config(int numParams, float *paramArray) { - N = numParams; - searchParams = new float[N]; - - for(int i = 0; i < N; ++i) { - searchParams[i] = paramArray[i]; - } -} - -Config::~Config() { - if (searchParams) { - delete[] searchParams; - } -} - -float Config::getParamValue(int i) const { - return searchParams[i]; -} - -bool Config::getConfig(const char* FileName) { - std::string value; - float angle; - int distance; - float weight; - unsigned int steplimit; - float loglevel; - float curvatureHeuriscitWeight; - double pivotRadius = 0; - float decreaseDistance; - int distanceMin; - int numOfParentsToIncreaseRadius; - bool postsmoother; - std::stringstream stream; - - TiXmlDocument doc(FileName); - if (!doc.LoadFile()) { - std::cout << "Error openning input XML file."<FirstChildElement(CNS_TAG_ALGORITHM); - if (!algorithm) { - std::cout << "No '" << CNS_TAG_ALGORITHM << "' element found in XML file." << std::endl; - return false; - } - - TiXmlElement *element; - - N = CN_PT_NUM; - searchParams = new float[N]; - - element = algorithm->FirstChildElement(CNS_TAG_ANGLELIMIT); - if (!element) { - std::cout << "Error! No '"<< CNS_TAG_ANGLELIMIT << "' element found inside '" << CNS_TAG_ALGORITHM - << "' section." << std::endl; - return false; - } else { - value = element->GetText(); - stream << value; - stream >> angle; - stream.clear(); - stream.str(""); - if (angle < 0) angle *= -1; - if (angle > 180) { - std::cout << "Warning! Trying to set angle limit to more than 180. " << - "Angle limit is set to 180 instead." << std::endl; - angle = 180; - } - } - if (angle == 0) { - std::cout << "Warning! '"<< CNS_TAG_ANGLELIMIT << "' is zero. Set to default value: " << CN_PTD_AL << "." << std::endl; - angle = CN_PTD_AL; - } - searchParams[CN_PT_AL] = angle; - - - element = algorithm->FirstChildElement(CNS_TAG_DISTANCE); - if (!element) { - std::cout << "Error! No '" << CNS_TAG_DISTANCE << "' element found inside '" << CNS_TAG_ALGORITHM - << "' section." << std::endl; - return false; - } else { - value = element->GetText(); - stream << value; - stream >> distance; - stream.clear(); - stream.str(""); - if (distance < 0) distance *= -1; - } - - if (distance == 0) { - std::cout << "Warning! Wrong '" << CNS_TAG_DISTANCE << "' element. Set to default value: " << CN_PTD_D - << "." << std::endl; - distance = CN_PTD_D; - } - searchParams[CN_PT_D] = distance; - - - element = algorithm->FirstChildElement(CNS_TAG_WEIGHT); - if (!element) { - std::cout << "Warning! No '" << CNS_TAG_WEIGHT << "' element found inside '" << CNS_TAG_ALGORITHM - << "' section. Set to default value: " << CN_PTD_W << "." << std::endl; - weight = CN_PTD_W; - } else { - value = element->GetText(); - stream << value; - stream >> weight; - stream.clear(); - stream.str(""); - } - - if (weight == 0) { - std::cout << "Warning! Wrong '" << CNS_TAG_WEIGHT << "' element. Set to default value: " << CN_PTD_W - << "." << std::endl; - weight = CN_PTD_W; - } - searchParams[CN_PT_W] = weight; - - /* element = algorithm->FirstChildElement(CNS_TAG_BREAKINGTIE); - if (!element) { - std::cout << "Warning! No '" << CNS_TAG_BREAKINGTIE << "' element found inside '" << CNS_TAG_ALGORITHM - << "' section. Set to default value: " << CNS_TAG_ATTR_GMAX <<"." << std::endl; - breakingties = CN_BT_GMAX; - } else { - value = element->GetText(); - if (value == CNS_TAG_ATTR_GMAX) { - breakingties = CN_BT_GMAX; - } else if (value == CNS_TAG_ATTR_GMIN) { - breakingties = CN_BT_GMIN; - } else { - std::cout << "Warning! Wrong '" << CNS_TAG_BREAKINGTIE << "' element. Set to default value: \"" - << CNS_TAG_ATTR_GMAX << "\"." << std::endl; - breakingties = CN_BT_GMAX; - } - } - searchParams[CN_PT_BT] = breakingties;*/ - - - element = algorithm->FirstChildElement(CNS_TAG_STEPLIMIT); - if (!element) { - std::cout << "Warning! No '" << CNS_TAG_STEPLIMIT << "' element found inside '" << CNS_TAG_ALGORITHM - << "' section. Set to default value: 0." << std::endl; - steplimit = 0; - } else { - value = element->GetText(); - if (value[0] == '-') steplimit = 0; - else { - stream << value; - stream >> steplimit; - stream.clear(); - stream.str(""); - } - } - searchParams[CN_PT_SL] = steplimit; - - element = algorithm->FirstChildElement(CNS_TAG_CURVHEURWEIGHT); - if (!element) { - std::cout << "Warning! No '" << CNS_TAG_CURVHEURWEIGHT << "' element found inside '" << CNS_TAG_ALGORITHM - << "' section. Set to default value: 0." << std::endl; - curvatureHeuriscitWeight = 0; - } else { - value = element->GetText(); - stream << value; - stream >> curvatureHeuriscitWeight; - stream.clear(); - stream.str(""); - } - searchParams[CN_PT_CHW] = curvatureHeuriscitWeight; - - element = algorithm->FirstChildElement(CNS_TAG_SMOOTHER); - if (!element) { - std::cout << "Warning! No '" << CNS_TAG_SMOOTHER << "' element found inside '" << CNS_TAG_ALGORITHM - << "' section. Set to default value: false." << std::endl; - postsmoother = 0; - } else { - std::string ps; - value = element->GetText(); - stream << value; - stream >> ps; - stream.clear(); - stream.str(""); - if (ps == "true" || ps == "True" || ps == "1") postsmoother = 1; - else if (ps == "false" || ps == "False" || ps == "0") postsmoother = 0; - else { - std::cout << "Warning! Wrong '" << CNS_TAG_SMOOTHER << "Set to default value: false." << std::endl; - postsmoother = 0; - } - } - searchParams[CN_PT_PS] = postsmoother; - - element = algorithm->FirstChildElement(CNS_TAG_DECRDISTFACTOR); - if (!element) { - std::cout << "Warning! No '" << CNS_TAG_DECRDISTFACTOR << "' element found inside '" << CNS_TAG_ALGORITHM - << "' section. Set to default value: " << CN_PTD_DDF << "." << std::endl; - decreaseDistance = CN_PTD_DDF; - } else { - value = element->GetText(); - stream << value; - stream >> decreaseDistance; - stream.clear(); - stream.str(""); - if(decreaseDistance == 0) { - std::cout << "Warning! Wrong '" << CNS_TAG_DECRDISTFACTOR << "Set to default value: " << CN_PTD_DDF - << "." << std::endl; - decreaseDistance = CN_PTD_DDF; - } - } - searchParams[CN_PT_DDF] = decreaseDistance; - - - element = algorithm->FirstChildElement(CNS_TAG_DISTANCEMIN); - if (!element) { - std::cout << "Warning! No '" << CNS_TAG_DISTANCEMIN << "' element found inside '" << CNS_TAG_ALGORITHM - << "' section. Set to default value: " << CN_PTD_DMIN << "." << std::endl; - distanceMin = CN_PTD_DMIN; - } else { - value = element->GetText(); - stream << value; - stream >> distanceMin; - stream.clear(); - stream.str(""); - if(distanceMin == 0) { - std::cout << "Warning! Wrong '" << CNS_TAG_DISTANCEMIN << "Set to default value: " << CN_PTD_DMIN - << "." << std::endl; - distanceMin = CN_PTD_DMIN; - } - } - searchParams[CN_PT_DM] = distanceMin; - - - element = algorithm->FirstChildElement(CNS_TAG_PIVOTCIRCLE); - if (!element) { - std::cout << "Warning! No '" << CNS_TAG_PIVOTCIRCLE << "' element found inside '" << CNS_TAG_ALGORITHM - << "' section. Set to default value: 0." << std::endl; - pivotRadius = 0; - } else { - value = element->GetText(); - stream << value; - stream >> pivotRadius; - stream.clear(); - stream.str(""); - if (pivotRadius < 0) { - pivotRadius *= -1; - } - } - searchParams[CN_PT_PC] = pivotRadius; - - - element = algorithm->FirstChildElement(CNS_TAG_NOFPTOINCRAD); - if (!element) { - std::cout << "Warning! No '" << CNS_TAG_NOFPTOINCRAD << "' element found inside '" << CNS_TAG_ALGORITHM - << "' section. Set to default value: " << CN_PTD_NOFPTOINCRAD << "." << std::endl; - numOfParentsToIncreaseRadius = CN_PTD_NOFPTOINCRAD; - } else { - value = element->GetText(); - stream << value; - stream >> numOfParentsToIncreaseRadius; - stream.clear(); - stream.str(""); - if(numOfParentsToIncreaseRadius==0) { - std::cout << "Warning! Wrong '" << CNS_TAG_NOFPTOINCRAD << "Set to default value: " << CN_PTD_NOFPTOINCRAD - << "." << std::endl; - numOfParentsToIncreaseRadius = CN_PTD_NOFPTOINCRAD; - } - } - searchParams[CN_PT_NOP] = numOfParentsToIncreaseRadius; - - TiXmlElement *options = root->FirstChildElement(CNS_TAG_OPTIONS); - if(!options) { - std::cout << "Error! No '" << CNS_TAG_OPTIONS << "' element found in XML file." << std::endl; - return false; - } - - element = options->FirstChildElement(CNS_TAG_LOGLVL); - if(!element) { - std::cout << "Error! No '"<< CNS_TAG_LOGLVL << "' element found in XML file." << std::endl; - return false; - } - - value = element->GetText(); - stream << value; - stream >> loglevel; - stream.clear(); - stream.str(""); - - searchParams[CN_PT_LOGLVL] = loglevel; - - return true; -} diff --git a/config.h b/config.h deleted file mode 100755 index 9e6188b..0000000 --- a/config.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef CONFIG_H -#define CONFIG_H - -#include "gl_const.h" -#include "tinyxml/tinyxml.h" -#include "tinyxml/tinystr.h" - -#include -#include -#include -#include - -class Config { - -private: - int N; - float *searchParams; - -public: - Config(); - Config(int numParams, float *paramArray); - ~Config(); - - float getParamValue(int i) const; - - bool getConfig(const char* FileName); -}; - -#endif diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt new file mode 100644 index 0000000..94adcf7 --- /dev/null +++ b/contrib/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(tinyxml) \ No newline at end of file diff --git a/contrib/tinyxml/CMakeLists.txt b/contrib/tinyxml/CMakeLists.txt new file mode 100644 index 0000000..9993c0c --- /dev/null +++ b/contrib/tinyxml/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(tinyxml + tinyxml.h + tinyxml.cpp + tinystr.h + tinystr.cpp + tinyxmlerror.cpp + tinyxmlparser.cpp +) \ No newline at end of file diff --git a/contrib/tinyxml/tinystr.cpp b/contrib/tinyxml/tinystr.cpp new file mode 100755 index 0000000..6812507 --- /dev/null +++ b/contrib/tinyxml/tinystr.cpp @@ -0,0 +1,116 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +/* + * THIS FILE WAS ALTERED BY Tyge Lшvset, 7. April 2005. + */ + + +#ifndef TIXML_USE_STL + +#include "tinystr.h" + +// Error value for find primitive +const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1); + + +// Null rep. +TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } }; + + +void TiXmlString::reserve (size_type cap) +{ + if (cap > capacity()) + { + TiXmlString tmp; + tmp.init(length(), cap); + memcpy(tmp.start(), data(), length()); + swap(tmp); + } +} + + +TiXmlString& TiXmlString::assign(const char* str, size_type len) +{ + size_type cap = capacity(); + if (len > cap || cap > 3*(len + 8)) + { + TiXmlString tmp; + tmp.init(len); + memcpy(tmp.start(), str, len); + swap(tmp); + } + else + { + memmove(start(), str, len); + set_size(len); + } + return *this; +} + + +TiXmlString& TiXmlString::append(const char* str, size_type len) +{ + size_type newsize = length() + len; + if (newsize > capacity()) + { + reserve (newsize + capacity()); + } + memmove(finish(), str, len); + set_size(newsize); + return *this; +} + + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b) +{ + TiXmlString tmp; + tmp.reserve(a.length() + b.length()); + tmp += a; + tmp += b; + return tmp; +} + +TiXmlString operator + (const TiXmlString & a, const char* b) +{ + TiXmlString tmp; + TiXmlString::size_type b_len = static_cast( strlen(b) ); + tmp.reserve(a.length() + b_len); + tmp += a; + tmp.append(b, b_len); + return tmp; +} + +TiXmlString operator + (const char* a, const TiXmlString & b) +{ + TiXmlString tmp; + TiXmlString::size_type a_len = static_cast( strlen(a) ); + tmp.reserve(a_len + b.length()); + tmp.append(a, a_len); + tmp += b; + return tmp; +} + + +#endif // TIXML_USE_STL diff --git a/contrib/tinyxml/tinystr.h b/contrib/tinyxml/tinystr.h new file mode 100755 index 0000000..3c2aa9d --- /dev/null +++ b/contrib/tinyxml/tinystr.h @@ -0,0 +1,319 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +/* + * THIS FILE WAS ALTERED BY Tyge Lovset, 7. April 2005. + * + * - completely rewritten. compact, clean, and fast implementation. + * - sizeof(TiXmlString) = pointer size (4 bytes on 32-bit systems) + * - fixed reserve() to work as per specification. + * - fixed buggy compares operator==(), operator<(), and operator>() + * - fixed operator+=() to take a const ref argument, following spec. + * - added "copy" constructor with length, and most compare operators. + * - added swap(), clear(), size(), capacity(), operator+(). + */ + +#ifndef TIXML_USE_STL + +#ifndef TIXML_STRING_INCLUDED +#define TIXML_STRING_INCLUDED + +#include +#include + +/* The support for explicit isn't that universal, and it isn't really + required - it is used to check that the TiXmlString class isn't incorrectly + used. Be nice to old compilers and macro it here: +*/ +#if defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + #define TIXML_EXPLICIT explicit +#elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + #define TIXML_EXPLICIT explicit +#else + #define TIXML_EXPLICIT +#endif + + +/* + TiXmlString is an emulation of a subset of the std::string template. + Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. + Only the member functions relevant to the TinyXML project have been implemented. + The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase + a string and there's no more room, we allocate a buffer twice as big as we need. +*/ +class TiXmlString +{ + public : + // The size type used + typedef size_t size_type; + + // Error value for find primitive + static const size_type npos; // = -1; + + + // TiXmlString empty constructor + TiXmlString () : rep_(&nullrep_) + { + } + + // TiXmlString copy constructor + TiXmlString ( const TiXmlString & copy) : rep_(0) + { + init(copy.length()); + memcpy(start(), copy.data(), length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0) + { + init( static_cast( strlen(copy) )); + memcpy(start(), copy, length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0) + { + init(len); + memcpy(start(), str, len); + } + + // TiXmlString destructor + ~TiXmlString () + { + quit(); + } + + // = operator + TiXmlString& operator = (const char * copy) + { + return assign( copy, (size_type)strlen(copy)); + } + + // = operator + TiXmlString& operator = (const TiXmlString & copy) + { + return assign(copy.start(), copy.length()); + } + + + // += operator. Maps to append + TiXmlString& operator += (const char * suffix) + { + return append(suffix, static_cast( strlen(suffix) )); + } + + // += operator. Maps to append + TiXmlString& operator += (char single) + { + return append(&single, 1); + } + + // += operator. Maps to append + TiXmlString& operator += (const TiXmlString & suffix) + { + return append(suffix.data(), suffix.length()); + } + + + // Convert a TiXmlString into a null-terminated char * + const char * c_str () const { return rep_->str; } + + // Convert a TiXmlString into a char * (need not be null terminated). + const char * data () const { return rep_->str; } + + // Return the length of a TiXmlString + size_type length () const { return rep_->size; } + + // Alias for length() + size_type size () const { return rep_->size; } + + // Checks if a TiXmlString is empty + bool empty () const { return rep_->size == 0; } + + // Return capacity of string + size_type capacity () const { return rep_->capacity; } + + + // single char extraction + const char& at (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // [] operator + char& operator [] (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // find a char in a string. Return TiXmlString::npos if not found + size_type find (char lookup) const + { + return find(lookup, 0); + } + + // find a char in a string from an offset. Return TiXmlString::npos if not found + size_type find (char tofind, size_type offset) const + { + if (offset >= length()) return npos; + + for (const char* p = c_str() + offset; *p != '\0'; ++p) + { + if (*p == tofind) return static_cast< size_type >( p - c_str() ); + } + return npos; + } + + void clear () + { + //Lee: + //The original was just too strange, though correct: + // TiXmlString().swap(*this); + //Instead use the quit & re-init: + quit(); + init(0,0); + } + + /* Function to reserve a big amount of data when we know we'll need it. Be aware that this + function DOES NOT clear the content of the TiXmlString if any exists. + */ + void reserve (size_type cap); + + TiXmlString& assign (const char* str, size_type len); + + TiXmlString& append (const char* str, size_type len); + + void swap (TiXmlString& other) + { + Rep* r = rep_; + rep_ = other.rep_; + other.rep_ = r; + } + + private: + + void init(size_type sz) { init(sz, sz); } + void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; } + char* start() const { return rep_->str; } + char* finish() const { return rep_->str + rep_->size; } + + struct Rep + { + size_type size, capacity; + char str[1]; + }; + + void init(size_type sz, size_type cap) + { + if (cap) + { + // Lee: the original form: + // rep_ = static_cast(operator new(sizeof(Rep) + cap)); + // doesn't work in some cases of new being overloaded. Switching + // to the normal allocation, although use an 'int' for systems + // that are overly picky about structure alignment. + const size_type bytesNeeded = sizeof(Rep) + cap; + const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); + rep_ = reinterpret_cast( new int[ intsNeeded ] ); + + rep_->str[ rep_->size = sz ] = '\0'; + rep_->capacity = cap; + } + else + { + rep_ = &nullrep_; + } + } + + void quit() + { + if (rep_ != &nullrep_) + { + // The rep_ is really an array of ints. (see the allocator, above). + // Cast it back before delete, so the compiler won't incorrectly call destructors. + delete [] ( reinterpret_cast( rep_ ) ); + } + } + + Rep * rep_; + static Rep nullrep_; + +} ; + + +inline bool operator == (const TiXmlString & a, const TiXmlString & b) +{ + return ( a.length() == b.length() ) // optimization on some platforms + && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare +} +inline bool operator < (const TiXmlString & a, const TiXmlString & b) +{ + return strcmp(a.c_str(), b.c_str()) < 0; +} + +inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); } +inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; } +inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); } +inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); } + +inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; } +inline bool operator == (const char* a, const TiXmlString & b) { return b == a; } +inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); } +inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); } + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b); +TiXmlString operator + (const TiXmlString & a, const char* b); +TiXmlString operator + (const char* a, const TiXmlString & b); + + +/* + TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. + Only the operators that we need for TinyXML have been developped. +*/ +class TiXmlOutStream : public TiXmlString +{ +public : + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const TiXmlString & in) + { + *this += in; + return *this; + } + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const char * in) + { + *this += in; + return *this; + } + +} ; + +#endif // TIXML_STRING_INCLUDED +#endif // TIXML_USE_STL diff --git a/contrib/tinyxml/tinyxml.cpp b/contrib/tinyxml/tinyxml.cpp new file mode 100755 index 0000000..b86adbc --- /dev/null +++ b/contrib/tinyxml/tinyxml.cpp @@ -0,0 +1,1888 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include + +#ifdef TIXML_USE_STL +#include +#include +#endif + +#include "tinyxml.h" + + +bool TiXmlBase::condenseWhiteSpace = true; + +// Microsoft compiler security +FILE* TiXmlFOpen( const char* filename, const char* mode ) +{ + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + FILE* fp = 0; + errno_t err = fopen_s( &fp, filename, mode ); + if ( !err && fp ) + return fp; + return 0; + #else + return fopen( filename, mode ); + #endif +} + +void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString ) +{ + int i=0; + + while( i<(int)str.length() ) + { + unsigned char c = (unsigned char) str[i]; + + if ( c == '&' + && i < ( (int)str.length() - 2 ) + && str[i+1] == '#' + && str[i+2] == 'x' ) + { + // Hexadecimal character reference. + // Pass through unchanged. + // © -- copyright symbol, for example. + // + // The -1 is a bug fix from Rob Laveaux. It keeps + // an overflow from happening if there is no ';'. + // There are actually 2 ways to exit this loop - + // while fails (error case) and break (semicolon found). + // However, there is no mechanism (currently) for + // this function to return an error. + while ( i<(int)str.length()-1 ) + { + outString->append( str.c_str() + i, 1 ); + ++i; + if ( str[i] == ';' ) + break; + } + } + else if ( c == '&' ) + { + outString->append( entity[0].str, entity[0].strLength ); + ++i; + } + else if ( c == '<' ) + { + outString->append( entity[1].str, entity[1].strLength ); + ++i; + } + else if ( c == '>' ) + { + outString->append( entity[2].str, entity[2].strLength ); + ++i; + } + else if ( c == '\"' ) + { + outString->append( entity[3].str, entity[3].strLength ); + ++i; + } + else if ( c == '\'' ) + { + outString->append( entity[4].str, entity[4].strLength ); + ++i; + } + else if ( c < 32 ) + { + // Easy pass at non-alpha/numeric/symbol + // Below 32 is symbolic. + char buf[ 32 ]; + + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); + #else + sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); + #endif + + //*ME: warning C4267: convert 'size_t' to 'int' + //*ME: Int-Cast to make compiler happy ... + outString->append( buf, (int)strlen( buf ) ); + ++i; + } + else + { + //char realc = (char) c; + //outString->append( &realc, 1 ); + *outString += (char) c; // somewhat more efficient function call. + ++i; + } + } +} + + +TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() +{ + parent = 0; + type = _type; + firstChild = 0; + lastChild = 0; + prev = 0; + next = 0; +} + + +TiXmlNode::~TiXmlNode() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } +} + + +void TiXmlNode::CopyTo( TiXmlNode* target ) const +{ + target->SetValue (value.c_str() ); + target->userData = userData; +} + + +void TiXmlNode::Clear() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } + + firstChild = 0; + lastChild = 0; +} + + +TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) +{ + assert( node->parent == 0 || node->parent == this ); + assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); + + if ( node->Type() == TiXmlNode::DOCUMENT ) + { + delete node; + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + node->parent = this; + + node->prev = lastChild; //здесь возникает ошибка при loglevel == 3 + node->next = 0; + + if ( lastChild ) + lastChild->next = node; + else + firstChild = node; // it was an empty list. + + lastChild = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) +{ + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + + return LinkEndChild( node ); +} + + +TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) +{ + if ( !beforeThis || beforeThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->next = beforeThis; + node->prev = beforeThis->prev; + if ( beforeThis->prev ) + { + beforeThis->prev->next = node; + } + else + { + assert( firstChild == beforeThis ); + firstChild = node; + } + beforeThis->prev = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) +{ + if ( !afterThis || afterThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->prev = afterThis; + node->next = afterThis->next; + if ( afterThis->next ) + { + afterThis->next->prev = node; + } + else + { + assert( lastChild == afterThis ); + lastChild = node; + } + afterThis->next = node; + return node; +} + + +TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) +{ + if ( replaceThis->parent != this ) + return 0; + + TiXmlNode* node = withThis.Clone(); + if ( !node ) + return 0; + + node->next = replaceThis->next; + node->prev = replaceThis->prev; + + if ( replaceThis->next ) + replaceThis->next->prev = node; + else + lastChild = node; + + if ( replaceThis->prev ) + replaceThis->prev->next = node; + else + firstChild = node; + + delete replaceThis; + node->parent = this; + return node; +} + + +bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) +{ + if ( removeThis->parent != this ) + { + assert( 0 ); + return false; + } + + if ( removeThis->next ) + removeThis->next->prev = removeThis->prev; + else + lastChild = removeThis->prev; + + if ( removeThis->prev ) + removeThis->prev->next = removeThis->next; + else + firstChild = removeThis->next; + + delete removeThis; + return true; +} + +const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + + +const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +void TiXmlElement::RemoveAttribute( const char * name ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING str( name ); + TiXmlAttribute* node = attributeSet.Find( str ); + #else + TiXmlAttribute* node = attributeSet.Find( name ); + #endif + if ( node ) + { + attributeSet.Remove( node ); + delete node; + } +} + +const TiXmlElement* TiXmlNode::FirstChildElement() const +{ + const TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement() const +{ + const TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlDocument* TiXmlNode::GetDocument() const +{ + const TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + + +TiXmlElement::TiXmlElement (const char * _value) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} + + +#ifdef TIXML_USE_STL +TiXmlElement::TiXmlElement( const std::string& _value ) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} +#endif + + +TiXmlElement::TiXmlElement( const TiXmlElement& copy) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + copy.CopyTo( this ); +} + + +void TiXmlElement::operator=( const TiXmlElement& base ) +{ + ClearThis(); + base.CopyTo( this ); +} + + +TiXmlElement::~TiXmlElement() +{ + ClearThis(); +} + + +void TiXmlElement::ClearThis() +{ + Clear(); + while( attributeSet.First() ) + { + TiXmlAttribute* node = attributeSet.First(); + attributeSet.Remove( node ); + delete node; + } +} + + +const char* TiXmlElement::Attribute( const char* name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return node->Value(); + return 0; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return &node->ValueStr(); + return 0; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, int* i ) const +{ + const char* s = Attribute( name ); + if ( i ) + { + if ( s ) { + *i = atoi( s ); + } + else { + *i = 0; + } + } + return s; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const +{ + const std::string* s = Attribute( name ); + if ( i ) + { + if ( s ) { + *i = atoi( s->c_str() ); + } + else { + *i = 0; + } + } + return s; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, double* d ) const +{ + const char* s = Attribute( name ); + if ( d ) + { + if ( s ) { + *d = atof( s ); + } + else { + *d = 0; + } + } + return s; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const +{ + const std::string* s = Attribute( name ); + if ( d ) + { + if ( s ) { + *d = atof( s->c_str() ); + } + else { + *d = 0; + } + } + return s; +} +#endif + + +int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryIntValue( ival ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryIntValue( ival ); +} +#endif + + +int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryDoubleValue( dval ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryDoubleValue( dval ); +} +#endif + + +void TiXmlElement::SetAttribute( const char * name, int val ) +{ + char buf[64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%d", val ); + #else + sprintf( buf, "%d", val ); + #endif + SetAttribute( name, buf ); +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, int val ) +{ + std::ostringstream oss; + oss << val; + SetAttribute( name, oss.str() ); +} +#endif + + +void TiXmlElement::SetDoubleAttribute( const char * name, double val ) +{ + char buf[256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%f", val ); + #else + sprintf( buf, "%f", val ); + #endif + SetAttribute( name, buf ); +} + + +void TiXmlElement::SetAttribute( const char * cname, const char * cvalue ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING _name( cname ); + TIXML_STRING _value( cvalue ); + #else + const char* _name = cname; + const char* _value = cvalue; + #endif + + TiXmlAttribute* node = attributeSet.Find( _name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( cname, cvalue ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, const std::string& _value ) +{ + TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( name, _value ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} +#endif + + +void TiXmlElement::Print( FILE* cfile, int depth ) const +{ + int i; + assert( cfile ); + for ( i=0; iNext() ) + { + fprintf( cfile, " " ); + attrib->Print( cfile, depth ); + } + + // There are 3 different formatting approaches: + // 1) An element without children is printed as a node + // 2) An element with only a text child is printed as text + // 3) An element with children is printed on multiple lines. + TiXmlNode* node; + if ( !firstChild ) + { + fprintf( cfile, " />" ); + } + else if ( firstChild == lastChild && firstChild->ToText() ) + { + fprintf( cfile, ">" ); + firstChild->Print( cfile, depth + 1 ); + fprintf( cfile, "", value.c_str() ); + } + else + { + fprintf( cfile, ">" ); + + for ( node = firstChild; node; node=node->NextSibling() ) + { + if ( !node->ToText() ) + { + fprintf( cfile, "\n" ); + } + node->Print( cfile, depth+1 ); + } + fprintf( cfile, "\n" ); + for( i=0; i", value.c_str() ); + } +} + + +void TiXmlElement::CopyTo( TiXmlElement* target ) const +{ + // superclass: + TiXmlNode::CopyTo( target ); + + // Element class: + // Clone the attributes, then clone the children. + const TiXmlAttribute* attribute = 0; + for( attribute = attributeSet.First(); + attribute; + attribute = attribute->Next() ) + { + target->SetAttribute( attribute->Name(), attribute->Value() ); + } + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + +bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this, attributeSet.First() ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +TiXmlNode* TiXmlElement::Clone() const +{ + TiXmlElement* clone = new TiXmlElement( Value() ); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +const char* TiXmlElement::GetText() const +{ + const TiXmlNode* child = this->FirstChild(); + if ( child ) { + const TiXmlText* childText = child->ToText(); + if ( childText ) { + return childText->Value(); + } + } + return 0; +} + + +TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + ClearError(); +} + +TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} + + +#ifdef TIXML_USE_STL +TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} +#endif + + +TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDocument::operator=( const TiXmlDocument& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) +{ + // See STL_STRING_BUG below. + //StringToBuffer buf( value ); + + return LoadFile( Value(), encoding ); +} + + +bool TiXmlDocument::SaveFile() const +{ + // See STL_STRING_BUG below. +// StringToBuffer buf( value ); +// +// if ( buf.buffer && SaveFile( buf.buffer ) ) +// return true; +// +// return false; + return SaveFile( Value() ); +} + +bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) +{ + // There was a really terrifying little bug here. The code: + // value = filename + // in the STL case, cause the assignment method of the std::string to + // be called. What is strange, is that the std::string had the same + // address as it's c_str() method, and so bad things happen. Looks + // like a bug in the Microsoft STL implementation. + // Add an extra string to avoid the crash. + TIXML_STRING filename( _filename ); + value = filename; + + // reading in binary mode so that tinyxml can normalize the EOL + FILE* file = TiXmlFOpen( value.c_str (), "rb" ); + + if ( file ) + { + bool result = LoadFile( file, encoding ); + fclose( file ); + return result; + } + else + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } +} + +bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) +{ + if ( !file ) + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Delete the existing data: + Clear(); + location.Clear(); + + // Get the file size, so we can pre-allocate the string. HUGE speed impact. + long length = 0; + fseek( file, 0, SEEK_END ); + length = ftell( file ); + fseek( file, 0, SEEK_SET ); + + // Strange case, but good to handle up front. + if ( length <= 0 ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // If we have a file, assume it is all one big XML file, and read it in. + // The document parser may decide the document ends sooner than the entire file, however. + TIXML_STRING data; + data.reserve( length ); + + // Subtle bug here. TinyXml did use fgets. But from the XML spec: + // 2.11 End-of-Line Handling + // + // + // ...the XML processor MUST behave as if it normalized all line breaks in external + // parsed entities (including the document entity) on input, before parsing, by translating + // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to + // a single #xA character. + // + // + // It is not clear fgets does that, and certainly isn't clear it works cross platform. + // Generally, you expect fgets to translate from the convention of the OS to the c/unix + // convention, and not work generally. + + /* + while( fgets( buf, sizeof(buf), file ) ) + { + data += buf; + } + */ + + char* buf = new char[ length+1 ]; + buf[0] = 0; + + if ( fread( buf, length, 1, file ) != 1 ) { + delete [] buf; + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + const char* lastPos = buf; + const char* p = buf; + + buf[length] = 0; + while( *p ) { + assert( p < (buf+length) ); + if ( *p == 0xa ) { + // Newline character. No special rules for this. Append all the characters + // since the last string, and include the newline. + data.append( lastPos, (p-lastPos+1) ); // append, include the newline + ++p; // move past the newline + lastPos = p; // and point to the new buffer (may be 0) + assert( p <= (buf+length) ); + } + else if ( *p == 0xd ) { + // Carriage return. Append what we have so far, then + // handle moving forward in the buffer. + if ( (p-lastPos) > 0 ) { + data.append( lastPos, p-lastPos ); // do not add the CR + } + data += (char)0xa; // a proper newline + + if ( *(p+1) == 0xa ) { + // Carriage return - new line sequence + p += 2; + lastPos = p; + assert( p <= (buf+length) ); + } + else { + // it was followed by something else...that is presumably characters again. + ++p; + lastPos = p; + assert( p <= (buf+length) ); + } + } + else { + ++p; + } + } + // Handle any left over characters. + if ( p-lastPos ) { + data.append( lastPos, p-lastPos ); + } + delete [] buf; + buf = 0; + + Parse( data.c_str(), 0, encoding ); + + if ( Error() ) + return false; + else + return true; +} + + +bool TiXmlDocument::SaveFile( const char * filename ) const +{ + // The old c stuff lives on... + FILE* fp = TiXmlFOpen( filename, "w" ); + if ( fp ) + { + bool result = SaveFile( fp ); + fclose( fp ); + return result; + } + return false; +} + + +bool TiXmlDocument::SaveFile( FILE* fp ) const +{ + if ( useMicrosoftBOM ) + { + const unsigned char TIXML_UTF_LEAD_0 = 0xefU; + const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; + const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + + fputc( TIXML_UTF_LEAD_0, fp ); + fputc( TIXML_UTF_LEAD_1, fp ); + fputc( TIXML_UTF_LEAD_2, fp ); + } + Print( fp, 0 ); + return (ferror(fp) == 0); +} + + +void TiXmlDocument::CopyTo( TiXmlDocument* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->error = error; + target->errorId = errorId; + target->errorDesc = errorDesc; + target->tabsize = tabsize; + target->errorLocation = errorLocation; + target->useMicrosoftBOM = useMicrosoftBOM; + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlDocument::Clone() const +{ + TiXmlDocument* clone = new TiXmlDocument(); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlDocument::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + node->Print( cfile, depth ); + fprintf( cfile, "\n" ); + } +} + + +bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +const TiXmlAttribute* TiXmlAttribute::Next() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + +/* +TiXmlAttribute* TiXmlAttribute::Next() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} +*/ + +const TiXmlAttribute* TiXmlAttribute::Previous() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + +/* +TiXmlAttribute* TiXmlAttribute::Previous() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} +*/ + +void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + TIXML_STRING n, v; + + EncodeString( name, &n ); + EncodeString( value, &v ); + + if (value.find ('\"') == TIXML_STRING::npos) { + if ( cfile ) { + fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; + } + } + else { + if ( cfile ) { + fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; + } + } +} + + +int TiXmlAttribute::QueryIntValue( int* ival ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +int TiXmlAttribute::QueryDoubleValue( double* dval ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +void TiXmlAttribute::SetIntValue( int _value ) +{ + char buf [64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); + #else + sprintf (buf, "%d", _value); + #endif + SetValue (buf); +} + +void TiXmlAttribute::SetDoubleValue( double _value ) +{ + char buf [256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%lf", _value); + #else + sprintf (buf, "%lf", _value); + #endif + SetValue (buf); +} + +int TiXmlAttribute::IntValue() const +{ + return atoi (value.c_str ()); +} + +double TiXmlAttribute::DoubleValue() const +{ + return atof (value.c_str ()); +} + + +TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::COMMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlComment::operator=( const TiXmlComment& base ) +{ + Clear(); + base.CopyTo( this ); +} + + +void TiXmlComment::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlComment::CopyTo( TiXmlComment* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlComment::Clone() const +{ + TiXmlComment* clone = new TiXmlComment(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlText::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + if ( cdata ) + { + int i; + fprintf( cfile, "\n" ); + for ( i=0; i\n", value.c_str() ); // unformatted output + } + else + { + TIXML_STRING buffer; + EncodeString( value, &buffer ); + fprintf( cfile, "%s", buffer.c_str() ); + } +} + + +void TiXmlText::CopyTo( TiXmlText* target ) const +{ + TiXmlNode::CopyTo( target ); + target->cdata = cdata; +} + + +bool TiXmlText::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlText::Clone() const +{ + TiXmlText* clone = 0; + clone = new TiXmlText( "" ); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDeclaration::TiXmlDeclaration( const char * _version, + const char * _encoding, + const char * _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} + + +#ifdef TIXML_USE_STL +TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} +#endif + + +TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + if ( cfile ) fprintf( cfile, "" ); + if ( str ) (*str) += "?>"; +} + + +void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->version = version; + target->encoding = encoding; + target->standalone = standalone; +} + + +bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlDeclaration::Clone() const +{ + TiXmlDeclaration* clone = new TiXmlDeclaration(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlUnknown::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlUnknown::Clone() const +{ + TiXmlUnknown* clone = new TiXmlUnknown(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlAttributeSet::TiXmlAttributeSet() +{ + sentinel.next = &sentinel; + sentinel.prev = &sentinel; +} + + +TiXmlAttributeSet::~TiXmlAttributeSet() +{ + assert( sentinel.next == &sentinel ); + assert( sentinel.prev == &sentinel ); +} + + +void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) +{ + #ifdef TIXML_USE_STL + assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. + #else + assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. + #endif + + addMe->next = &sentinel; + addMe->prev = sentinel.prev; + + sentinel.prev->next = addMe; + sentinel.prev = addMe; +} + +void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node == removeMe ) + { + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = 0; + node->prev = 0; + return; + } + } + assert( 0 ); // we tried to remove a non-linked attribute. +} + + +#ifdef TIXML_USE_STL +const TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const +{ + for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + +/* +TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} +*/ +#endif + + +const TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const +{ + for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} + +/* +TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} +*/ + +#ifdef TIXML_USE_STL +std::istream& operator>> (std::istream & in, TiXmlNode & base) +{ + TIXML_STRING tag; + tag.reserve( 8 * 1000 ); + base.StreamIn( &in, &tag ); + + base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); + return in; +} +#endif + + +#ifdef TIXML_USE_STL +std::ostream& operator<< (std::ostream & out, const TiXmlNode & base) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out << printer.Str(); + + return out; +} + + +std::string& operator<< (std::string& out, const TiXmlNode& base ) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out.append( printer.Str() ); + + return out; +} +#endif + + +TiXmlHandle TiXmlHandle::FirstChild() const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement() const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild(); + for ( i=0; + child && iNextSibling(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild( value ); + for ( i=0; + child && iNextSibling( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement(); + for ( i=0; + child && iNextSiblingElement(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement( value ); + for ( i=0; + child && iNextSiblingElement( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +bool TiXmlPrinter::VisitEnter( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitExit( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + DoIndent(); + buffer += "<"; + buffer += element.Value(); + + for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() ) + { + buffer += " "; + attrib->Print( 0, 0, &buffer ); + } + + if ( !element.FirstChild() ) + { + buffer += " />"; + DoLineBreak(); + } + else + { + buffer += ">"; + if ( element.FirstChild()->ToText() + && element.LastChild() == element.FirstChild() + && element.FirstChild()->ToText()->CDATA() == false ) + { + simpleTextPrint = true; + // no DoLineBreak()! + } + else + { + DoLineBreak(); + } + } + ++depth; + return true; +} + + +bool TiXmlPrinter::VisitExit( const TiXmlElement& element ) +{ + --depth; + if ( !element.FirstChild() ) + { + // nothing. + } + else + { + if ( simpleTextPrint ) + { + simpleTextPrint = false; + } + else + { + DoIndent(); + } + buffer += ""; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlText& text ) +{ + if ( text.CDATA() ) + { + DoIndent(); + buffer += ""; + DoLineBreak(); + } + else if ( simpleTextPrint ) + { + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + } + else + { + DoIndent(); + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration ) +{ + DoIndent(); + declaration.Print( 0, 0, &buffer ); + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlComment& comment ) +{ + DoIndent(); + buffer += ""; + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown ) +{ + DoIndent(); + buffer += "<"; + buffer += unknown.Value(); + buffer += ">"; + DoLineBreak(); + return true; +} + diff --git a/contrib/tinyxml/tinyxml.h b/contrib/tinyxml/tinyxml.h new file mode 100755 index 0000000..c6f40cc --- /dev/null +++ b/contrib/tinyxml/tinyxml.h @@ -0,0 +1,1802 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TINYXML_INCLUDED +#define TINYXML_INCLUDED + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4786 ) +#endif + +#include +#include +#include +#include +#include + +// Help out windows: +#if defined( _DEBUG ) && !defined( DEBUG ) +#define DEBUG +#endif + +#ifdef TIXML_USE_STL + #include + #include + #include + #define TIXML_STRING std::string +#else + #include "tinystr.h" + #define TIXML_STRING TiXmlString +#endif + +// Deprecated library function hell. Compilers want to use the +// new safe versions. This probably doesn't fully address the problem, +// but it gets closer. There are too many compilers for me to fully +// test. If you get compilation troubles, undefine TIXML_SAFE +#define TIXML_SAFE + +#ifdef TIXML_SAFE + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + // Microsoft visual studio, version 2005 and higher. + #define TIXML_SNPRINTF _snprintf_s + #define TIXML_SNSCANF _snscanf_s + #define TIXML_SSCANF sscanf_s + #elif defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + //#pragma message( "Using _sn* functions." ) + #define TIXML_SNPRINTF _snprintf + #define TIXML_SNSCANF _snscanf + #define TIXML_SSCANF sscanf + #elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_SNSCANF snscanf + #define TIXML_SSCANF sscanf + #else + #define TIXML_SSCANF sscanf + #endif +#endif + +class TiXmlDocument; +class TiXmlElement; +class TiXmlComment; +class TiXmlUnknown; +class TiXmlAttribute; +class TiXmlText; +class TiXmlDeclaration; +class TiXmlParsingData; + +const int TIXML_MAJOR_VERSION = 2; +const int TIXML_MINOR_VERSION = 5; +const int TIXML_PATCH_VERSION = 3; + +/* Internal structure for tracking location of items + in the XML file. +*/ +struct TiXmlCursor +{ + TiXmlCursor() { Clear(); } + void Clear() { row = col = -1; } + + int row; // 0 based. + int col; // 0 based. +}; + + +/** + If you call the Accept() method, it requires being passed a TiXmlVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves + are simple called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, no children of this node or its sibilings will be Visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting. + + You should never change the document from a callback. + + @sa TiXmlNode::Accept() +*/ +class TiXmlVisitor +{ +public: + virtual ~TiXmlVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; } + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; } + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; } + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; } + + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; } + /// Visit a text node + virtual bool Visit( const TiXmlText& /*text*/ ) { return true; } + /// Visit a comment node + virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; } + /// Visit an unknow node + virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; } +}; + +// Only used by Attribute::Query functions +enum +{ + TIXML_SUCCESS, + TIXML_NO_ATTRIBUTE, + TIXML_WRONG_TYPE +}; + + +// Used by the parsing routines. +enum TiXmlEncoding +{ + TIXML_ENCODING_UNKNOWN, + TIXML_ENCODING_UTF8, + TIXML_ENCODING_LEGACY +}; + +const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; + +/** TiXmlBase is a base class for every class in TinyXml. + It does little except to establish that TinyXml classes + can be printed and provide some utility functions. + + In XML, the document and elements can contain + other elements and other types of nodes. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + A Decleration contains: Attributes (not on tree) + @endverbatim +*/ +class TiXmlBase +{ + friend class TiXmlNode; + friend class TiXmlElement; + friend class TiXmlDocument; + +public: + TiXmlBase() : userData(0) {} + virtual ~TiXmlBase() {} + + /** All TinyXml classes can print themselves to a filestream + or the string class (TiXmlString in non-STL mode, std::string + in STL mode.) Either or both cfile and str can be null. + + This is a formatted print, and will insert + tabs and newlines. + + (For an unformatted stream, use the << operator.) + */ + virtual void Print( FILE* cfile, int depth ) const = 0; + + /** The world does not agree on whether white space should be kept or + not. In order to make everyone happy, these global, static functions + are provided to set whether or not TinyXml will condense all white space + into a single space or not. The default is to condense. Note changing this + value is not thread safe. + */ + static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } + + /// Return the current white space setting. + static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } + + /** Return the position, in the original source file, of this node or attribute. + The row and column are 1-based. (That is the first row and first column is + 1,1). If the returns values are 0 or less, then the parser does not have + a row and column value. + + Generally, the row and column value will be set when the TiXmlDocument::Load(), + TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set + when the DOM was created from operator>>. + + The values reflect the initial load. Once the DOM is modified programmatically + (by adding or changing nodes and attributes) the new values will NOT update to + reflect changes in the document. + + There is a minor performance cost to computing the row and column. Computation + can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. + + @sa TiXmlDocument::SetTabSize() + */ + int Row() const { return location.row + 1; } + int Column() const { return location.col + 1; } ///< See Row() + + void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data. + void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data. + const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data. + + // Table that returs, for a given lead byte, the total number of bytes + // in the UTF-8 sequence. + static const int utf8ByteTable[256]; + + virtual const char* Parse( const char* p, + TiXmlParsingData* data, + TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; + + /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc, + or they will be transformed into entities! + */ + static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out ); + + enum + { + TIXML_NO_ERROR = 0, + TIXML_ERROR, + TIXML_ERROR_OPENING_FILE, + TIXML_ERROR_OUT_OF_MEMORY, + TIXML_ERROR_PARSING_ELEMENT, + TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, + TIXML_ERROR_READING_ELEMENT_VALUE, + TIXML_ERROR_READING_ATTRIBUTES, + TIXML_ERROR_PARSING_EMPTY, + TIXML_ERROR_READING_END_TAG, + TIXML_ERROR_PARSING_UNKNOWN, + TIXML_ERROR_PARSING_COMMENT, + TIXML_ERROR_PARSING_DECLARATION, + TIXML_ERROR_DOCUMENT_EMPTY, + TIXML_ERROR_EMBEDDED_NULL, + TIXML_ERROR_PARSING_CDATA, + TIXML_ERROR_DOCUMENT_TOP_ONLY, + + TIXML_ERROR_STRING_COUNT + }; + +protected: + + static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); + inline static bool IsWhiteSpace( char c ) + { + return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); + } + inline static bool IsWhiteSpace( int c ) + { + if ( c < 256 ) + return IsWhiteSpace( (char) c ); + return false; // Again, only truly correct for English/Latin...but usually works. + } + + #ifdef TIXML_USE_STL + static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ); + static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag ); + #endif + + /* Reads an XML name into the string provided. Returns + a pointer just past the last character of the name, + or 0 if the function has an error. + */ + static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); + + /* Reads text. Returns a pointer past the given end tag. + Wickedly complex options, but it keeps the (sensitive) code in one place. + */ + static const char* ReadText( const char* in, // where to start + TIXML_STRING* text, // the string read + bool ignoreWhiteSpace, // whether to keep the white space + const char* endTag, // what ends this text + bool ignoreCase, // whether to ignore case in the end tag + TiXmlEncoding encoding ); // the current encoding + + // If an entity has been found, transform it into a character. + static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); + + // Get a character, while interpreting entities. + // The length can be from 0 to 4 bytes. + inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) + { + assert( p ); + if ( encoding == TIXML_ENCODING_UTF8 ) + { + *length = utf8ByteTable[ *((const unsigned char*)p) ]; + assert( *length >= 0 && *length < 5 ); + } + else + { + *length = 1; + } + + if ( *length == 1 ) + { + if ( *p == '&' ) + return GetEntity( p, _value, length, encoding ); + *_value = *p; + return p+1; + } + else if ( *length ) + { + //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), + // and the null terminator isn't needed + for( int i=0; p[i] && i<*length; ++i ) { + _value[i] = p[i]; + } + return p + (*length); + } + else + { + // Not valid text. + return 0; + } + } + + // Return true if the next characters in the stream are any of the endTag sequences. + // Ignore case only works for english, and should only be relied on when comparing + // to English words: StringEqual( p, "version", true ) is fine. + static bool StringEqual( const char* p, + const char* endTag, + bool ignoreCase, + TiXmlEncoding encoding ); + + static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; + + TiXmlCursor location; + + /// Field containing a generic user pointer + void* userData; + + // None of these methods are reliable for any language except English. + // Good for approximation, not great for accuracy. + static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); + static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); + inline static int ToLower( int v, TiXmlEncoding encoding ) + { + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( v < 128 ) return tolower( v ); + return v; + } + else + { + return tolower( v ); + } + } + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + +private: + TiXmlBase( const TiXmlBase& ); // not implemented. + void operator=( const TiXmlBase& base ); // not allowed. + + struct Entity + { + const char* str; + unsigned int strLength; + char chr; + }; + enum + { + NUM_ENTITY = 5, + MAX_ENTITY_LENGTH = 6 + + }; + static Entity entity[ NUM_ENTITY ]; + static bool condenseWhiteSpace; +}; + + +/** The parent class for everything in the Document Object Model. + (Except for attributes). + Nodes have siblings, a parent, and children. A node can be + in a document, or stand on its own. The type of a TiXmlNode + can be queried, and it can be cast to its more defined type. +*/ +class TiXmlNode : public TiXmlBase +{ + friend class TiXmlDocument; + friend class TiXmlElement; + +public: + #ifdef TIXML_USE_STL + + /** An input stream operator, for every class. Tolerant of newlines and + formatting, but doesn't expect them. + */ + friend std::istream& operator >> (std::istream& in, TiXmlNode& base); + + /** An output stream operator, for every class. Note that this outputs + without any newlines or formatting, as opposed to Print(), which + includes tabs and new lines. + + The operator<< and operator>> are not completely symmetric. Writing + a node to a stream is very well defined. You'll get a nice stream + of output, without any extra whitespace or newlines. + + But reading is not as well defined. (As it always is.) If you create + a TiXmlElement (for example) and read that from an input stream, + the text needs to define an element or junk will result. This is + true of all input streams, but it's worth keeping in mind. + + A TiXmlDocument will read nodes until it reads a root element, and + all the children of that root element. + */ + friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); + + /// Appends the XML node or attribute to a std::string. + friend std::string& operator<< (std::string& out, const TiXmlNode& base ); + + #endif + + /** The types of XML nodes supported by TinyXml. (All the + unsupported types are picked up by UNKNOWN.) + */ + enum NodeType + { + DOCUMENT, + ELEMENT, + COMMENT, + UNKNOWN, + TEXT, + DECLARATION, + TYPECOUNT + }; + + virtual ~TiXmlNode(); + + /** The meaning of 'value' changes for the specific type of + TiXmlNode. + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + + The subclasses will wrap this function. + */ + const char *Value() const { return value.c_str (); } + + #ifdef TIXML_USE_STL + /** Return Value() as a std::string. If you only use STL, + this is more efficient than calling Value(). + Only available in STL mode. + */ + const std::string& ValueStr() const { return value; } + #endif + + const TIXML_STRING& ValueTStr() const { return value; } + + /** Changes the value of the node. Defined as: + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + void SetValue(const char * _value) { value = _value;} + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Delete all the children of this node. Does not affect 'this'. + void Clear(); + + /// One step up the DOM. + TiXmlNode* Parent() { return parent; } + const TiXmlNode* Parent() const { return parent; } + + const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. + TiXmlNode* FirstChild() { return firstChild; } + const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. + /// The first child of this node with the matching 'value'. Will be null if none found. + TiXmlNode* FirstChild( const char * _value ) { + // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe) + // call the method, cast the return back to non-const. + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value )); + } + const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. + TiXmlNode* LastChild() { return lastChild; } + + const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. + TiXmlNode* LastChild( const char * _value ) { + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value )); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. + #endif + + /** An alternate way to walk the children of a node. + One way to iterate over nodes is: + @verbatim + for( child = parent->FirstChild(); child; child = child->NextSibling() ) + @endverbatim + + IterateChildren does the same thing with the syntax: + @verbatim + child = 0; + while( child = parent->IterateChildren( child ) ) + @endverbatim + + IterateChildren takes the previous child as input and finds + the next one. If the previous child is null, it returns the + first. IterateChildren will return null when done. + */ + const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) ); + } + + /// This flavor of IterateChildren searches for children with a particular 'value' + const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + #endif + + /** Add a new node related to this. Adds a child past the LastChild. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); + + + /** Add a new node related to this. Adds a child past the LastChild. + + NOTE: the node to be added is passed by pointer, and will be + henceforth owned (and deleted) by tinyXml. This method is efficient + and avoids an extra copy, but should be used with care as it + uses a different memory model than the other insert functions. + + @sa InsertEndChild + */ + TiXmlNode* LinkEndChild( TiXmlNode* addThis ); + + /** Add a new node related to this. Adds a child before the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); + + /** Add a new node related to this. Adds a child after the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); + + /** Replace a child of this node. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); + + /// Delete a child of this node. + bool RemoveChild( TiXmlNode* removeThis ); + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling() const { return prev; } + TiXmlNode* PreviousSibling() { return prev; } + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling( const char * ) const; + TiXmlNode* PreviousSibling( const char *_prev ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Navigate to a sibling node. + const TiXmlNode* NextSibling() const { return next; } + TiXmlNode* NextSibling() { return next; } + + /// Navigate to a sibling node with the given 'value'. + const TiXmlNode* NextSibling( const char * ) const; + TiXmlNode* NextSibling( const char* _next ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement() const; + TiXmlElement* NextSiblingElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement( const char * ) const; + TiXmlElement* NextSiblingElement( const char *_next ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement() const; + TiXmlElement* FirstChildElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() ); + } + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement( const char * _value ) const; + TiXmlElement* FirstChildElement( const char * _value ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /** Query the type (as an enumerated value, above) of this node. + The possible types are: DOCUMENT, ELEMENT, COMMENT, + UNKNOWN, TEXT, and DECLARATION. + */ + int Type() const { return type; } + + /** Return a pointer to the Document this node lives in. + Returns null if not in a document. + */ + const TiXmlDocument* GetDocument() const; + TiXmlDocument* GetDocument() { + return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() ); + } + + /// Returns true if this node has no children. + bool NoChildren() const { return !firstChild; } + + virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + /** Create an exact duplicate of this node and return it. The memory must be deleted + by the caller. + */ + virtual TiXmlNode* Clone() const = 0; + + /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the TiXmlVisitor interface. + + This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + TiXmlPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( TiXmlVisitor* visitor ) const = 0; + +protected: + TiXmlNode( NodeType _type ); + + // Copy to the allocated object. Shared functionality between Clone, Copy constructor, + // and the assignment operator. + void CopyTo( TiXmlNode* target ) const; + + #ifdef TIXML_USE_STL + // The real work of the input operator. + virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0; + #endif + + // Figure out what is at *p, and parse it. Returns null if it is not an xml node. + TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); + + TiXmlNode* parent; + NodeType type; + + TiXmlNode* firstChild; + TiXmlNode* lastChild; + + TIXML_STRING value; + + TiXmlNode* prev; + TiXmlNode* next; + +private: + TiXmlNode( const TiXmlNode& ); // not implemented. + void operator=( const TiXmlNode& base ); // not allowed. +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not TiXmlNodes, since they are not + part of the tinyXML document object model. There are other + suggested ways to look at this problem. +*/ +class TiXmlAttribute : public TiXmlBase +{ + friend class TiXmlAttributeSet; + +public: + /// Construct an empty attribute. + TiXmlAttribute() : TiXmlBase() + { + document = 0; + prev = next = 0; + } + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlAttribute( const std::string& _name, const std::string& _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + #endif + + /// Construct an attribute with a name and value. + TiXmlAttribute( const char * _name, const char * _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + + const char* Name() const { return name.c_str(); } ///< Return the name of this attribute. + const char* Value() const { return value.c_str(); } ///< Return the value of this attribute. + #ifdef TIXML_USE_STL + const std::string& ValueStr() const { return value; } ///< Return the value of this attribute. + #endif + int IntValue() const; ///< Return the value of this attribute, converted to an integer. + double DoubleValue() const; ///< Return the value of this attribute, converted to a double. + + // Get the tinyxml string representation + const TIXML_STRING& NameTStr() const { return name; } + + /** QueryIntValue examines the value string. It is an alternative to the + IntValue() method with richer error checking. + If the value is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. + + A specialized but useful call. Note that for success it returns 0, + which is the opposite of almost all other TinyXml calls. + */ + int QueryIntValue( int* _value ) const; + /// QueryDoubleValue examines the value string. See QueryIntValue(). + int QueryDoubleValue( double* _value ) const; + + void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. + void SetValue( const char* _value ) { value = _value; } ///< Set the value. + + void SetIntValue( int _value ); ///< Set the value from an integer. + void SetDoubleValue( double _value ); ///< Set the value from a double. + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetName( const std::string& _name ) { name = _name; } + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Get the next sibling attribute in the DOM. Returns null at end. + const TiXmlAttribute* Next() const; + TiXmlAttribute* Next() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); + } + + /// Get the previous sibling attribute in the DOM. Returns null at beginning. + const TiXmlAttribute* Previous() const; + TiXmlAttribute* Previous() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); + } + + bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } + bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } + bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } + + /* Attribute parsing starts: first letter of the name + returns: the next char after the value end quote + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + // Prints this Attribute to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + + // [internal use] + // Set the document pointer so the attribute can report errors. + void SetDocument( TiXmlDocument* doc ) { document = doc; } + +private: + TiXmlAttribute( const TiXmlAttribute& ); // not implemented. + void operator=( const TiXmlAttribute& base ); // not allowed. + + TiXmlDocument* document; // A pointer back to a document, for error reporting. + TIXML_STRING name; + TIXML_STRING value; + TiXmlAttribute* prev; + TiXmlAttribute* next; +}; + + +/* A class used to manage a group of attributes. + It is only used internally, both by the ELEMENT and the DECLARATION. + + The set can be changed transparent to the Element and Declaration + classes that use it, but NOT transparent to the Attribute + which has to implement a next() and previous() method. Which makes + it a bit problematic and prevents the use of STL. + + This version is implemented with circular lists because: + - I like circular lists + - it demonstrates some independence from the (typical) doubly linked list. +*/ +class TiXmlAttributeSet +{ +public: + TiXmlAttributeSet(); + ~TiXmlAttributeSet(); + + void Add( TiXmlAttribute* attribute ); + void Remove( TiXmlAttribute* attribute ); + + const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + + const TiXmlAttribute* Find( const char* _name ) const; + TiXmlAttribute* Find( const char* _name ) { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) ); + } + #ifdef TIXML_USE_STL + const TiXmlAttribute* Find( const std::string& _name ) const; + TiXmlAttribute* Find( const std::string& _name ) { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) ); + } + + #endif + +private: + //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), + //*ME: this class must be also use a hidden/disabled copy-constructor !!! + TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed + void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) + + TiXmlAttribute sentinel; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TiXmlElement : public TiXmlNode +{ +public: + /// Construct an element. + TiXmlElement (const char * in_value); + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlElement( const std::string& _value ); + #endif + + TiXmlElement( const TiXmlElement& ); + + void operator=( const TiXmlElement& base ); + + virtual ~TiXmlElement(); + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + */ + const char* Attribute( const char* name ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an integer, + the integer value will be put in the return 'i', if 'i' + is non-null. + */ + const char* Attribute( const char* name, int* i ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an double, + the double value will be put in the return 'd', if 'd' + is non-null. + */ + const char* Attribute( const char* name, double* d ) const; + + /** QueryIntAttribute examines the attribute - it is an alternative to the + Attribute() method with richer error checking. + If the attribute is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. If the attribute + does not exist, then TIXML_NO_ATTRIBUTE is returned. + */ + int QueryIntAttribute( const char* name, int* _value ) const; + /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). + int QueryDoubleAttribute( const char* name, double* _value ) const; + /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). + int QueryFloatAttribute( const char* name, float* _value ) const { + double d; + int result = QueryDoubleAttribute( name, &d ); + if ( result == TIXML_SUCCESS ) { + *_value = (float)d; + } + return result; + } + + #ifdef TIXML_USE_STL + /** Template form of the attribute query which will try to read the + attribute into the specified type. Very easy, very powerful, but + be careful to make sure to call this with the correct type. + + NOTE: This method doesn't work correctly for 'string' types. + + @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE + */ + template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + std::stringstream sstream( node->ValueStr() ); + sstream >> *outValue; + if ( !sstream.fail() ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; + } + /* + This is - in theory - a bug fix for "QueryValueAtribute returns truncated std::string" + but template specialization is hard to get working cross-compiler. Leaving the bug for now. + + // The above will fail for std::string because the space character is used as a seperator. + // Specialize for strings. Bug [ 1695429 ] QueryValueAtribute returns truncated std::string + template<> int QueryValueAttribute( const std::string& name, std::string* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + *outValue = node->ValueStr(); + return TIXML_SUCCESS; + } + */ + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char* name, const char * _value ); + + #ifdef TIXML_USE_STL + const std::string* Attribute( const std::string& name ) const; + const std::string* Attribute( const std::string& name, int* i ) const; + const std::string* Attribute( const std::string& name, double* d ) const; + int QueryIntAttribute( const std::string& name, int* _value ) const; + int QueryDoubleAttribute( const std::string& name, double* _value ) const; + + /// STL std::string form. + void SetAttribute( const std::string& name, const std::string& _value ); + ///< STL std::string form. + void SetAttribute( const std::string& name, int _value ); + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char * name, int value ); + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetDoubleAttribute( const char * name, double value ); + + /** Deletes an attribute with the given name. + */ + void RemoveAttribute( const char * name ); + #ifdef TIXML_USE_STL + void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. + #endif + + const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. + TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } + const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. + TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the TiXmlText child + and accessing it directly. + + If the first child of 'this' is a TiXmlText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + This is text + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + This is text + @endverbatim + GetText() will return "This is ". + + WARNING: GetText() accesses a child node - don't become confused with the + similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are + safe type casts on the referenced node. + */ + const char* GetText() const; + + /// Creates a new Element and returns it - the returned element is a copy. + virtual TiXmlNode* Clone() const; + // Print the Element to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: next char past '<' + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + + void CopyTo( TiXmlElement* target ) const; + void ClearThis(); // like clear, but initializes 'this' object as well + + // Used to be public [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + /* [internal use] + Reads the "value" of the element -- another element, or text. + This should terminate with the current end tag. + */ + const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +private: + + TiXmlAttributeSet attributeSet; +}; + + +/** An XML comment. +*/ +class TiXmlComment : public TiXmlNode +{ +public: + /// Constructs an empty comment. + TiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) {} + /// Construct a comment from text. + TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::COMMENT ) { + SetValue( _value ); + } + TiXmlComment( const TiXmlComment& ); + void operator=( const TiXmlComment& base ); + + virtual ~TiXmlComment() {} + + /// Returns a copy of this Comment. + virtual TiXmlNode* Clone() const; + // Write this Comment to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: at the ! of the !-- + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlComment* target ) const; + + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif +// virtual void StreamOut( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** XML text. A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCDATA() and query it with CDATA(). +*/ +class TiXmlText : public TiXmlNode +{ + friend class TiXmlElement; +public: + /** Constructor for text element. By default, it is treated as + normal, encoded text. If you want it be output as a CDATA text + element, set the parameter _cdata to 'true' + */ + TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + virtual ~TiXmlText() {} + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + #endif + + TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TEXT ) { copy.CopyTo( this ); } + void operator=( const TiXmlText& base ) { base.CopyTo( this ); } + + // Write this text object to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /// Queries whether this represents text using a CDATA section. + bool CDATA() const { return cdata; } + /// Turns on or off a CDATA representation of text. + void SetCDATA( bool _cdata ) { cdata = _cdata; } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + /// [internal use] Creates a new Element and returns it. + virtual TiXmlNode* Clone() const; + void CopyTo( TiXmlText* target ) const; + + bool Blank() const; // returns true if all white space and new lines + // [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + bool cdata; // true if this should be input and output as a CDATA style text element +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + + @endverbatim + + TinyXml will happily read or write files without a declaration, + however. There are 3 possible attributes to the declaration: + version, encoding, and standalone. + + Note: In this version of the code, the attributes are + handled as special cases, not generic attributes, simply + because there can only be at most 3 and they are always the same. +*/ +class TiXmlDeclaration : public TiXmlNode +{ +public: + /// Construct an empty declaration. + TiXmlDeclaration() : TiXmlNode( TiXmlNode::DECLARATION ) {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ); +#endif + + /// Construct. + TiXmlDeclaration( const char* _version, + const char* _encoding, + const char* _standalone ); + + TiXmlDeclaration( const TiXmlDeclaration& copy ); + void operator=( const TiXmlDeclaration& copy ); + + virtual ~TiXmlDeclaration() {} + + /// Version. Will return an empty string if none was found. + const char *Version() const { return version.c_str (); } + /// Encoding. Will return an empty string if none was found. + const char *Encoding() const { return encoding.c_str (); } + /// Is this a standalone document? + const char *Standalone() const { return standalone.c_str (); } + + /// Creates a copy of this Declaration and returns it. + virtual TiXmlNode* Clone() const; + // Print this declaration to a FILE stream. + virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlDeclaration* target ) const; + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + + TIXML_STRING version; + TIXML_STRING encoding; + TIXML_STRING standalone; +}; + + +/** Any tag that tinyXml doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into TiXmlUnknowns. +*/ +class TiXmlUnknown : public TiXmlNode +{ +public: + TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN ) {} + virtual ~TiXmlUnknown() {} + + TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::UNKNOWN ) { copy.CopyTo( this ); } + void operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); } + + /// Creates a copy of this Unknown and returns it. + virtual TiXmlNode* Clone() const; + // Print this Unknown to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected: + void CopyTo( TiXmlUnknown* target ) const; + + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + +}; + + +/** Always the top level node. A document binds together all the + XML pieces. It can be saved, loaded, and printed to the screen. + The 'value' of a document node is the xml file name. +*/ +class TiXmlDocument : public TiXmlNode +{ +public: + /// Create an empty document, that has no name. + TiXmlDocument(); + /// Create a document with a name. The name of the document is also the filename of the xml. + TiXmlDocument( const char * documentName ); + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlDocument( const std::string& documentName ); + #endif + + TiXmlDocument( const TiXmlDocument& copy ); + void operator=( const TiXmlDocument& copy ); + + virtual ~TiXmlDocument() {} + + /** Load a file using the current document value. + Returns true if successful. Will delete any existing + document data before loading. + */ + bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the current document value. Returns true if successful. + bool SaveFile() const; + /// Load a file using the given filename. Returns true if successful. + bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given filename. Returns true if successful. + bool SaveFile( const char * filename ) const; + /** Load a file using the given FILE*. Returns true if successful. Note that this method + doesn't stream - the entire object pointed at by the FILE* + will be interpreted as an XML file. TinyXML doesn't stream in XML from the current + file location. Streaming may be added in the future. + */ + bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given FILE*. Returns true if successful. + bool SaveFile( FILE* ) const; + + #ifdef TIXML_USE_STL + bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. + { +// StringToBuffer f( filename ); +// return ( f.buffer && LoadFile( f.buffer, encoding )); + return LoadFile( filename.c_str(), encoding ); + } + bool SaveFile( const std::string& filename ) const ///< STL std::string version. + { +// StringToBuffer f( filename ); +// return ( f.buffer && SaveFile( f.buffer )); + return SaveFile( filename.c_str() ); + } + #endif + + /** Parse the given null terminated block of xml data. Passing in an encoding to this + method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml + to use that encoding, regardless of what TinyXml might otherwise try to detect. + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** Get the root element -- the only top level element -- of the document. + In well formed XML, there should only be one. TinyXml is tolerant of + multiple elements at the document level. + */ + const TiXmlElement* RootElement() const { return FirstChildElement(); } + TiXmlElement* RootElement() { return FirstChildElement(); } + + /** If an error occurs, Error will be set to true. Also, + - The ErrorId() will contain the integer identifier of the error (not generally useful) + - The ErrorDesc() method will return the name of the error. (very useful) + - The ErrorRow() and ErrorCol() will return the location of the error (if known) + */ + bool Error() const { return error; } + + /// Contains a textual (english) description of the error if one occurs. + const char * ErrorDesc() const { return errorDesc.c_str (); } + + /** Generally, you probably want the error string ( ErrorDesc() ). But if you + prefer the ErrorId, this function will fetch it. + */ + int ErrorId() const { return errorId; } + + /** Returns the location (if known) of the error. The first column is column 1, + and the first row is row 1. A value of 0 means the row and column wasn't applicable + (memory errors, for example, have no row/column) or the parser lost the error. (An + error in the error reporting, in that case.) + + @sa SetTabSize, Row, Column + */ + int ErrorRow() const { return errorLocation.row+1; } + int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() + + /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) + to report the correct values for row and column. It does not change the output + or input in any way. + + By calling this method, with a tab size + greater than 0, the row and column of each node and attribute is stored + when the file is loaded. Very useful for tracking the DOM back in to + the source file. + + The tab size is required for calculating the location of nodes. If not + set, the default of 4 is used. The tabsize is set per document. Setting + the tabsize to 0 disables row/column tracking. + + Note that row and column tracking is not supported when using operator>>. + + The tab size needs to be enabled before the parse or load. Correct usage: + @verbatim + TiXmlDocument doc; + doc.SetTabSize( 8 ); + doc.Load( "myfile.xml" ); + @endverbatim + + @sa Row, Column + */ + void SetTabSize( int _tabsize ) { tabsize = _tabsize; } + + int TabSize() const { return tabsize; } + + /** If you have handled the error, it can be reset with this call. The error + state is automatically cleared if you Parse a new XML block. + */ + void ClearError() { error = false; + errorId = 0; + errorDesc = ""; + errorLocation.row = errorLocation.col = 0; + //errorLocation.last = 0; + } + + /** Write the document to standard out using formatted printing ("pretty print"). */ + void Print() const { Print( stdout, 0 ); } + + /* Write the document to a string using formatted printing ("pretty print"). This + will allocate a character array (new char[]) and return it as a pointer. The + calling code pust call delete[] on the return char* to avoid a memory leak. + */ + //char* PrintToMemory() const; + + /// Print this Document to a FILE stream. + virtual void Print( FILE* cfile, int depth = 0 ) const; + // [internal use] + void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + + virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + // [internal use] + virtual TiXmlNode* Clone() const; + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + void CopyTo( TiXmlDocument* target ) const; + + bool error; + int errorId; + TIXML_STRING errorDesc; + int tabsize; + TiXmlCursor errorLocation; + bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write. +}; + + +/** + A TiXmlHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + + + + + + + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + TiXmlElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity + of such code. A TiXmlHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + TiXmlHandle docHandle( &document ); + TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + TiXmlHandle handleCopy = handle; + @endverbatim + + What they should not be used for is iteration: + + @verbatim + int i=0; + while ( true ) + { + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement(); + if ( !child ) + break; + // do something + ++i; + } + @endverbatim + + It seems reasonable, but it is in fact two embedded while loops. The Child method is + a linear walk to find the element, so this code would iterate much more than it needs + to. Instead, prefer: + + @verbatim + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement(); + + for( child; child; child=child->NextSiblingElement() ) + { + // do something + } + @endverbatim +*/ +class TiXmlHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + TiXmlHandle( TiXmlNode* _node ) { this->node = _node; } + /// Copy constructor + TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } + TiXmlHandle operator=( const TiXmlHandle& ref ) { this->node = ref.node; return *this; } + + /// Return a handle to the first child node. + TiXmlHandle FirstChild() const; + /// Return a handle to the first child node with the given name. + TiXmlHandle FirstChild( const char * value ) const; + /// Return a handle to the first child element. + TiXmlHandle FirstChildElement() const; + /// Return a handle to the first child element with the given name. + TiXmlHandle FirstChildElement( const char * value ) const; + + /** Return a handle to the "index" child with the given name. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( const char* value, int index ) const; + /** Return a handle to the "index" child. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( int index ) const; + /** Return a handle to the "index" child element with the given name. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( const char* value, int index ) const; + /** Return a handle to the "index" child element. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( int index ) const; + + #ifdef TIXML_USE_STL + TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } + TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } + + TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } + TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } + #endif + + /** Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* ToNode() const { return node; } + /** Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } + /** Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } + /** Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } + + /** @deprecated use ToNode. + Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* Node() const { return ToNode(); } + /** @deprecated use ToElement. + Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* Element() const { return ToElement(); } + /** @deprecated use ToText() + Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* Text() const { return ToText(); } + /** @deprecated use ToUnknown() + Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* Unknown() const { return ToUnknown(); } + +private: + TiXmlNode* node; +}; + + +/** Print to memory functionality. The TiXmlPrinter is useful when you need to: + + -# Print to memory (especially in non-STL mode) + -# Control formatting (line endings, etc.) + + When constructed, the TiXmlPrinter is in its default "pretty printing" mode. + Before calling Accept() you can call methods to control the printing + of the XML document. After TiXmlNode::Accept() is called, the printed document can + be accessed via the CStr(), Str(), and Size() methods. + + TiXmlPrinter uses the Visitor API. + @verbatim + TiXmlPrinter printer; + printer.SetIndent( "\t" ); + + doc.Accept( &printer ); + fprintf( stdout, "%s", printer.CStr() ); + @endverbatim +*/ +class TiXmlPrinter : public TiXmlVisitor +{ +public: + TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ), + buffer(), indent( " " ), lineBreak( "\n" ) {} + + virtual bool VisitEnter( const TiXmlDocument& doc ); + virtual bool VisitExit( const TiXmlDocument& doc ); + + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + virtual bool VisitExit( const TiXmlElement& element ); + + virtual bool Visit( const TiXmlDeclaration& declaration ); + virtual bool Visit( const TiXmlText& text ); + virtual bool Visit( const TiXmlComment& comment ); + virtual bool Visit( const TiXmlUnknown& unknown ); + + /** Set the indent characters for printing. By default 4 spaces + but tab (\t) is also useful, or null/empty string for no indentation. + */ + void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; } + /// Query the indention string. + const char* Indent() { return indent.c_str(); } + /** Set the line breaking string. By default set to newline (\n). + Some operating systems prefer other characters, or can be + set to the null/empty string for no indenation. + */ + void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; } + /// Query the current line breaking string. + const char* LineBreak() { return lineBreak.c_str(); } + + /** Switch over to "stream printing" which is the most dense formatting without + linebreaks. Common when the XML is needed for network transmission. + */ + void SetStreamPrinting() { indent = ""; + lineBreak = ""; + } + /// Return the result. + const char* CStr() { return buffer.c_str(); } + /// Return the length of the result string. + size_t Size() { return buffer.size(); } + + #ifdef TIXML_USE_STL + /// Return the result. + const std::string& Str() { return buffer; } + #endif + +private: + void DoIndent() { + for( int i=0; i +#include + +#include "tinyxml.h" + +//#define DEBUG_PARSER +#if defined( DEBUG_PARSER ) +# if defined( DEBUG ) && defined( _MSC_VER ) +# include +# define TIXML_LOG OutputDebugString +# else +# define TIXML_LOG printf +# endif +#endif + +// Note tha "PutString" hardcodes the same list. This +// is less flexible than it appears. Changing the entries +// or order will break putstring. +TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] = +{ + { "&", 5, '&' }, + { "<", 4, '<' }, + { ">", 4, '>' }, + { """, 6, '\"' }, + { "'", 6, '\'' } +}; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// Including the basic of this table, which determines the #bytes in the +// sequence from the lead byte. 1 placed for invalid sequences -- +// although the result will be junk, pass it through as much as possible. +// Beware of the non-characters in UTF-8: +// ef bb bf (Microsoft "lead bytes") +// ef bf be +// ef bf bf + +const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +const int TiXmlBase::utf8ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte + 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid +}; + + +void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) + *length = 1; + else if ( input < 0x800 ) + *length = 2; + else if ( input < 0x10000 ) + *length = 3; + else if ( input < 0x200000 ) + *length = 4; + else + { *length = 0; return; } // This code won't covert this correctly anyway. + + output += *length; + + // Scary scary fall throughs. + switch (*length) + { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + } +} + + +/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalpha( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalpha( anyByte ); +// } +} + + +/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalnum( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalnum( anyByte ); +// } +} + + +class TiXmlParsingData +{ + friend class TiXmlDocument; + public: + void Stamp( const char* now, TiXmlEncoding encoding ); + + const TiXmlCursor& Cursor() { return cursor; } + + private: + // Only used by the document! + TiXmlParsingData( const char* start, int _tabsize, int row, int col ) + { + assert( start ); + stamp = start; + tabsize = _tabsize; + cursor.row = row; + cursor.col = col; + } + + TiXmlCursor cursor; + const char* stamp; + int tabsize; +}; + + +void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) +{ + assert( now ); + + // Do nothing if the tabsize is 0. + if ( tabsize < 1 ) + { + return; + } + + // Get the current row, column. + int row = cursor.row; + int col = cursor.col; + const char* p = stamp; + assert( p ); + + while ( p < now ) + { + // Treat p as unsigned, so we have a happy compiler. + const unsigned char* pU = (const unsigned char*)p; + + // Code contributed by Fletcher Dunn: (modified by lee) + switch (*pU) { + case 0: + // We *should* never get here, but in case we do, don't + // advance past the terminating null character, ever + return; + + case '\r': + // bump down to the next line + ++row; + col = 0; + // Eat the character + ++p; + + // Check for \r\n sequence, and treat this as a single character + if (*p == '\n') { + ++p; + } + break; + + case '\n': + // bump down to the next line + ++row; + col = 0; + + // Eat the character + ++p; + + // Check for \n\r sequence, and treat this as a single + // character. (Yes, this bizarre thing does occur still + // on some arcane platforms...) + if (*p == '\r') { + ++p; + } + break; + + case '\t': + // Eat the character + ++p; + + // Skip to next tab stop + col = (col / tabsize + 1) * tabsize; + break; + + case TIXML_UTF_LEAD_0: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( *(p+1) && *(p+2) ) + { + // In these cases, don't advance the column. These are + // 0-width spaces. + if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) + p += 3; + else + { p +=3; ++col; } // A normal character. + } + } + else + { + ++p; + ++col; + } + break; + + default: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // Eat the 1 to 4 byte utf8 character. + int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; + if ( step == 0 ) + step = 1; // Error case from bad encoding, but handle gracefully. + p += step; + + // Just advance one column, of course. + ++col; + } + else + { + ++p; + ++col; + } + break; + } + } + cursor.row = row; + cursor.col = col; + assert( cursor.row >= -1 ); + assert( cursor.col >= -1 ); + stamp = p; + assert( stamp ); +} + + +const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) +{ + if ( !p || !*p ) + { + return 0; + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + while ( *p ) + { + const unsigned char* pU = (const unsigned char*)p; + + // Skip the stupid Microsoft UTF-8 Byte order marks + if ( *(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==TIXML_UTF_LEAD_1 + && *(pU+2)==TIXML_UTF_LEAD_2 ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbeU ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbfU ) + { + p += 3; + continue; + } + + if ( IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) // Still using old rules for white space. + ++p; + else + break; + } + } + else + { + while ( *p && IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) + ++p; + } + + return p; +} + +#ifdef TIXML_USE_STL +/*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ) +{ + for( ;; ) + { + if ( !in->good() ) return false; + + int c = in->peek(); + // At this scope, we can't get to a document. So fail silently. + if ( !IsWhiteSpace( c ) || c <= 0 ) + return true; + + *tag += (char) in->get(); + } +} + +/*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag ) +{ + //assert( character > 0 && character < 128 ); // else it won't work in utf-8 + while ( in->good() ) + { + int c = in->peek(); + if ( c == character ) + return true; + if ( c <= 0 ) // Silent failure: can't get document at this scope + return false; + + in->get(); + *tag += (char) c; + } + return false; +} +#endif + +// One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The +// "assign" optimization removes over 10% of the execution time. +// +const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) +{ + // Oddly, not supported on some comilers, + //name->clear(); + // So use this: + *name = ""; + assert( p ); + + // Names start with letters or underscores. + // Of course, in unicode, tinyxml has no idea what a letter *is*. The + // algorithm is generous. + // + // After that, they can be letters, underscores, numbers, + // hyphens, or colons. (Colons are valid ony for namespaces, + // but tinyxml can't tell namespaces from names.) + if ( p && *p + && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) + { + const char* start = p; + while( p && *p + && ( IsAlphaNum( (unsigned char ) *p, encoding ) + || *p == '_' + || *p == '-' + || *p == '.' + || *p == ':' ) ) + { + //(*name) += *p; // expensive + ++p; + } + if ( p-start > 0 ) { + name->assign( start, p-start ); + } + return p; + } + return 0; +} + +const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) +{ + // Presume an entity, and pull it out. + TIXML_STRING ent; + int i; + *length = 0; + + if ( *(p+1) && *(p+1) == '#' && *(p+2) ) + { + unsigned long ucs = 0; + ptrdiff_t delta = 0; + unsigned mult = 1; + + if ( *(p+2) == 'x' ) + { + // Hexadecimal. + if ( !*(p+3) ) return 0; + + const char* q = p+3; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != 'x' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else if ( *q >= 'a' && *q <= 'f' ) + ucs += mult * (*q - 'a' + 10); + else if ( *q >= 'A' && *q <= 'F' ) + ucs += mult * (*q - 'A' + 10 ); + else + return 0; + mult *= 16; + --q; + } + } + else + { + // Decimal. + if ( !*(p+2) ) return 0; + + const char* q = p+2; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != '#' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else + return 0; + mult *= 10; + --q; + } + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + } + else + { + *value = (char)ucs; + *length = 1; + } + return p + delta + 1; + } + + // Now try to match it. + for( i=0; iappend( cArr, len ); + } + } + else + { + bool whitespace = false; + + // Remove leading white space: + p = SkipWhiteSpace( p, encoding ); + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) ) + { + if ( *p == '\r' || *p == '\n' ) + { + whitespace = true; + ++p; + } + else if ( IsWhiteSpace( *p ) ) + { + whitespace = true; + ++p; + } + else + { + // If we've found whitespace, add it before the + // new character. Any whitespace just becomes a space. + if ( whitespace ) + { + (*text) += ' '; + whitespace = false; + } + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + if ( len == 1 ) + (*text) += cArr[0]; // more efficient + else + text->append( cArr, len ); + } + } + } + if ( p ) + p += strlen( endTag ); + return p; +} + +#ifdef TIXML_USE_STL + +void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + // The basic issue with a document is that we don't know what we're + // streaming. Read something presumed to be a tag (and hope), then + // identify it, and call the appropriate stream method on the tag. + // + // This "pre-streaming" will never read the closing ">" so the + // sub-tag can orient itself. + + if ( !StreamTo( in, '<', tag ) ) + { + SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + while ( in->good() ) + { + int tagIndex = (int) tag->length(); + while ( in->good() && in->peek() != '>' ) + { + int c = in->get(); + if ( c <= 0 ) + { + SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + break; + } + (*tag) += (char) c; + } + + if ( in->good() ) + { + // We now have something we presume to be a node of + // some sort. Identify it, and call the node to + // continue streaming. + TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); + + if ( node ) + { + node->StreamIn( in, tag ); + bool isElement = node->ToElement() != 0; + delete node; + node = 0; + + // If this is the root element, we're done. Parsing will be + // done by the >> operator. + if ( isElement ) + { + return; + } + } + else + { + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + } + } + // We should have returned sooner. + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); +} + +#endif + +const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) +{ + ClearError(); + + // Parse away, at the document level. Since a document + // contains nothing but other tags, most of what happens + // here is skipping white space. + if ( !p || !*p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + // Note that, for a document, this needs to come + // before the while space skip, so that parsing + // starts from the pointer we are given. + location.Clear(); + if ( prevData ) + { + location.row = prevData->cursor.row; + location.col = prevData->cursor.col; + } + else + { + location.row = 0; + location.col = 0; + } + TiXmlParsingData data( p, TabSize(), location.row, location.col ); + location = data.Cursor(); + + if ( encoding == TIXML_ENCODING_UNKNOWN ) + { + // Check for the Microsoft UTF-8 lead bytes. + const unsigned char* pU = (const unsigned char*)p; + if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 + && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 + && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) + { + encoding = TIXML_ENCODING_UTF8; + useMicrosoftBOM = true; + } + } + + p = SkipWhiteSpace( p, encoding ); + if ( !p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + while ( p && *p ) + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, &data, encoding ); + LinkEndChild( node ); + } + else + { + break; + } + + // Did we get encoding info? + if ( encoding == TIXML_ENCODING_UNKNOWN + && node->ToDeclaration() ) + { + TiXmlDeclaration* dec = node->ToDeclaration(); + const char* enc = dec->Encoding(); + assert( enc ); + + if ( *enc == 0 ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice + else + encoding = TIXML_ENCODING_LEGACY; + } + + p = SkipWhiteSpace( p, encoding ); + } + + // Was this empty? + if ( !firstChild ) { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); + return 0; + } + + // All is well. + return p; +} + +void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + // The first error in a chain is more accurate - don't set again! + if ( error ) + return; + + assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); + error = true; + errorId = err; + errorDesc = errorString[ errorId ]; + + errorLocation.Clear(); + if ( pError && data ) + { + data->Stamp( pError, encoding ); + errorLocation = data->Cursor(); + } +} + + +TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) +{ + TiXmlNode* returnNode = 0; + + p = SkipWhiteSpace( p, encoding ); + if( !p || !*p || *p != '<' ) + { + return 0; + } + + TiXmlDocument* doc = GetDocument(); + p = SkipWhiteSpace( p, encoding ); + + if ( !p || !*p ) + { + return 0; + } + + // What is this thing? + // - Elements start with a letter or underscore, but xml is reserved. + // - Comments: "; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // [ 1475201 ] TinyXML parses entities in comments + // Oops - ReadText doesn't work, because we don't want to parse the entities. + // p = ReadText( p, &value, false, endTag, false, encoding ); + // + // from the XML spec: + /* + [Definition: Comments may appear anywhere in a document outside other markup; in addition, + they may appear within the document type declaration at places allowed by the grammar. + They are not part of the document's character data; an XML processor MAY, but need not, + make it possible for an application to retrieve the text of comments. For compatibility, + the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity + references MUST NOT be recognized within comments. + + An example of a comment: + + + */ + + value = ""; + // Keep all the white space. + while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) + { + value.append( p, 1 ); + ++p; + } + if ( p ) + p += strlen( endTag ); + + return p; +} + + +const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) return 0; + +// int tabsize = 4; +// if ( document ) +// tabsize = document->TabSize(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + // Read the name, the '=' and the value. + const char* pErr = p; + p = ReadName( p, &name, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p || *p != '=' ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + ++p; // skip '=' + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + const char* end; + const char SINGLE_QUOTE = '\''; + const char DOUBLE_QUOTE = '\"'; + + if ( *p == SINGLE_QUOTE ) + { + ++p; + end = "\'"; // single quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else if ( *p == DOUBLE_QUOTE ) + { + ++p; + end = "\""; // double quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else + { + // All attribute values should be in single or double quotes. + // But this is such a common error that the parser will try + // its best, even without them. + value = ""; + while ( p && *p // existence + && !IsWhiteSpace( *p ) && *p != '\n' && *p != '\r' // whitespace + && *p != '/' && *p != '>' ) // tag end + { + if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { + // [ 1451649 ] Attribute values with trailing quotes not handled correctly + // We did not have an opening quote but seem to have a + // closing one. Give up and throw an error. + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + value += *p; + ++p; + } + } + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->peek(); + if ( !cdata && (c == '<' ) ) + { + return; + } + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + in->get(); // "commits" the peek made above + + if ( cdata && c == '>' && tag->size() >= 3 ) { + size_t len = tag->size(); + if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) { + // terminator of cdata. + return; + } + } + } +} +#endif + +const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + value = ""; + TiXmlDocument* document = GetDocument(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + const char* const startTag = ""; + + if ( cdata || StringEqual( p, startTag, false, encoding ) ) + { + cdata = true; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // Keep all the white space, ignore the encoding, etc. + while ( p && *p + && !StringEqual( p, endTag, false, encoding ) + ) + { + value += *p; + ++p; + } + + TIXML_STRING dummy; + p = ReadText( p, &dummy, false, endTag, false, encoding ); + return p; + } + else + { + bool ignoreWhite = true; + + const char* end = "<"; + p = ReadText( p, &value, ignoreWhite, end, false, encoding ); + if ( p ) + return p-1; // don't truncate the '<' + return 0; + } +} + +#ifdef TIXML_USE_STL +void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + +const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) +{ + p = SkipWhiteSpace( p, _encoding ); + // Find the beginning, find the end, and look for + // the stuff in-between. + TiXmlDocument* document = GetDocument(); + if ( !p || !*p || !StringEqual( p, "SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); + return 0; + } + if ( data ) + { + data->Stamp( p, _encoding ); + location = data->Cursor(); + } + p += 5; + + version = ""; + encoding = ""; + standalone = ""; + + while ( p && *p ) + { + if ( *p == '>' ) + { + ++p; + return p; + } + + p = SkipWhiteSpace( p, _encoding ); + if ( StringEqual( p, "version", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + version = attrib.Value(); + } + else if ( StringEqual( p, "encoding", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + encoding = attrib.Value(); + } + else if ( StringEqual( p, "standalone", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + standalone = attrib.Value(); + } + else + { + // Read over whatever it is. + while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) + ++p; + } + } + return 0; +} + +bool TiXmlText::Blank() const +{ + for ( unsigned i=0; i 2 - 25 + 70 15 2 2 diff --git a/examples/eLianTest_log.xml b/examples/eLianTest_log.xml index 9d01de1..e75dadf 100644 --- a/examples/eLianTest_log.xml +++ b/examples/eLianTest_log.xml @@ -42,7 +42,7 @@ 2 - 25 + 70 15 2 2 @@ -58,8 +58,7 @@ - /home/admin/Pathplanning/LIAN/LIAN/examples/eLianTest.xml - + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 @@ -74,14 +73,14 @@ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 - 0 0 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 * 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 * * 0 0 0 1 1 1 1 1 1 0 0 0 0 * 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 * 0 0 1 1 1 1 1 1 0 0 0 0 * 0 0 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 * * 1 1 1 1 1 1 0 0 0 0 * 0 0 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 * 0 0 0 0 0 0 0 0 * 0 0 0 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 * * * * * * * * 0 0 0 0 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 * * * * * * * * * * * * * * * * * * * * 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 * * 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 * * * * * 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 * + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 * * * * * 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 * + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 * * * * 0 1 1 1 1 1 1 0 0 0 0 0 0 * + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 * * * 0 0 0 0 0 0 0 0 0 0 * + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 * * * * 0 0 0 0 0 0 * + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 * * * 0 0 * 0 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 * * 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 @@ -91,549 +90,75 @@ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+
+
+
+
+ diff --git a/examples/example1.xml b/examples/example1.xml index 746fd76..dabe048 100644 --- a/examples/example1.xml +++ b/examples/example1.xml @@ -50,7 +50,7 @@ 2 - 1 + 3 diff --git a/examples/example1_log1.xml b/examples/example1_log.xml similarity index 78% rename from examples/example1_log1.xml rename to examples/example1_log.xml index e31dee4..ad8ac46 100644 --- a/examples/example1_log1.xml +++ b/examples/example1_log.xml @@ -50,13 +50,12 @@ 2 - 1 + 3 - /home/admin/Pathplanning/LIAN/Lian/LIAN/examples/example1.xml - + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 @@ -85,30 +84,36 @@ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - - + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + +
- + + + + + + + diff --git a/examples/example1_log05.xml b/examples/example1_log05.xml deleted file mode 100644 index 1a01576..0000000 --- a/examples/example1_log05.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - 25 - 25 - 24 - 7 - 15 - 20 - 10 - - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 - 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 - 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 - 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - - - - 2 - 45 - 5 - 3 - 1 - 0 - -1 - false - 0 - 2 - - - 0.5 - - - - - /home/admin/Pathplanning/LIAN/Lian/LIAN/examples/example1.xml - - - diff --git a/examples/example1_log15.xml b/examples/example1_log15.xml deleted file mode 100644 index 5ec7814..0000000 --- a/examples/example1_log15.xml +++ /dev/null @@ -1,127 +0,0 @@ - - - - - 25 - 25 - 24 - 7 - 15 - 20 - 10 - - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 - 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 - 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 - 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - - - - 2 - 45 - 5 - 3 - 1 - 0 - -1 - false - 0 - 2 - - - 1.5 - - - - - /home/admin/Pathplanning/LIAN/Lian/LIAN/examples/example1.xml - - - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 - 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 - 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 - 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 * 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 * 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 * 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 * 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 * 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 * * * * * * * * * * 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - diff --git a/examples/example1_log2.xml b/examples/example1_log2.xml deleted file mode 100644 index 6b913dc..0000000 --- a/examples/example1_log2.xml +++ /dev/null @@ -1,143 +0,0 @@ - - - - - 25 - 25 - 24 - 7 - 15 - 20 - 10 - - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 - 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 - 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 - 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - - - - 2 - 45 - 5 - 3 - 1 - 0 - -1 - false - 0 - 2 - - - 2 - - - - - /home/admin/Pathplanning/LIAN/Lian/LIAN/examples/example1.xml - - - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 - 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 - 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 - 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 * 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 * 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 * 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 * 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 * 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 * * * * * * * * * * 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/gl_const.h b/gl_const.h deleted file mode 100755 index 488574e..0000000 --- a/gl_const.h +++ /dev/null @@ -1,134 +0,0 @@ -#ifndef GL_CONST_H -#define GL_CONST_H - -#define CN_PI_CONSTANT 3.14159265359 -#define CN_SQRT2_CONSTANT 1.41421356237 -#define CN_EPSILON 1e-8 - -#define CN_LOG "_log" - -//Parametrs Type -#define CN_PT_LOGLVL 0 // log level -#define CN_PT_AL 1 // anglelimit -#define CN_PT_D 2 // distance -#define CN_PT_W 3 // weight -#define CN_PT_SL 4 // steplimit -#define CN_PT_CHW 5 // curvature heuristic weight -#define CN_PT_DDF 6 // coefficient to decrease distance -#define CN_PT_DM 7 // distancemin -#define CN_PT_PC 8 // pivot circle value -#define CN_PT_NOP 9 // number of parents to increase radius -#define CN_PT_PS 10 // post smoother - -#define CN_PT_NUM 11 // number of parameters - - -//Default values of parameters - -#define CN_PTD_AL 30 -#define CN_PTD_D 4 -#define CN_PTD_W 2 -#define CN_PTD_SL 20000 // Maximum number of steps, after this number the path is considered nonexistent -#define CN_PTD_LR 1 - -#define CN_PTD_DDF 1 // Value that divides distance in order to decrease radius of the step -#define CN_PTD_DMIN 3 // Radius minimum - -#define CN_PTD_NOFPTOINCRAD 2 // Number of parent vertecies that must have the same radius in order to start the increase of current radius - -//Obstacle -#define CN_OBSTL 1 - -//loglevel -#define CN_LOGLVL_NO 0 -#define CN_LOGLVL_TINY 0.5 -#define CN_LOGLVL_HIGH 1 -#define CN_LOGLVL_MED 1.5 -#define CN_LOGLVL_LOW 2 -#define CN_LOGLVL_ITER 1.2 - -/* - * XML file tags --------------------------------------------------------------- - */ - - -#define CNS_TAG_ROOT "root" - #define CNS_TAG_ALGORITHM "algorithm" - #define CNS_TAG_MAP "map" - #define CNS_TAG_HEIGHT "height" - #define CNS_TAG_WIDTH "width" - #define CNS_TAG_SX "startx" - #define CNS_TAG_SY "starty" - #define CNS_TAG_FX "finishx" - #define CNS_TAG_FY "finishy" - #define CNS_TAG_CELLSIZE "cellsize" - #define CNS_TAG_GRID "grid" - #define CNS_TAG_ROW "row" - #define CNS_TAG_ANGLELIMIT "anglelimit" - #define CNS_TAG_DISTANCE "distance" - #define CNS_TAG_WEIGHT "hweight" - #define CNS_TAG_STEPLIMIT "steplimit" - #define CNS_TAG_CURVHEURWEIGHT "curvatureHeuristicWeight" - #define CNS_TAG_SMOOTHER "postsmoothing" - #define CNS_TAG_PIVOTCIRCLE "pivotCircleRadius" - #define CNS_TAG_DISTANCEMIN "distancemin" - #define CNS_TAG_DECRDISTFACTOR "decreaseDistanceFactor" - #define CNS_TAG_NOFPTOINCRAD "numOfParentsToIncreaseRadius" - #define CNS_TAG_OPTIONS "options" - #define CNS_TAG_LOGLVL "loglevel" - #define CNS_TAG_LOG "log" - #define CNS_TAG_MAPFN "mapfilename" - #define CNS_TAG_SUM "summary" - #define CNS_TAG_PATH "path" - #define CNS_TAG_ROW "row" - #define CNS_TAG_LPLEVEL "lplevel" - #define CNS_TAG_HPLEVEL "hplevel" - #define CNS_TAG_ANGLES "angles" - #define CNS_TAG_ANGLE "angle" - #define CNS_TAG_ITERS "iterations" - #define CNS_TAG_LOWLEVEL "lowlevel" - #define CNS_TAG_SECTION "section" - #define CNS_TAG_STEP "step" - #define CNS_TAG_OPEN "open" - #define CNS_TAG_NODE "node" - #define CNS_TAG_CLOSE "close" - -/* - * End of XML files tags ------------------------------------------------------- - */ - - -/* - * XML files tag's attributes -------------------------------------------------- - */ - #define CNS_TAG_ATTR_NUMOFSTEPS "numberofsteps" - #define CNS_TAG_ATTR_NODESCREATED "nodescreated" - #define CNS_TAG_ATTR_LENGTH "length" - #define CNS_TAG_ATTR_LENGTHSC "length_scaled" - #define CNS_TAG_ATTR_SECTIONS "sections" - #define CNS_TAG_ATTR_TIME "time" - #define CNS_TAG_ATTR_X "x" - #define CNS_TAG_ATTR_Y "y" - #define CNS_TAG_ATTR_NUM "number" - #define CNS_TAG_ATTR_F "F" - #define CNS_TAG_ATTR_G "g" - #define CNS_TAG_ATTR_PARX "parent_x" - #define CNS_TAG_ATTR_PARY "parent_y" - #define CNS_TAG_ATTR_VALUE "value" - #define CNS_TAG_ATTR_SX "start.x" - #define CNS_TAG_ATTR_SY "start.y" - #define CNS_TAG_ATTR_FX "finish.x" - #define CNS_TAG_ATTR_FY "finish.y" - #define CNS_TAG_ATTR_PF "pathfound" - #define CNS_TAG_ATTR_TRUE "true" - #define CNS_TAG_ATTR_FALSE "false" - #define CNS_TAG_ATTR_MAXANGLE "max_angle" - #define CNS_TAG_ATTR_ACCUMANGLE "accum_angle" - - - -/* - * End of XML files tag's attributes ------------------------------------------- - */ - -#endif diff --git a/liansearch.cpp b/liansearch.cpp deleted file mode 100755 index ac04563..0000000 --- a/liansearch.cpp +++ /dev/null @@ -1,755 +0,0 @@ -#include "liansearch.h" - - -/* - * // Use for more accurate time calculation - * #ifdef __linux__ - * #include - * #else - * #include - * #endif - * -*/ - -LianSearch::~LianSearch() {} - - -LianSearch::LianSearch(float angleLimit_, int distance_, float weight_, - unsigned int steplimit_, float curvatureHeuristicWeight_, bool postsmoother_, - float decreaseDistanceFactor_, int distanceMin_, - double PivotRadius_, int numOfParentsToIncreaseRadius_) { - this->angleLimit = angleLimit_; - this->distance = distance_; - this->weight = weight_; - this->stepLimit = steplimit_; - this->curvatureHeuristicWeight = curvatureHeuristicWeight_; - this->postsmoother = postsmoother_; - this->decreaseDistanceFactor = decreaseDistanceFactor_; - this->distanceMin = distanceMin_; - this->pivotRadius = PivotRadius_; - this->numOfParentsToIncreaseRadius = numOfParentsToIncreaseRadius_; - closeSize = 0; - srand(time(NULL)); -} - -void LianSearch::calculateCircle(int radius) { //here radius - radius of the circle in cells - circleNodes.clear(); - circleNodes.resize(listOfDistancesSize); - for(int k = 0; k < listOfDistancesSize; ++k) { - radius = listOfDistances[k]; - circleNodes[k].clear(); - std::vector circle_nodes(0); - int x = 0; - int y = radius; - int delta = 2 - 2 * radius; - int error = 0; - while (y >= 0) { - if(x > radius) x = radius; - else if(x < -radius) x = -radius; - if(y > radius) y = radius; - else if(y < -radius) y = -radius; - double dist = getCost(0,0,x,y); - circle_nodes.push_back(circleNode(x, y, dist)); - circle_nodes.push_back(circleNode(x, -y, dist)); - circle_nodes.push_back(circleNode(-x, y, dist)); - circle_nodes.push_back(circleNode(-x, -y, dist)); - - error = 2 * (delta + y) - 1; - if (delta < 0 && error <= 0) { - delta += 2 * ++x + 1; - continue; - } - - error = 2 * (delta - x) - 1; - if (delta > 0 && error > 0) { - delta += 1 - 2 * --y; - continue; - } - delta += 2 * (++x - y--); - } - - for (int i = 0; i < circle_nodes.size(); i += 4) - circleNodes[k].push_back(circle_nodes[i]); - for (int i = circle_nodes.size() - 7; i > 0; i -= 4) - circleNodes[k].push_back(circle_nodes[i]); - for (int i = 7; i < circle_nodes.size(); i += 4) - circleNodes[k].push_back(circle_nodes[i]); - for (int i = circle_nodes.size() - 6; i > 0; i -= 4) - circleNodes[k].push_back(circle_nodes[i]); - circleNodes[k].pop_back(); - for (size_t i = 0; i < circleNodes[k].size(); ++i) { - double angle = acos((circleNodes[k][0].i * circleNodes[k][i].i + circleNodes[k][0].j * circleNodes[k][i].j) - / (sqrt(pow(circleNodes[k][0].i, 2) + pow(circleNodes[k][0].j, 2)) - * sqrt(pow(circleNodes[k][i].i, 2) + pow(circleNodes[k][i].j, 2)))); - if(i < circleNodes[k].size() / 2) - circleNodes[k][i].heading = angle * 180 / CN_PI_CONSTANT; - else - circleNodes[k][i].heading = 360 - angle * 180 / CN_PI_CONSTANT; - //std::cout << circleNodes[k][i].heading << std::endl; - } - } -} - -void LianSearch::calculatePivotCircle() { - pivotCircle.clear(); - int add_i, add_j, num(pivotRadius + 0.5 - CN_EPSILON); - for (int i = -num; i <= +num; i++) { - for (int j = -num; j <= +num; j++) { - add_i = i != 0 ? 1 : 0; - add_j = j != 0 ? 1 : 0; - if ((pow(2 * abs(i) - add_i, 2) + pow(2 * abs(j) - add_j, 2)) < pow(2 * pivotRadius, 2)) - pivotCircle.push_back({i, j}); - } - } - if (pivotCircle.empty()) - pivotCircle.push_back({0, 0}); -} - -bool LianSearch::checkPivotCircle(const Map &map, const Node ¢er) { - int i, j; - for (int k = 0; k < pivotCircle.size(); k++) { - i = center.i + pivotCircle[k].first; - j = center.j + pivotCircle[k].second; - if (!map.CellOnGrid(i, j) || map.CellIsObstacle(i,j)) - return false; - } - return true; -} - -void LianSearch::calculateDistances() { - int curDistance = distance; - if(decreaseDistanceFactor > 1) { - while(curDistance >= distanceMin) { - listOfDistances.push_back(curDistance); - curDistance = ceil(curDistance / decreaseDistanceFactor); - } - } else { - listOfDistances.push_back(curDistance); - } - listOfDistancesSize = listOfDistances.size(); -} - -void LianSearch::calculateLineSegment(std::vector &line, const Node &start, const Node &goal) { - int x1 = start.i; - int x2 = goal.i; - int y1 = start.j; - int y2 = goal.j; - - int x,y; - int dx, dy; - int StepVal = 0; - int Rotate = 0; - - line.clear(); - - if (x1 > x2 && y1 > y2) { - std::swap(x1, x2); - std::swap(y1, y2); - - dx = x2 - x1; - dy = y2 - y1; - } else { - dx = x2 - x1; - dy = y2 - y1; - - if (dx >= 0 && dy >= 0) Rotate = 2; - else if (dy < 0) { - dy = -dy; - std::swap(y1, y2); - - Rotate = 1; - } else if (dx < 0) { - dx = -dx; - std::swap(x1, x2); - - Rotate = 3; - } - } - - if (Rotate == 1) { - if(dx >= dy) { - for(x = x1; x <= x2; ++x) { - line.push_back(Node(x, y2)); - StepVal += dy; - if(StepVal >= dx) { - --y2; - StepVal -= dx; - } - } - } else { - for(y = y1; y <= y2; ++y) { - line.insert(line.begin(),Node(x2, y)); - StepVal += dx; - if(StepVal >= dy) { - --x2; - StepVal -= dy; - } - } - } - return; - } else if(Rotate == 2) { - if(dx >= dy) { - for(x = x1; x <= x2; ++x) { - line.push_back(Node(x, y1)); - StepVal += dy; - if(StepVal >= dx) { - ++y1; - StepVal -= dx; - } - } - return; - } else { - for(y = y1; y <= y2; ++y) { - line.push_back(Node(x1, y)); - StepVal += dx; - if(StepVal >= dy) { - ++x1; - StepVal -= dy; - } - } - return; - } - } else if (Rotate == 3) { - if(dx >= dy) { - for(x = x1; x <= x2; ++x) { - line.insert(line.begin(),Node(x, y2)); - StepVal += dy; - if(StepVal >= dx){ - --y2; - StepVal -= dx; - } - } - } else { - for(y = y1; y <= y2; ++y) { - line.push_back(Node(x2, y)); - StepVal += dx; - if(StepVal >= dy) { - --x2; - StepVal -= dy; - } - } - } - return; - } - - if(dx >= dy) { - for(x = x1; x <= x2; ++x) { - line.insert(line.begin(),Node(x, y1)); - StepVal += dy; - if(StepVal >= dx){ - ++y1; - StepVal -= dx; - } - } - } else { - for(y = y1; y <= y2; ++y) { - line.insert(line.begin(),Node(x1, y)); - StepVal += dx; - if(StepVal >= dy) { - ++x1; - StepVal -= dy; - } - } - } -} - -bool LianSearch::checkLineSegment(const Map &map, const Node &start, const Node &goal) { - int x1 = start.i; - int x2 = goal.i; - int y1 = start.j; - int y2 = goal.j; - - int x,y; - int dx, dy; - int StepVal = 0; - int Rotate = 0; - - if (x1 > x2 && y1 > y2) { - std::swap(x1, x2); - std::swap(y1, y2); - - dx = x2 - x1; - dy = y2 - y1; - } else { - dx = x2 - x1; - dy = y2 - y1; - - if (dx >= 0 && dy >= 0) Rotate = 2; - else if (dy < 0) { - dy = -dy; - std::swap(y1, y2); - Rotate = 1; - } else if (dx < 0) { - dx = -dx; - std::swap(x1, x2); - Rotate = 3; - } - } - - if (Rotate == 1) { - if (dx >= dy) { - for(x = x1; x <= x2; ++x) { - if (map.CellIsObstacle(x, y2)) return false; - StepVal += dy; - if (StepVal >= dx){ - --y2; - StepVal -= dx; - } - } - } else { - for (y = y1; y <= y2; ++y) { - if (map.CellIsObstacle(x2, y)) return false; - StepVal += dx; - if (StepVal >= dy) { - --x2; - StepVal -= dy; - } - } - } - return true; - } else if(Rotate == 2) { - if (dx >= dy) { - y = y1; - for (x = x1; x <= x2; ++x) { - if (map.CellIsObstacle(x, y1)) return false; - StepVal += dy; - if (StepVal >= dx) { - ++y1; - StepVal -= dx; - } - } - return true; - } else { - for (y = y1; y <= y2; ++y) { - if (map.CellIsObstacle(x1, y)) return false; - StepVal += dx; - if (StepVal >= dy) { - ++x1; - StepVal -= dy; - } - } - return true; - } - } else if (Rotate == 3) { - if (dx >= dy) { - for (x = x1; x <= x2; ++x) { - if (map.CellIsObstacle(x, y2)) return false; - StepVal += dy; - if (StepVal >= dx) { - --y2; - StepVal -= dx; - } - } - } else { - for(y = y1; y <= y2; ++y) { - if (map.CellIsObstacle(x2, y)) return false; - StepVal += dx; - if (StepVal >= dy) { - --x2; - StepVal -= dy; - } - } - } - return true; - } - - if(dx >= dy) { - for(x = x1; x <= x2; ++x) { - if (map.CellIsObstacle(x, y1)) return false; - StepVal += dy; - if(StepVal >= dx){ - ++y1; - StepVal -= dx; - } - } - } else { - for(y = y1; y <= y2; ++y) { - if (map.CellIsObstacle(x1, y)) return false; - StepVal += dx; - if (StepVal >= dy) { - ++x1; - StepVal -= dy; - } - } - } - return true; -} - -bool LianSearch::stopCriterion() { - if(open.get_size() == 0) { - std::cout << "OPEN list is empty!" << std::endl; - return true; - } - - if (closeSize > stepLimit && stepLimit > 0) { - std::cout << "Algorithm esceeded step limit!" << std::endl; - return true; - } - return false; -} - -double LianSearch::getCost(int a_i, int a_j, int b_i, int b_j) const { - return sqrt(abs(a_i - b_i) * abs(a_i - b_i) + abs(a_j - b_j) * abs(a_j - b_j)); -} - - -double LianSearch::calcAngle(const Node &dad, const Node &node, const Node &son) const { - double cos_angle = (node.j - dad.j) * (son.j - node.j) + - (node.i - dad.i) * (son.i - node.i); - cos_angle /= getCost(son.i, son.j, node.i, node.j); - cos_angle /= getCost(node.i, node.j, dad.i, dad.j); - - if (cos_angle < -1) cos_angle = -1; - if (cos_angle > 1) cos_angle = 1; - - return acos(cos_angle); -} - -SearchResult LianSearch::startSearch(Logger *Log, const Map &map) { - - calculateDistances(); - - std::cout << "List of distances :"; - for (auto dist : listOfDistances) { - std::cout << " " << dist; - } - std::cout << std::endl; - - open.resize(map.getHeight()); - Node curNode(map.start_i,map.start_j, 0.0, 0.0, 0.0); - curNode.radius = distance; - curNode.F = weight * getCost(curNode.i,curNode.j, map.goal_i,map.goal_j); - bool pathFound = false; - open.add(curNode); - calculateCircle((int) curNode.radius); - calculatePivotCircle(); - - std::chrono::time_point begin, end; - begin = std::chrono::system_clock::now(); - - /* - * #ifdef __linux__ - * timeval begin, end; - * gettimeofday(&begin, NULL); - * #else - * LARGE_INTEGER begin,end,freq; - * QueryPerformanceCounter(&begin); - * QueryPerformanceFrequency(&freq); - * #endif - */ - - while(!stopCriterion()) { // main cycle of the search - curNode = open.getMin(); - close.insert({curNode.convolution(map.getWidth()),curNode}); - ++closeSize; - - if(Log->loglevel == CN_LOGLVL_ITER) Log->writeToLogIter(closeSize, curNode); - - if (curNode.i == map.goal_i && curNode.j == map.goal_j) { // if current point is goal point - end of the cycle - pathFound = true; - break; - } - - if(!expand(curNode, map) && listOfDistancesSize>1) - while(curNode.radius>listOfDistances[listOfDistancesSize-1]) - if(tryToDecreaseRadius(curNode,map.getWidth())) - if(expand(curNode, map)) - break; - - if(Log->loglevel >= CN_LOGLVL_LOW) Log->writeToLogOpenClose(open, close); - } - - if (Log->loglevel == CN_LOGLVL_MED) Log->writeToLogOpenClose(open, close); - - sresult.nodescreated = open.get_size() + closeSize; - sresult.numberofsteps = closeSize; - if (pathFound) { - sresult.pathlength = curNode.g; - makePrimaryPath(curNode); - if (postsmoother) { - hppath = smoothPath(hppath, map); - } - makeSecondaryPath(); - float max_angle = makeAngles(); - sresult.pathfound = true; - sresult.hppath = hppath; - sresult.lppath = lppath; - sresult.angles = angles; - sresult.max_angle = max_angle; - sresult.sections = hppath.size()-1; - - end = std::chrono::system_clock::now(); - sresult.time = static_cast(std::chrono::duration_cast(end - begin).count()) / 1000000000; - /* // for more accurate time calculation - #ifdef __linux__ - gettimeofday(&end, NULL); - sresult.time = (end.tv_sec - begin.tv_sec) + static_cast(end.tv_usec - begin.tv_usec) / 1000000; - #else - QueryPerformanceCounter(&end); - sresult.time = static_cast(end.QuadPart-begin.QuadPart) / freq.QuadPart; - #endif */ - - return sresult; - } else { - sresult.pathfound = false; - - end = std::chrono::system_clock::now(); - sresult.time = static_cast(std::chrono::duration_cast(end - begin).count()) / 1000000000; - - /* for more accurate time calculation - #ifdef __linux__ - gettimeofday(&end, NULL); - sresult.time = (end.tv_sec - begin.tv_sec) + static_cast(end.tv_usec - begin.tv_usec) / 1000000; - #else - QueryPerformanceCounter(&end); - sresult.time = static_cast(end.QuadPart-begin.QuadPart) / freq.QuadPart; - #endif */ - - return sresult; - } -} - -int LianSearch::tryToIncreaseRadius(Node curNode) { - bool change = false; - int i, k = 0; - while (k < numOfParentsToIncreaseRadius) { - if (curNode.parent != NULL) { - if (curNode.radius == curNode.parent->radius) { - ++k; - curNode = *curNode.parent; - continue; - } - } - break; - } - if (k == numOfParentsToIncreaseRadius) { - for (i = listOfDistancesSize-1; i >= 0; --i) - if (curNode.radius == listOfDistances[i]) break; - if(i > 0) change=true; - } - if(change) return listOfDistances[i-1]; - else return curNode.radius; -} - -void LianSearch::update(const Node current_node, Node new_node, bool &successors, const Map &map) { - if (!checkLineSegment(map, *new_node.parent, new_node)) return; - if (pivotRadius > 0 && (new_node.i != map.goal_i || new_node.j != map.goal_j) && !checkPivotCircle(map, new_node)) return; - - auto it = close.find(new_node.convolution(map.getWidth())); - if (it != close.end()) { - auto range = close.equal_range(it->first); - for (auto it = range.first; it != range.second; ++it) - if (it->second.parent == nullptr || (it->second.parent->i == current_node.i && it->second.parent->j == current_node.j)) - return; - } - - if(listOfDistancesSize > 1) new_node.radius = tryToIncreaseRadius(new_node); - open.add(new_node); - successors = true; -} - -bool LianSearch::expand(const Node curNode, const Map &map) { - int current_distance; - for(current_distance = 0; current_distance < listOfDistancesSize; ++current_distance) - if(listOfDistances[current_distance] == curNode.radius) - break; - - std::vector circle_nodes = circleNodes[current_distance]; - - bool successors_are_fine = false; - auto parent = &(close.find(curNode.convolution(map.getWidth()))->second); - if (curNode.parent != nullptr) { - int node_straight_ahead = (int)round(curNode.angle * circleNodes[current_distance].size() / 360) % circleNodes[current_distance].size(); - double angle = fabs(curNode.angle - circleNodes[current_distance][node_straight_ahead].heading); - if ((angle <= 180 && angle <= angleLimit) || (angle > 180 && 360 - angle <= angleLimit)) { - int new_pos_i = curNode.i + circle_nodes[node_straight_ahead].i; - int new_pos_j = curNode.j + circle_nodes[node_straight_ahead].j; - if (map.CellOnGrid(new_pos_i, new_pos_j) && map.CellIsTraversable(new_pos_i, new_pos_j)) { - Node newNode = Node(new_pos_i, new_pos_j); - newNode.g = curNode.g + getCost(curNode.i, curNode.j, new_pos_i, new_pos_j); - newNode.angle = circleNodes[current_distance][node_straight_ahead].heading; - newNode.F = newNode.g + weight * getCost(new_pos_i, new_pos_j, map.goal_i, map.goal_j) + - curvatureHeuristicWeight * distance * fabs(curNode.angle - newNode.angle); - newNode.radius = curNode.radius; - newNode.parent = parent; - - update(curNode, newNode, successors_are_fine, map); - } - } // now we will expand neighbors that are closest to the node that lies straight ahead - - std::vector candidates = std::vector{node_straight_ahead, node_straight_ahead}; - bool limit1 = true; - bool limit2 = true; - while (++candidates[0] != --candidates[1] && (limit1 || limit2)) { // untill the whole circle is explored or we exessed anglelimit somewhere - if (candidates[0] >= circle_nodes.size()) candidates[0] = 0; - if (candidates[1] < 0) candidates[1] = circle_nodes.size() - 1; - - for (auto cand : candidates) { - double angle = fabs(curNode.angle - circleNodes[current_distance][cand].heading); - if ((angle <= 180 && angle <= angleLimit) || (angle > 180 && 360 - angle <= angleLimit)) { - int new_pos_i = curNode.i + circle_nodes[cand].i; - int new_pos_j = curNode.j + circle_nodes[cand].j; - - if (!map.CellOnGrid(new_pos_i, new_pos_j)) continue; - if (map.CellIsObstacle(new_pos_i, new_pos_j)) continue; - - Node newNode = Node(new_pos_i, new_pos_j); - newNode.g = curNode.g + getCost(curNode.i, curNode.j, new_pos_i, new_pos_j); - newNode.angle = circleNodes[current_distance][cand].heading; - newNode.F = newNode.g + weight * getCost(new_pos_i, new_pos_j, map.goal_i, map.goal_j) + - curvatureHeuristicWeight * distance * fabs(curNode.angle - newNode.angle); - newNode.radius = curNode.radius; - newNode.parent = parent; - - update(curNode, newNode, successors_are_fine, map); - } else { - if (cand == candidates[0]) limit1 = false; - else limit2 = false; - } - } - } - } else { // when we do not have parent, we should explore all neighbors - int angle_position(-1), new_pos_i, new_pos_j; - for (auto node : circle_nodes) { - new_pos_i = curNode.i + node.i; - new_pos_j = curNode.j + node.j; - angle_position++; - - if (!map.CellOnGrid(new_pos_i, new_pos_j)) continue; - if (map.CellIsObstacle(new_pos_i, new_pos_j)) continue; - - Node newNode = Node(new_pos_i, new_pos_j); - newNode.g = curNode.g + getCost(curNode.i, curNode.j, new_pos_i, new_pos_j); - newNode.F = newNode.g + weight * getCost(new_pos_i, new_pos_j, map.goal_i, map.goal_j); - newNode.radius = curNode.radius; - newNode.angle = circleNodes[current_distance][angle_position].heading; - newNode.parent = parent; - - update(curNode, newNode, successors_are_fine, map); - } - } - - // when we are near goal point, we should try to reach it - if (getCost(curNode.i, curNode.j, map.goal_i, map.goal_j) <= curNode.radius) { - double angle = calcAngle(*curNode.parent, curNode, Node(map.goal_i, map.goal_j)); - - if (fabs(angle * 180 / CN_PI_CONSTANT) <= angleLimit) { - Node newNode = Node( map.goal_i, map.goal_j, - curNode.g + getCost(curNode.i, curNode.j, map.goal_i, map.goal_j), 0.0, - curNode.radius, parent, curvatureHeuristicWeight * distance, 0.0); - - update(curNode, newNode, successors_are_fine, map); - } - } - return successors_are_fine; -} - - -bool LianSearch::tryToDecreaseRadius(Node& curNode, int width) { - int i; - for(i = listOfDistancesSize - 1; i >= 0; --i) - if (curNode.radius == listOfDistances[i]) break; - if (i < listOfDistancesSize - 1) { - curNode.radius = listOfDistances[i + 1]; - auto it = close.find(curNode.convolution(width)); - auto range = close.equal_range(it->first); - for(auto it = range.first; it != range.second; ++it) { - if(it->second.parent && it->second.parent->i == curNode.parent->i - && it->second.parent->j == curNode.parent->j) { - it->second.radius = listOfDistances[i + 1]; - break; - } - } - return true; - } - return false; -} - - -void LianSearch::makePrimaryPath(Node curNode) { - hppath.push_front(curNode); - curNode = *curNode.parent; - do { - hppath.push_front(curNode); - //std::cout << '(' << curNode.i << ", " << curNode.j << ") "; - curNode = *curNode.parent; - - } while (curNode.parent != nullptr); - hppath.push_front(curNode); - //std::cout << '(' << curNode.i << ", " << curNode.j << ")\n"; -} - -bool LianSearch::checkAngle(const Node &dad, const Node &node, const Node &son) const { - double angle = calcAngle(dad, node, son) * 180 / CN_PI_CONSTANT; - if (fabs(angle) <= angleLimit) { - return true; - } - return false; -} - - -std::list LianSearch::smoothPath(const std::list& path, const Map& map) { - std::list new_path; - sresult.pathlength = 0; - auto it = path.begin(); - auto curr_it = path.begin(); - Node start_section = path.front(); - Node end_section = path.front(); - bool first = true; - Node previous = *it++; - while (end_section != path.back()) { - for (it; it != path.end(); ++it) { - auto next = ++it; - --it; - if (!first && !checkAngle(previous, start_section, *it)) continue; - if ((next != path.end() && checkAngle(start_section, *it, *next) || - next == path.end()) && checkLineSegment(map, start_section, *it)) { - end_section = *it; - curr_it = it; - } - } - sresult.pathlength += (double)getCost(previous.i, previous.j, start_section.i, start_section.j); - new_path.push_back(start_section); - previous = start_section; - first = false; - start_section = end_section; - it = ++curr_it; - } - sresult.pathlength += (double)getCost(previous.i, previous.j, end_section.i, end_section.j); - new_path.push_back(end_section); - return new_path; -} - -void LianSearch::makeSecondaryPath() { - std::vector lineSegment; - auto it = hppath.begin(); - Node parent = *it++; - while (it != hppath.end()) { - calculateLineSegment(lineSegment, parent, *it); - std::reverse(std::begin(lineSegment), std::end(lineSegment)); - lppath.insert(lppath.begin(), ++lineSegment.begin(), lineSegment.end()); - parent = *it++; - } - lppath.push_front(hppath.back()); - std::reverse(std::begin(lppath), std::end(lppath)); -} - - -double LianSearch::makeAngles() { - angles.clear(); - double max_angle = 0; - sresult.accum_angle = 0; - auto pred = hppath.begin(); - auto current = ++hppath.begin(); - auto succ = ++(++hppath.begin()); - - while(succ != hppath.end()) { - double angle = calcAngle(*pred++, *current++, *succ++); - angle = angle * 180 / CN_PI_CONSTANT; - if (angle > max_angle) max_angle = angle; - sresult.accum_angle += angle; - angles.push_back(angle); - } - std::reverse(std::begin(angles), std::end(angles)); - return max_angle; -} diff --git a/liansearch.h b/liansearch.h deleted file mode 100755 index 4b7a2dd..0000000 --- a/liansearch.h +++ /dev/null @@ -1,103 +0,0 @@ -#ifndef LIANSEARCH_H -#define LIANSEARCH_H - -#include "gl_const.h" -#include "map.h" -#include "node.h" -#include "openlist.h" -#include "search.h" - -#include -#include -#include -#include -#include -#include - -class LianSearch : public Search { - -public: - - // Constructor with parameters - LianSearch(float angleLimit_, int distance_, float weight_, unsigned int steplimit_, - float curvatureHeuristicWeight_, bool postsmoother_, float decreaseDistanceFactor_, - int distanceMin_, double PivotRadius_, int numOfParentsToIncreaseRadius_); - - ~LianSearch(); - SearchResult startSearch(Logger *Log, const Map &map); // General searching algorithm - -private: - - float angleLimit; // Maximal value of deviation angle (turning limit) - - int distance; // Minimal value of length of steps - - int numOfParentsToIncreaseRadius; - - std::vector listOfDistances; - int listOfDistancesSize; - - float weight; // Heuristics weight - - bool postsmoother; // Smoothing the path after the algorithm - - // Heurisic coefficient: - // If there is heuristic that checks deviation of trajectory from line on each - // step, this deviation is multiplyed by this coefficient - float curvatureHeuristicWeight; - - float pivotRadius; // Radius of safety circle around every turn point. - - unsigned int stepLimit; // Maximum number of iterations, allowed for the algorithm - - unsigned int closeSize; // Number of elements in close (elements that were already examined) - - float decreaseDistanceFactor; // Value for decreasing the initial distance value - int distanceMin; // Minimal distance value - - std::vector > circleNodes; // Virtual nodes that create circle around the cell - - std::vector > pivotCircle; // Vector of nodes (shifts) for pivot security check - - std::vector angles; - - std::list lppath, hppath; // Final path in two representations - OpenList open; // Open : list of nodes waiting for expanding - - std::unordered_multimap close; // Close: list of nodes that were already expanded - - // Method that calculate Bresenham's Circle (center - (0, 0)) and writing list of created nodes to circleNodes - void calculateCircle(int radius); // Radius - radius of the circle in cells - - void calculatePivotCircle(); - - int calculatePreferableRadius(const Map &map); // Method calculates the most preferable radius depending on the parameters of the initial map - - void calculateDistances(); - - void calculateLineSegment(std::vector &line, const Node &start, const Node &goal); // Method builds Bresenham's Line - - bool checkLineSegment(const Map &map, const Node &start, const Node &goal); // Method builds Bresenham's Line and check it for impassable parts - - // check that there are no obstacle in a safety radius from a turn point - bool checkPivotCircle(const Map &map, const Node ¢er); - - double getCost(int a_i, int a_j, int b_i, int b_j) const; - - double calcAngle(const Node &dad, const Node &node, const Node &son) const; - bool checkAngle(const Node &dad, const Node &node, const Node &son) const; - - bool stopCriterion(); // Check for the ending criteria. Return true if the algorithm should be stopped - - int tryToIncreaseRadius(Node curNode); - bool tryToDecreaseRadius(Node &curNode, int width); - void findSuccessors(const Node curNode,std::vector &successors, const Map &map); - void update(const Node current_node, Node new_node, bool &successors, const Map &map); - bool expand(const Node curNode, const Map &map); - std::list smoothPath(const std::list& path, const Map& map); - void makePrimaryPath(Node curNode); - void makeSecondaryPath(); - double makeAngles(); -}; - -#endif // LIANSEARCH_H diff --git a/logger.h b/logger.h deleted file mode 100755 index fa76b46..0000000 --- a/logger.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef LOGGER_H -#define LOGGER_H - -#include "map.h" -#include "node.h" -#include "openlist.h" - -#include -#include -#include - - -class Logger { - -public: - float loglevel; - -public: - Logger() : loglevel(-1) {} - virtual ~Logger() {} - virtual bool getLog(const char* FileName) = 0; - virtual void saveLog() = 0; - virtual void writeToLogIter(int closeSize, const Node &curNode) = 0; - virtual void writeToLogMap(const Map &map,const std::list &path) = 0; - virtual void writeToLogOpenClose(const OpenList &open, const std::unordered_multimap& close) = 0; - virtual void writeToLogPath(const std::list &path, const std::vector &angles) = 0; - virtual void writeToLogHpLevel(const std::list &path) = 0; - virtual void writeToLogSummary(const std::list &path, int numberofsteps, int nodescreated, float length, float length_scaled, - long double time, float max_angle, float accum_angle, int sections) = 0;}; - -#endif diff --git a/main.cpp b/main.cpp deleted file mode 100755 index 2ff04f2..0000000 --- a/main.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include"mission.h" - -#include - -int main(int argc, char* argv[]) { - if (argc == 2) { - Mission Mission(argv[1]); - std::cout<<"Retreiving map from input XML file.\n"; - if (!Mission.getMap()) { - std::cout<<"Program terminated.\n"; - return 0; - } - - std::cout<<"Retreiving search algorithm configuration from input XML file.\n"; - if (!Mission.getConfig()) return 0; - - Mission.createSearch(); - Mission.createLog(); - Mission.startSearch(); - - std::cout<<"Search is finished!"<= 0 && curr_j < width && curr_j >= 0); -} - -int Map::getHeight() const { - return height; -} - -int Map::getWidth() const { - return width; -} - -double Map::getCellSize() const { - return CellSize; -} - -bool Map::getMap(const char* FileName) { - const char* grid = 0; - std::string value; - TiXmlElement *root = 0; - std::string text = ""; - bool hasGrid = false; - std::stringstream stream; - TiXmlDocument doc(FileName); - if(!doc.LoadFile()) { - std::cout << "Error openning input XML file."<FirstChildElement(CNS_TAG_MAP); - if (!map) { - std::cout << "Error! No '" << CNS_TAG_MAP << "' element found in XML file." << std::endl; - return false; - } - - TiXmlNode *node = 0; - TiXmlElement *element = 0; - - node = map->FirstChild(); - - while (node) { - element = node->ToElement(); - value = node->Value(); - std::transform(value.begin(), value.end(), value.begin(), ::tolower); - CellSize = 1; - - if (!hasGrid && height > 0 && width > 0) { - Grid = new int * [height]; - for (int i = 0; i < height; i++) { - Grid[i] = new int[width]; - } - - hasGrid = true; - } - - if (value == CNS_TAG_HEIGHT) { - text = element->GetText(); - stream << text; - stream >> height; - stream.clear(); - stream.str(""); - if (height <= 0) { - std::cout << "Error! Wrong '" << CNS_TAG_HEIGHT << "' value." << std::endl; - return false; - } - } else if (value == CNS_TAG_WIDTH) { - text = element->GetText(); - stream << text; - stream >> width; - stream.clear(); - stream.str(""); - - if (width <= 0) { - std::cout << "Error! Wrong '" << CNS_TAG_WIDTH << "' value." << std::endl; - return false; - } - } else if (value == CNS_TAG_CELLSIZE) { - text = element->GetText(); - stream << text; - stream >> CellSize; - stream.clear(); - stream.str(""); - - if (CellSize <= 0) { - std::cout << "Warning! Wrong '"<< CNS_TAG_CELLSIZE << "' value. Set to default value: 1." << std::endl; - CellSize = 1; - } - } else if (value == CNS_TAG_SX) { - text = element->GetText(); - stream << text; - stream >> start_j; - stream.clear(); - stream.str(""); - - if (start_j < 0 || start_j >= width) { - std::cout << "Error! Wrong '" << CNS_TAG_SX << "' value." << std::endl; - return false; - } - } else if (value == CNS_TAG_SY) { - text = element->GetText(); - stream << text; - stream >> start_i; - stream.clear(); - stream.str(""); - - if (start_i < 0 || start_i >= height) { - std::cout << "Error! Wrong '" << CNS_TAG_SY << "' value." << std::endl; - return false; - } - } else if (value == CNS_TAG_FX) { - text = element->GetText(); - stream << text; - stream >> goal_j; - stream.clear(); - stream.str(""); - - if (goal_j < 0 || goal_j >= width) { - std::cout << "Error! Wrong '" << CNS_TAG_FX << "' value." << std::endl; - return false; - } - } else if (value == CNS_TAG_FY) { - text = element->GetText(); - stream << text; - stream >> goal_i; - stream.clear(); - stream.str(""); - - if (goal_i < 0 || goal_i >= height) { - std::cout << "Error! Wrong '" << CNS_TAG_FY << "' value." << std::endl; - return false; - } - } else if (value == CNS_TAG_GRID) { - if (height == -1 || width == -1) { - std::cout << "Error! No '" << CNS_TAG_HEIGHT << "' or '" << CNS_TAG_WIDTH << "' before '" << CNS_TAG_GRID << "' given." << std::endl; - return false; - } - - element = node->FirstChildElement(CNS_TAG_ROW); - - int i=0; - while (i < height) { - if (!element) { - std::cout << "Not enough '" << CNS_TAG_ROW << "' in '" << CNS_TAG_GRID << "' given." << std::endl; - return false; - } - - grid = element->GetText(); - int k = 0; - text = ""; - int j = 0; - - for(k = 0; k < (strlen(grid)); k++) { - if (grid[k] == ' ') { - stream << text; - stream >> Grid[i][j]; - stream.clear(); - stream.str(""); - text = ""; - j++; - } else { - text += grid[k]; - } - } - stream << text; - stream >> Grid[i][j]; - stream.clear(); - stream.str(""); - - if (j < width - 1) { - std::cout << "Not enough cells in '" << CNS_TAG_ROW << "' " << i << " given." << std::endl; - return false; - } - i++; - element = element->NextSiblingElement(); - } - } - node = map->IterateChildren(node); - } - - return true; -} diff --git a/mission.cpp b/mission.cpp deleted file mode 100755 index aaf2a86..0000000 --- a/mission.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "mission.h" - -Mission::Mission(const char* fName) : fileName(fName), search(nullptr), logger(nullptr) {} - -Mission::~Mission() { - delete search; - delete logger; -} - -bool Mission::getMap() { - return map.getMap(fileName); -} - -bool Mission::getConfig() { - return config.getConfig(fileName); -} - -void Mission::createSearch() { - search = new LianSearch((float)config.getParamValue(CN_PT_AL), - (int)config.getParamValue(CN_PT_D), - (float)config.getParamValue(CN_PT_W), - (unsigned int)config.getParamValue(CN_PT_SL), - (float)config.getParamValue(CN_PT_CHW), - (bool)config.getParamValue(CN_PT_PS), - (float)config.getParamValue(CN_PT_DDF), - (int)config.getParamValue(CN_PT_DM), - (double)config.getParamValue(CN_PT_PC), - (int)config.getParamValue(CN_PT_NOP)); -} - -bool Mission::createLog() { - if(config.getParamValue(CN_PT_LOGLVL) == CN_LOGLVL_LOW || config.getParamValue(CN_PT_LOGLVL) == CN_LOGLVL_HIGH || - config.getParamValue(CN_PT_LOGLVL) == CN_LOGLVL_MED || config.getParamValue(CN_PT_LOGLVL) == CN_LOGLVL_TINY || - config.getParamValue(CN_PT_LOGLVL) - CN_LOGLVL_ITER < 0.001) { - logger = new XmlLogger(config.getParamValue(CN_PT_LOGLVL)); - } else if(config.getParamValue(CN_PT_LOGLVL) == CN_LOGLVL_NO) { - logger = new XmlLogger(config.getParamValue(CN_PT_LOGLVL)); - return true; - } else { - std::cout << "'loglevel' is not correctly specified in input XML-file.\n"; - return false; - } - return logger->getLog(fileName); -} - -void Mission::startSearch() { - sr = search->startSearch(logger, map); -} - -void Mission::printSearchResultsToConsole() { - std::cout << "Path "; - if (!sr.pathfound) - std::cout << "NOT "; - std::cout << "found!" << std::endl; - std::cout << "nodescreated=" << sr.nodescreated << std::endl; - std::cout << "numberofsteps=" << sr.numberofsteps << std::endl; - if (sr.pathfound) { - std::cout << "pathlength=" << sr.pathlength << std::endl; - std::cout << "length_scaled=" << sr.pathlength * map.getCellSize() << std::endl; - } - std::cout << "time=" << sr.time << std::endl; -} - -void Mission::saveSearchResultsToLog() { - logger->writeToLogSummary(sr.hppath, sr.numberofsteps, sr.nodescreated, sr.pathlength, sr.pathlength * map.getCellSize(), - sr.time, sr.max_angle, sr.accum_angle, sr.sections); - - if (sr.pathfound) { - logger->writeToLogPath(sr.lppath, sr.angles); - logger->writeToLogMap(map,sr.lppath); - logger->writeToLogHpLevel(sr.hppath); - } - logger->saveLog(); -} - diff --git a/node.h b/node.h deleted file mode 100755 index 6808e0a..0000000 --- a/node.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef NODE_H -#define NODE_H - -#include "gl_const.h" - -#include -#include -#include -#include - -struct circleNode -{ - int i, j; - double heading; - double cost; - - circleNode(): i(-1), j(-1), heading(-1), cost(0) {} - circleNode(int i, int j, double cost): i(i), j(j), heading(-1), cost(cost) {} - circleNode(int i, int j, double heading, double cost): i(i), j(j), heading(heading), cost(cost) {} -}; - -struct Node { - - Node* parent; - - int i, j; - int radius; - float F; - float g; - - double angle; - - Node() : i(-1), j(-1), F(std::numeric_limits::infinity()), g(std::numeric_limits::infinity()), - parent(nullptr), radius(CN_PTD_D), angle(0) {} - - Node(int x, int y, float g_=std::numeric_limits::infinity(), double h_=std::numeric_limits::infinity(), - float radius_=CN_PTD_D, Node *parent_=nullptr, float cweightdist_=0, double ang_=0) : - i(x), j(y), g(g_), radius(radius_), parent(parent_), angle(ang_) { - if (parent) { - F = g + h_ + cweightdist_ * fabs(ang_ - parent->angle); - } else { - F = g + h_; - } - } - - ~Node() { - parent = nullptr; - } - - inline Node& operator=(const Node& other) { - i = other.i; - j = other.j; - F = other.F; - g = other.g; - parent = other.parent; - angle = other.angle; - radius = other.radius; - return *this; - } - - inline bool operator==(const Node& p) const { - return i == p.i && j == p.j && parent->i == p.parent->i && parent->j == p.parent->j; - } - - inline bool operator!=(const Node& p) const { - return !(*this == p); - } - - int convolution(int width) const { - return i * width + j; - } -}; - -#endif diff --git a/openlist.cpp b/openlist.cpp deleted file mode 100755 index c06ddcf..0000000 --- a/openlist.cpp +++ /dev/null @@ -1,125 +0,0 @@ -#include "openlist.h" - -OpenList::OpenList() { size = 0; } - -OpenList::OpenList(int size_) { - elements = new std::list[size_]; - size = 0; - height = size_; -} - -void OpenList::resize(int size_) { - elements = new std::list[size_]; - height = size_; - size = 0; -} - -OpenList::~OpenList() { - delete [] elements; -} - -size_t OpenList::get_size() const { - return size; -} - -bool OpenList::is_empty() const { - if (size == 0) return true; - return false; -} - -void OpenList::add(Node new_node) { - if (elements[new_node.i].empty()) { - elements[new_node.i].push_back(new_node); - ++size; - return; - } - std::list::iterator pos = elements[new_node.i].end(); - bool pos_found = false; - - for(auto it = elements[new_node.i].begin(); it != elements[new_node.i].end(); ++it) { - if ((it->F >= new_node.F) && (!pos_found)) { - pos = it; - pos_found = true; - } - if (*it == new_node) { - if (new_node.F >= it->F) return; - else { - if(pos == it) { - it->g = new_node.g; - it->F = new_node.F; - it->radius = new_node.radius; - return; - } - elements[new_node.i].erase(it); - --size; - break; - } - } - } - ++size; - elements[new_node.i].insert(pos,new_node); -} - -Node OpenList::getMin() { - Node min; - min.F = std::numeric_limits::infinity(); - for(size_t i = 0; i < height; i++) { - if(!elements[i].empty() && elements[i].begin()->F <= min.F) { - if (elements[i].begin()->F == min.F) { - if (elements[i].begin()->g >= min.g) { - min = elements[i].front(); - } - } else { - min = elements[i].front(); - } - } - } - elements[min.i].pop_front(); - --size; - return min; -} - -TiXmlElement * OpenList::writeToXml(TiXmlElement * element, TiXmlNode * child) const { - Node min; - min.F = std::numeric_limits::infinity(); - for(size_t i = 0; i < height; i++) { - if(!elements[i].empty() && elements[i].begin()->F <= min.F) { - if (elements[i].begin()->F == min.F) { - if (elements[i].begin()->g >= min.g) { - min = elements[i].front(); - } - } else { - min = elements[i].front(); - } - } - } - if(min.F != std::numeric_limits::infinity()) { - element = new TiXmlElement(CNS_TAG_NODE); - element -> SetAttribute(CNS_TAG_ATTR_X, min.j); - element -> SetAttribute(CNS_TAG_ATTR_Y, min.i); - element -> SetDoubleAttribute(CNS_TAG_ATTR_F, min.F); - element -> SetDoubleAttribute(CNS_TAG_ATTR_G, min.g); - element -> SetAttribute(CNS_TAG_ATTR_PARX, min.parent->j); - element -> SetAttribute(CNS_TAG_ATTR_PARY, min.parent->i); - child -> InsertEndChild(*element); - } - for(size_t i = 0; i < height; ++i) { - if(!elements[i].empty()) { - for (auto it = elements[i].begin(); it != elements[i].end(); ++it) { - if (*it != min) { - element -> Clear(); - element -> SetAttribute(CNS_TAG_ATTR_X, it->j); - element -> SetAttribute(CNS_TAG_ATTR_Y, it->i); - element -> SetDoubleAttribute(CNS_TAG_ATTR_F, it->F); - element -> SetDoubleAttribute(CNS_TAG_ATTR_G, it->g); - if (it->g > 0){ - element -> SetAttribute(CNS_TAG_ATTR_PARX, it->parent->j); - element -> SetAttribute(CNS_TAG_ATTR_PARY, it->parent->i); - } - child->InsertEndChild(*element); - } - } - } - } - return element; -} diff --git a/openlist.h b/openlist.h deleted file mode 100755 index 701b074..0000000 --- a/openlist.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef OPENLIST_H -#define OPENLIST_H - -#include "node.h" -#include "tinyxml/tinyxml.h" -#include "tinyxml/tinystr.h" - -#include - -class OpenList { - -public: - OpenList(); - OpenList(int size_); - - ~OpenList(); - - void resize(int size_); - - size_t get_size() const; - bool is_empty() const; - - void add(Node new_node); - Node getMin(); - void pop(Node min); - - TiXmlElement *writeToXml(TiXmlElement * element, TiXmlNode *child) const; - -private: - std::list *elements; - size_t size; - size_t height; -}; - -#endif // OPENLIST_H diff --git a/search.h b/search.h deleted file mode 100755 index d20281d..0000000 --- a/search.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef SEARCH_H -#define SEARCH_H - -#include "gl_const.h" -#include "logger.h" -#include "map.h" -#include "searchresult.h" -#include "xmllogger.h" - -class Search { - -public: - Search() {} - virtual ~Search () {} - virtual SearchResult startSearch(Logger *Log, const Map &map) = 0; - - SearchResult sresult; -}; - -#endif diff --git a/searchresult.h b/searchresult.h deleted file mode 100755 index ababb11..0000000 --- a/searchresult.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef SEARCHRESULT_H -#define SEARCHRESULT_H - -#include "node.h" -#include - -struct SearchResult { - - bool pathfound; - float pathlength; - std::list hppath,lppath; - unsigned int nodescreated; - unsigned int numberofsteps; - std::vector angles; - float accum_angle; - double time; - float max_angle; - int sections; - - SearchResult() : pathfound(false), pathlength(0), nodescreated(0), - numberofsteps(0), time(0), max_angle(0), sections(0) { - hppath.clear(); - lppath.clear(); - angles.clear(); - } -}; - -#endif // SEARCHRESULT_H diff --git a/src/liansearch.cpp b/src/liansearch.cpp new file mode 100644 index 0000000..ac24892 --- /dev/null +++ b/src/liansearch.cpp @@ -0,0 +1,616 @@ +#include "liansearch.h" + +#include +#include + +double getCost(int a_i, int a_j, int b_i, int b_j) { + return sqrt(abs(a_i - b_i) * abs(a_i - b_i) + abs(a_j - b_j) * abs(a_j - b_j)); +} + +void saveIterationToLog(std::shared_ptr &logger, int closeSize_, const Node& curNode) { + auto space = logger->logSpace(Logger::Tags::iterations); + if (!space) { + return; + } + + TiXmlElement element(Logger::Tags::step); + + element.SetAttribute(Logger::Tags::step, closeSize_); + element.SetAttribute(Logger::Tags::parentX, curNode.j); + element.SetAttribute(Logger::Tags::y, curNode.i); + if (curNode.parent) { + element.SetAttribute(Logger::Tags::parentX, curNode.parent->j); + element.SetAttribute(Logger::Tags::parentY, curNode.parent->i); + } + element.SetDoubleAttribute(Logger::Tags::F, curNode.F); + element.SetDoubleAttribute(Logger::Tags::g, curNode.g); + + space->InsertEndChild(element); +} + +/* + * // Use for more accurate time calculation + * #ifdef __linux__ + * #include + * #else + * #include + * #endif + * +*/ + +std::vector LianSearch::buildDistances() const { + std::vector result; + int curDistance = settings_.distance; + if (settings_.decreaseDistanceFactor > 1) { + while (curDistance >= settings_.distanceMin) { + result.push_back(curDistance); + curDistance = std::ceil((float)curDistance / settings_.decreaseDistanceFactor); + } + } + else { + result.push_back(curDistance); + } + return result; +} + +LianSearch::LianSearch(SearchParams settings) + : settings_(settings) + , distanceLookup_(buildDistances()) +{ + srand(time(nullptr)); +} + +void LianSearch::calculateCircle(int radius) { //here radius - radius of the circle in cells + circleNodes_.clear(); + circleNodes_.resize(distanceLookup_.size()); + for (int k = 0; k < distanceLookup_.size(); ++k) { + radius = distanceLookup_[k]; + circleNodes_[k].clear(); + std::vector circle_nodes(0); + int x = 0; + int y = radius; + int delta = 2 - 2 * radius; + while (y >= 0) { + x = std::min(x, radius); + x = std::max(x, -radius); + y = std::min(y, radius); + y = std::max(y, -radius); + double dist = getCost(0, 0, x, y); + circle_nodes.emplace_back(x, y, dist); + circle_nodes.emplace_back(x, -y, dist); + circle_nodes.emplace_back(-x, y, dist); + circle_nodes.emplace_back(-x, -y, dist); + + int error = 2 * (delta + y) - 1; + if (delta < 0 && error <= 0) { + delta += 2 * ++x + 1; + continue; + } + + error = 2 * (delta - x) - 1; + if (delta > 0 && error > 0) { + delta += 1 - 2 * --y; + continue; + } + delta += 2 * (++x - y--); + } + + for (int i = 0; i < circle_nodes.size(); i += 4) { + circleNodes_[k].push_back(circle_nodes[i]); + } + for (int i = circle_nodes.size() - 7; i > 0; i -= 4) { + circleNodes_[k].push_back(circle_nodes[i]); + } + for (int i = 7; i < circle_nodes.size(); i += 4) { + circleNodes_[k].push_back(circle_nodes[i]); + } + for (int i = circle_nodes.size() - 6; i > 0; i -= 4) { + circleNodes_[k].push_back(circle_nodes[i]); + } + circleNodes_[k].pop_back(); + for (size_t i = 0; i < circleNodes_[k].size(); ++i) { + double angle = acos((circleNodes_[k][0].i * circleNodes_[k][i].i + circleNodes_[k][0].j * circleNodes_[k][i].j) + / (sqrt(pow(circleNodes_[k][0].i, 2) + pow(circleNodes_[k][0].j, 2)) + * sqrt(pow(circleNodes_[k][i].i, 2) + pow(circleNodes_[k][i].j, 2)))); + if (i < circleNodes_[k].size() / 2) { + circleNodes_[k][i].heading = angle * 180 / std::numbers::pi; + } + else { + circleNodes_[k][i].heading = 360 - angle * 180 / std::numbers::pi; + } + //std::cout << circleNodes[k][i].heading << std::endl; + } + } +} + +void LianSearch::calculatePivotCircle() { + pivotCircle_.clear(); + int add_i; + int add_j; + int num = floor(settings_.pivotRadius + 0.5 - CN_EPSILON); + for (int i = -num; i <= +num; i++) { + for (int j = -num; j <= +num; j++) { + add_i = i != 0 ? 1 : 0; + add_j = j != 0 ? 1 : 0; + if ((pow(2 * abs(i) - add_i, 2) + pow(2 * abs(j) - add_j, 2)) < pow(2 * settings_.pivotRadius, 2)) { + pivotCircle_.emplace_back( i, j ); + } + } + } + if (pivotCircle_.empty()) { + pivotCircle_.emplace_back( 0, 0 ); + } +} + +bool LianSearch::checkPivotCircle(const Map& map, const Node& center) { + int i, j; + for (auto & cell : pivotCircle_) { + i = center.i + cell.first; + j = center.j + cell.second; + if (!map.CellOnGrid(i, j) || map.CellIsObstacle(i, j)) { + return false; + } + } + return true; +} + + +template +bool lineSegmentTraverse(const Node& start, const Node& goal, ActionF action) { + int64_t x1 = start.i; + int64_t y1 = start.j; + int64_t x2 = goal.i; + int64_t y2 = goal.j; + + int64_t dx = abs(x2 - x1), dy = abs(y2 - y1); + int64_t stepVal = 0; + int rotate = 0; + + if (x1 > x2 && y1 > y2) { + std::swap(x1, x2); + std::swap(y1, y2); + } + else if (x2 - x1 >= 0 && y2 - y1 >= 0) { + rotate = 2; + } + else if (y2 - y1 < 0) { + std::swap(y1, y2); + rotate = 1; + } + else if (x2 - x1 < 0) { + std::swap(x1, x2); + rotate = 3; + } + + int64_t i = 0, j = 0; + + bool iFromX = dx >= dy; + + int64_t stepInc = iFromX ? dy : dx; + int64_t stepDec = iFromX ? dx : dy; + int64_t startI = iFromX ? x1 : y1; + int64_t finishI = iFromX ? x2 : y2; + + bool rotateFirstBit = rotate & 1; + j = !rotateFirstBit ? (x1 ^ y1 ^ startI) : (x2 ^ y2 ^ finishI); + int dj = !rotateFirstBit ? 1 : -1; + + for (i = startI; i <= finishI; ++i) { + if (iFromX ? !action(i, j) : !action(j, i)) { + break; + } + + stepVal += stepInc; + if (stepVal >= stepDec) { + j += dj; + stepVal -= stepDec; + } + } + + bool reversed = rotate == 0 || (rotate == 1 && !iFromX) || (rotate == 3 && iFromX); + return reversed; +} + +std::vector LianSearch::calculateLineSegment(const Node& start, const Node& goal) { + std::vector line; + bool reversed = lineSegmentTraverse(start, goal, + [&line](int64_t t, int64_t c) { + line.emplace_back(t, c); + return true; + }); + if (reversed) { + std::reverse(line.begin(), line.end()); + } + return line; +} + +bool LianSearch::checkLineSegment(const Map& map, const Node& start, const Node& goal) { + bool hasObstacleOnLineSegment = false; + lineSegmentTraverse(start, goal, + [&map, &hasObstacleOnLineSegment](int64_t t, int64_t c) { + hasObstacleOnLineSegment |= map.CellIsObstacle(t, c); + return !hasObstacleOnLineSegment; + }); + return !hasObstacleOnLineSegment; +} + + +bool LianSearch::stopCriterion() { + if (search_tree_.empty()) { + std::cout << "OPEN list is empty!" << std::endl; + return true; + } + + if (search_tree_.sizeClose() > settings_.stepLimit && settings_.stepLimit > 0) { + std::cout << "Algorithm exceeded step limit!" << std::endl; + return true; + } + return false; +} + + +double LianSearch::calcAngle(const Node& dad, const Node& node, const Node& son) { + double cos_angle = (node.j - dad.j) * (son.j - node.j) + + (node.i - dad.i) * (son.i - node.i); + cos_angle /= getCost(son.i, son.j, node.i, node.j); + cos_angle /= getCost(node.i, node.j, dad.i, dad.j); + + cos_angle = std::min(cos_angle, (double)1); + cos_angle = std::max(cos_angle, (double)-1); + + return acos(cos_angle); +} + +SearchResult LianSearch::startSearch(std::shared_ptr logger, const Map& map) { + Node curNode(map.start_i, map.start_j, 0.0, 0.0, 0.0); + curNode.radius = settings_.distance; + curNode.F = settings_.weight * getCost(curNode.i, curNode.j, map.goal_i, map.goal_j); + bool pathFound = false; + search_tree_.addOpen(curNode); + calculateCircle((int)curNode.radius); + calculatePivotCircle(); + + std::chrono::time_point begin, end; + begin = std::chrono::system_clock::now(); + + /* + * #ifdef __linux__ + * timeval begin, end; + * gettimeofday(&begin, NULL); + * #else + * LARGE_INTEGER begin,end,freq; + * QueryPerformanceCounter(&begin); + * QueryPerformanceFrequency(&freq); + * #endif + */ + + while (!stopCriterion()) { // main cycle of the search + std::optional min = search_tree_.getOpen(); + if (!min) + break; + curNode = *min; + search_tree_.addClose( curNode); + + saveIterationToLog(logger, search_tree_.sizeClose(), curNode); + + if (curNode.i == map.goal_i && curNode.j == map.goal_j) { // if current point is goal point - end of the cycle + pathFound = true; + break; + } + + if (!expand(curNode, map) && distanceLookup_.size() > 1) + while (curNode.radius > distanceLookup_[distanceLookup_.size() - 1]) + if (tryDecreaseRadius(curNode)) + if (expand(curNode, map)) + break; + + search_tree_.saveToLogOpenAndClose(logger); + } + + search_tree_.saveToLogOpenAndClose(logger); + + sresult_.nodesCreated = search_tree_.sizeOpen() + search_tree_.sizeClose(); + sresult_.numberOfSteps = search_tree_.sizeClose(); + if (pathFound) { + sresult_.pathLength = curNode.g; + makePrimaryPath(&curNode); + if (settings_.postsmoother) { + hppath_ = smoothPath(hppath_, map); + } + makeSecondaryPath(); + float max_angle = makeAngles(); + sresult_.pathFound = true; + sresult_.hpPath = hppath_; + sresult_.lpPath = lppath_; + sresult_.angles = angles_; + sresult_.maxAngle = max_angle; + sresult_.sections = hppath_.size() - 1; + + end = std::chrono::system_clock::now(); + sresult_.time = static_cast(std::chrono::duration_cast(end - begin).count()) / 1000000000; + /* // for more accurate time calculation + #ifdef __linux__ + gettimeofday(&end, NULL); + sresult.time = (end.tv_sec - begin.tv_sec) + static_cast(end.tv_usec - begin.tv_usec) / 1000000; + #else + QueryPerformanceCounter(&end); + sresult.time = static_cast(end.QuadPart-begin.QuadPart) / freq.QuadPart; + #endif */ + + return sresult_; + } + else { + sresult_.pathFound = false; + + end = std::chrono::system_clock::now(); + sresult_.time = static_cast(std::chrono::duration_cast(end - begin).count()) / 1000000000; + + /* for more accurate time calculation + #ifdef __linux__ + gettimeofday(&end, NULL); + sresult.time = (end.tv_sec - begin.tv_sec) + static_cast(end.tv_usec - begin.tv_usec) / 1000000; + #else + QueryPerformanceCounter(&end); + sresult.time = static_cast(end.QuadPart-begin.QuadPart) / freq.QuadPart; + #endif */ + + return sresult_; + } +} + +void LianSearch::update(const Node& current_node, Node &new_node, bool& successors, const Map& map) { + if (!checkLineSegment(map, *new_node.parent, new_node)) { + return; + } + if (settings_.pivotRadius > 0 && (new_node.i != map.goal_i || new_node.j != map.goal_j)) { + if (!checkPivotCircle(map, new_node)) { + return; + } + } + + auto range = search_tree_.findCloseRange(new_node); + for (auto it = range.first; it != range.second; ++it) { + Node *parent = it->second.parent; + if (parent == nullptr || *parent == current_node) { + return; + } + } + + if (distanceLookup_.size() > 1) { + tryIncreaseRadius(new_node); + } + search_tree_.addOpen(new_node); + successors = true; +} + +bool LianSearch::expand(const Node& curNode, const Map& map) { + std::size_t current_distance; + for (current_distance = 0; current_distance < distanceLookup_.size(); ++current_distance) { + if (distanceLookup_[current_distance] == curNode.radius) { + break; + } + } + + std::vector circle_nodes = circleNodes_[current_distance]; + + bool successors_are_fine = false; + Node *curNodeFromClose = search_tree_.findClose(curNode); + if (curNode.parent != nullptr) { + int node_straight_ahead = (int)round(curNode.angle * circle_nodes.size() / 360) % circle_nodes.size(); + double angle = fabs(curNode.angle - circle_nodes[node_straight_ahead].heading); + if ((angle <= 180 && angle <= settings_.angleLimit) || (angle > 180 && 360 - angle <= settings_.angleLimit)) { + int new_pos_i = curNode.i + circle_nodes[node_straight_ahead].i; + int new_pos_j = curNode.j + circle_nodes[node_straight_ahead].j; + if (map.CellOnGrid(new_pos_i, new_pos_j) && map.CellIsTraversable(new_pos_i, new_pos_j)) { + Node newNode = Node(new_pos_i, new_pos_j); + newNode.g = curNode.g + getCost(curNode.i, curNode.j, new_pos_i, new_pos_j); + newNode.angle = circle_nodes[node_straight_ahead].heading; + newNode.F = newNode.g + settings_.weight * getCost(new_pos_i, new_pos_j, map.goal_i, map.goal_j) + + settings_.curvatureHeuristicWeight * (float)settings_.distance * fabs(curNode.angle - newNode.angle); + newNode.radius = curNode.radius; + newNode.parent = curNodeFromClose; + + update(curNode, newNode, successors_are_fine, map); + } + } // now we will expand neighbors that are closest to the node that lies straight ahead + + std::vector candidates = std::vector{ node_straight_ahead, node_straight_ahead }; + bool limit1 = true; + bool limit2 = true; + while (++candidates[0] != --candidates[1] && (limit1 || limit2)) { // untill the whole circle is explored or we exessed anglelimit somewhere + if (candidates[0] >= circle_nodes.size()) { + candidates[0] = 0; + } + if (candidates[1] < 0) { + candidates[1] = circle_nodes.size() - 1; + } + + for (auto cand : candidates) { + double angle = fabs(curNode.angle - circle_nodes[cand].heading); + if ((angle <= 180 && angle <= settings_.angleLimit) || (angle > 180 && 360 - angle <= settings_.angleLimit)) { + int new_pos_i = curNode.i + circle_nodes[cand].i; + int new_pos_j = curNode.j + circle_nodes[cand].j; + + if (!map.CellOnGrid(new_pos_i, new_pos_j) || map.CellIsObstacle(new_pos_i, new_pos_j)) { + continue; + } + + Node newNode = Node(new_pos_i, new_pos_j); + newNode.g = curNode.g + getCost(curNode.i, curNode.j, new_pos_i, new_pos_j); + newNode.F = newNode.g + settings_.weight * getCost(new_pos_i, new_pos_j, map.goal_i, map.goal_j) + + settings_.curvatureHeuristicWeight * (float)settings_.distance * fabs(curNode.angle - newNode.angle); + newNode.radius = curNode.radius; + newNode.angle = circle_nodes[cand].heading; + newNode.parent = curNodeFromClose; + + update(curNode, newNode, successors_are_fine, map); + } + else if (cand == candidates[0]) { + limit1 = false; + } + else { + limit2 = false; + } + } + } + } + else { // when we do not have parent, we should explore all neighbors + int angle_position(-1), new_pos_i, new_pos_j; + for (auto& node : circle_nodes) { + new_pos_i = curNode.i + node.i; + new_pos_j = curNode.j + node.j; + angle_position++; + + if (!map.CellOnGrid(new_pos_i, new_pos_j) || map.CellIsObstacle(new_pos_i, new_pos_j)) { + continue; + } + + Node newNode = Node(new_pos_i, new_pos_j); + newNode.g = curNode.g + getCost(curNode.i, curNode.j, new_pos_i, new_pos_j); + newNode.F = newNode.g + settings_.weight * getCost(new_pos_i, new_pos_j, map.goal_i, map.goal_j); + newNode.radius = curNode.radius; + newNode.angle = circle_nodes[angle_position].heading; + newNode.parent = curNodeFromClose; + + update(curNode, newNode, successors_are_fine, map); + } + } + + // when we are near goal point, we should try to reach it + if (getCost(curNode.i, curNode.j, map.goal_i, map.goal_j) <= curNode.radius) { + double angle = curNode.parent != nullptr ? calcAngle(*curNode.parent, curNode, Node(map.goal_i, map.goal_j)) : 0.0; + + if (fabs(angle * 180 / std::numbers::pi) <= settings_.angleLimit) { + Node newNode = Node(map.goal_i, map.goal_j, + curNode.g + getCost(curNode.i, curNode.j, map.goal_i, map.goal_j), 0.0, + curNode.radius, curNodeFromClose, settings_.curvatureHeuristicWeight * settings_.distance, 0.0); + + update(curNode, newNode, successors_are_fine, map); + } + } + return successors_are_fine; +} + +bool LianSearch::tryIncreaseRadius(Node &node) { + Node *iterNode = &node; + std::size_t k = 0; + while (k < settings_.numOfParentsToIncreaseRadius) { + if (!(iterNode->parent != nullptr && iterNode->radius == iterNode->parent->radius)) { + break; + } + ++k; + iterNode = iterNode->parent; + } + int result_radius = iterNode->radius; + if (k == settings_.numOfParentsToIncreaseRadius) { + auto radiusIter = std::lower_bound(distanceLookup_.rbegin(), distanceLookup_.rend(), node.radius); + if ((radiusIter + 1) != distanceLookup_.rend()) { + result_radius = *(radiusIter + 1); + } + } + bool change = (result_radius != node.radius); + node.radius = result_radius; + return change; +} + +bool LianSearch::tryDecreaseRadius(Node& node) { + auto radiusIter = std::lower_bound(distanceLookup_.rbegin(), distanceLookup_.rend(), node.radius); + if (radiusIter == distanceLookup_.rbegin()) { + return false; + } + + node.radius = *(radiusIter - 1); + auto range = search_tree_.findCloseRange(node); + for (auto it = range.first; it != range.second; ++it) { + Node &nodeFromClose = it->second; + if (areFromSameSource(node, nodeFromClose)) { + nodeFromClose.radius = node.radius; + break; + } + } + return true; +} + +void LianSearch::makePrimaryPath(Node* curNode) { + while (curNode) { + hppath_.push_front(*curNode); + curNode = curNode->parent; + } +} + +bool LianSearch::checkAngle(const Node& dad, const Node& node, const Node& son) const { + double angle = calcAngle(dad, node, son) * 180 / std::numbers::pi; + if (fabs(angle) <= settings_.angleLimit) { + return true; + } + return false; +} + + +std::list LianSearch::smoothPath(const std::list& path, const Map& map) { + std::list new_path; + sresult_.pathLength = 0; + auto it = path.begin(); + auto curr_it = path.begin(); + Node start_section = path.front(); + Node end_section = path.front(); + bool first = true; + Node previous = *it++; + while (end_section != path.back()) { + for (it; it != path.end(); ++it) { + auto& next = ++it; + --it; + if (!first && !checkAngle(previous, start_section, *it)) { + continue; + } + if (((next != path.end() && checkAngle(start_section, *it, *next)) + || next == path.end()) && checkLineSegment(map, start_section, *it)) { + end_section = *it; + curr_it = it; + } + } + sresult_.pathLength += (double)getCost(previous.i, previous.j, start_section.i, start_section.j); + new_path.push_back(start_section); + previous = start_section; + first = false; + start_section = end_section; + it = ++curr_it; + } + sresult_.pathLength += (double)getCost(previous.i, previous.j, end_section.i, end_section.j); + new_path.push_back(end_section); + return new_path; +} + +void LianSearch::makeSecondaryPath() { + ; + auto it = hppath_.begin(); + Node parent = *it++; + while (it != hppath_.end()) { + std::vector lineSegment = calculateLineSegment(parent, *it); + std::reverse(std::begin(lineSegment), std::end(lineSegment)); + lppath_.insert(lppath_.begin(), ++lineSegment.begin(), lineSegment.end()); + parent = *it++; + } + lppath_.push_front(hppath_.back()); + std::reverse(std::begin(lppath_), std::end(lppath_)); +} + + +double LianSearch::makeAngles() { + angles_.clear(); + double max_angle = 0; + sresult_.accumAngle = 0; + auto pred = hppath_.begin(); + auto current = ++hppath_.begin(); + auto succ = ++(++hppath_.begin()); + + while (succ != hppath_.end()) { + double angle = calcAngle(*pred++, *current++, *succ++); + angle = angle * 180 / std::numbers::pi; + if (angle > max_angle) max_angle = angle; + sresult_.accumAngle += angle; + angles_.push_back(angle); + } + std::reverse(std::begin(angles_), std::end(angles_)); + return max_angle; +} diff --git a/src/liansearch.h b/src/liansearch.h new file mode 100644 index 0000000..55f17f4 --- /dev/null +++ b/src/liansearch.h @@ -0,0 +1,64 @@ +#ifndef LIANSEARCH_H +#define LIANSEARCH_H + +#include "structures/map.h" +#include "structures/node.h" +#include "structures/search_tree.h" +#include "structures/search_result.h" +#include "structures/config.h" +#include "log/logger.h" + +#include +#include +#include +#include +#include +#include + +class LianSearch { +public: + LianSearch(SearchParams settings); + + SearchResult startSearch(std::shared_ptr logger, const Map &map); + +private: + SearchParams settings_; + SearchResult sresult_; + const std::vector distanceLookup_; // Possible step sizes, from largest to smallest + std::vector > circleNodes_; // Virtual nodes that create circle around the cell + std::vector > pivotCircle_; // Vector of nodes (shifts) for pivot security check + std::vector angles_; + std::list lppath_, hppath_; // Final path in two representations + SearchTree search_tree_; // consists of `open` (list of nodes waiting for expanding) \ + // and `close` (list of nodes that were already expanded) + + // Called on construction + std::vector buildDistances() const; + + // Method that calculate Bresenham's Circle (center - (0, 0)) and writing list of created nodes to circleNodes + void calculateCircle(int radius); // Radius - radius of the circle in cells + + void calculatePivotCircle(); + static std::vector calculateLineSegment(const Node &start, const Node &goal); // Method builds Bresenham's Line + static bool checkLineSegment(const Map &map, const Node &start, const Node &goal); // Method builds Bresenham's Line and check it for impassable parts + + // check that there are no obstacle in a safety radius from a turn point + bool checkPivotCircle(const Map &map, const Node ¢er); + + static double calcAngle(const Node &dad, const Node &node, const Node &son); + bool checkAngle(const Node &dad, const Node &node, const Node &son) const; + + bool stopCriterion(); // Check for the ending criteria. Return true if the algorithm should be stopped + + bool tryIncreaseRadius(Node &node); + bool tryDecreaseRadius(Node &node); + + void update(const Node& current_node, Node &new_node, bool &successors, const Map &map); + bool expand(const Node& curNode, const Map &map); + std::list smoothPath(const std::list& path, const Map& map); + void makePrimaryPath(Node* curNode); + void makeSecondaryPath(); + double makeAngles(); +}; + +#endif // LIANSEARCH_H diff --git a/src/log/gl_const.h b/src/log/gl_const.h new file mode 100644 index 0000000..adb1c78 --- /dev/null +++ b/src/log/gl_const.h @@ -0,0 +1,13 @@ +#ifndef GL_CONST_H +#define GL_CONST_H + +constexpr auto CN_EPSILON = 1e-8; + +constexpr auto CN_LOG = "_log"; + + +//Default values of parameters + + + +#endif diff --git a/src/log/logger.cpp b/src/log/logger.cpp new file mode 100644 index 0000000..05d4618 --- /dev/null +++ b/src/log/logger.cpp @@ -0,0 +1,49 @@ +#include + +#include "logger.h" + + +Logger::Logger(int loglvl, const std::string &configFileName) : doc_(configFileName.c_str()), logLevel_(loglvl) { + if (!doc_.LoadFile()) { + throw std::runtime_error("Error opening XML-file in getLog"); + } + + std::string value = configFileName; + size_t dotPos = value.find_last_of("."); + + if (dotPos != std::string::npos) { + value.insert(dotPos, CN_LOG); + } + else { + value += CN_LOG; + } + logFileName_ = value; + + auto root = doc_.FirstChild("root"); + + auto element = root->InsertEndChild(TiXmlElement(Logger::Tags::log)); + + element->InsertEndChild(TiXmlElement(Logger::Tags::summary)); + + if (logLevel_ > Logger::Levels::tiny) { + element->InsertEndChild(TiXmlElement(Logger::Tags::path)); + + element->InsertEndChild(TiXmlElement(Logger::Tags::angles)); + + element->InsertEndChild(TiXmlElement(Logger::Tags::lpLevel)); + + element->InsertEndChild(TiXmlElement(Logger::Tags::hpLevel)); + } + + if (logLevel_ >= Logger::Levels::med) { + element->InsertEndChild(TiXmlElement(Logger::Tags::lowLevel)); + } + + if (logLevel_ >= Logger::Levels::iter) { + element->InsertEndChild(TiXmlElement(Logger::Tags::iterations)); + } +} + +Logger::~Logger() { + doc_.SaveFile(logFileName_.c_str()); +} diff --git a/src/log/logger.h b/src/log/logger.h new file mode 100644 index 0000000..ea222b8 --- /dev/null +++ b/src/log/logger.h @@ -0,0 +1,82 @@ +#pragma once + +#include + +#include "gl_const.h" +#include "tinyxml/tinyxml.h" + +class Logger { +public: + Logger(int loglvl, const std::string& configFileName); + Logger(const Logger& oth) = delete; + Logger(Logger&& oth) = delete; + Logger& operator=(const Logger& oth) = delete; + Logger& operator=(Logger&& oth) = delete; + ~Logger(); + + template + TiXmlElement* logSpace(const std::string& tagName) { + if (logLevel_ < loglvl) { + return nullptr; + } + return doc_.FirstChild(Logger::Tags::root)->FirstChild(Logger::Tags::log)->FirstChildElement(tagName.c_str()); + } + + struct Tags { + static constexpr const char + *root = "root", + *row = "row", + *lpLevel = "lpLevel", + *hpLevel = "hpLevel", + *log = "log", + *lowLevel = "lowLevel", + *mapFileName = "mapFileName", + *summary = "summary", + *iterations = "iterations", + *section = "section", + *step = "step", + *open = "open", + *close = "close", + *path = "path", + *time = "time", + *y = "Y", + *F = "F", + *g = "g", + *angle = "angle", + *angles = "angles", + *finishX = "finish.x", + *finishY = "finish.y", + *length = "length", + *lengthScaled = "length_scaled", + *node = "node", + *nodesCreated = "nodescreated", + *number = "number", + *numberOfSteps = "numberofsteps", + *pathFound = "pathfound", + *parentX = "parent_x", + *parentY = "parent_y", + *sections = "sections", + *startX = "start.x", + *startY = "start.y", + *value = "value", + *tagTrue = "true", + *tagFalse = "false", + *maxAngle = "max_angle", + *accumAngle = "accum_angle"; + }; + + struct Levels { + static constexpr int + no = 0, + tiny = 1, + high = 2, + iter = 3, + med = 4, + low = 5; + }; + +private: + int logLevel_; + std::string logFileName_; + TiXmlDocument doc_; +}; diff --git a/src/log/xml_utils.cpp b/src/log/xml_utils.cpp new file mode 100644 index 0000000..3ddd26a --- /dev/null +++ b/src/log/xml_utils.cpp @@ -0,0 +1,29 @@ +#include "xml_utils.h" + +template <> +bool serialize(TiXmlElement* element) { + std::string buf(element->GetText()); + if (buf == "true") { + return true; + } + if (buf == "false") { + return false; + } + throw std::runtime_error("Expected true or false. Got: " + buf); +} + +TiXmlElement* getElement( + TiXmlElement* section, const std::string& elementName, + std::optional sectionName) { + TiXmlElement* element = section->FirstChildElement(elementName.c_str()); + if (!element) { + std::stringstream errorStream; + errorStream << "Error! No '" << elementName << "' element found"; + if (sectionName) { + errorStream << " inside '" << (*sectionName) << "' section."; + } + throw std::runtime_error(errorStream.str()); + } + + return element; +} diff --git a/src/log/xml_utils.h b/src/log/xml_utils.h new file mode 100644 index 0000000..d71b7d3 --- /dev/null +++ b/src/log/xml_utils.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "tinyxml/tinyxml.h" + +template +T serialize(TiXmlElement* element) { + std::stringstream ss; + ss << std::string(element->GetText()); + T res; + ss >> res; + return res; +} + +template <> +bool serialize(TiXmlElement* element); + +template +T serializeOrElse(TiXmlElement* element, const T& orElse) { + if (!element) { + return orElse; + } + return serialize(element); +} + +template +std::optional serializeOpt(TiXmlElement* element) { + if (!element) { + return std::nullopt; + } + return serialize(element); +} + +template +std::vector serializeVector(TiXmlElement* element) { + std::stringstream ss; + ss << std::string(element->GetText()); + T cell; + std::vector res; + while (ss >> cell) { + res.push_back(cell); + } + + return res; +} + +TiXmlElement* getElement( + TiXmlElement* section, const std::string& elementName, + std::optional sectionName = std::nullopt); diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..0ed7e1c --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,17 @@ +#include"structures/mission.h" + +#include + +int main(int argc, char* argv[]) { + if (argc != 2) { + return 1; + } + Mission Mission(argv[1]); + + Mission.startSearch(); + + Mission.printSearchResultsToConsole(); + + Mission.saveSearchResultsToLog(); + return 0; +} diff --git a/src/structures/config.cpp b/src/structures/config.cpp new file mode 100644 index 0000000..ef0cba9 --- /dev/null +++ b/src/structures/config.cpp @@ -0,0 +1,90 @@ +#include "config.h" + +#include "tinyxml/tinyxml.h" +#include "../log/xml_utils.h" + +namespace { + constexpr auto tagRoot = "root"; + constexpr auto tagAlgorithm = "algorithm"; + constexpr auto tagAngleLimit = "anglelimit"; + constexpr auto tagDistance = "distance"; + constexpr auto tagWeight = "hweight"; + constexpr auto tagStepLimit = "steplimit"; + constexpr auto tagCurvHeurWeight = "curvatureHeuristicWeight"; + constexpr auto tagSmoother = "postsmoothing"; + constexpr auto tagPivotCircle = "pivotCircleRadius"; + constexpr auto tagDistanceMin = "distancemin"; + constexpr auto tagDecriDistFactor = "decreaseDistanceFactor"; + constexpr auto tagNumOfParentsToIncRadius = "numOfParentsToIncreaseRadius"; + constexpr auto tagOptions = "options"; + constexpr auto tagLogLevel = "loglevel"; + + constexpr auto defaultWeight = 2; + + constexpr auto CN_PTD_DDF = 1;// Value that divides distance in order to decrease radius of the step + + constexpr auto CN_PTD_NOFPTOINCRAD = 2;// Number of parent vertecies that must have the same radius in order to start the increase of current radius +} + +const SearchParams& Config::params() const { + return params_; +} + +Config::Config(const char* fileName) { + TiXmlDocument doc(fileName); + if (!doc.LoadFile()) { + throw std::runtime_error("Error openning input XML file."); + } + + auto root = doc.FirstChildElement(tagRoot); + if (!root) { + std::ostringstream err_oss; + err_oss << "No '" << tagRoot << "' element found in XML file."; + throw std::runtime_error(err_oss.str()); + } + + auto algorithm = getElement(root, tagAlgorithm, tagRoot); + + TiXmlElement* element; + + // element = getElement(root, CNS_TAG_MAP); + // mapFileName_ = serialize(element); + + element = getElement(algorithm, tagAngleLimit, tagAlgorithm); + params_.angleLimit = std::min(std::abs(serialize(element)), 180.f); + + element = getElement(algorithm, tagDistance, tagAlgorithm); + params_.distance = std::abs(serialize(element)); + + // element = algorithm->FirstChildElement("isELian"); + // params_.doELian = serializeOrElse(element, 0); + + element = algorithm->FirstChildElement(tagWeight); + params_.weight = serializeOrElse(element, defaultWeight); + + element = algorithm->FirstChildElement(tagStepLimit); + params_.stepLimit = serializeOrElse(element, 0); + + element = algorithm->FirstChildElement(tagCurvHeurWeight); + params_.curvatureHeuristicWeight = serializeOrElse(element, 0.0); + + element = algorithm->FirstChildElement(tagSmoother); + params_.postsmoother = serializeOrElse(element, false); + + element = algorithm->FirstChildElement(tagDecriDistFactor); + params_.decreaseDistanceFactor = serializeOrElse(element, CN_PTD_DDF); + + element = algorithm->FirstChildElement(tagDistanceMin); + params_.distanceMin = serializeOrElse(element, params_.distance * 0.1); + + element = algorithm->FirstChildElement(tagPivotCircle); + params_.pivotRadius = std::abs(serializeOrElse(element, 0.0)); + + element = algorithm->FirstChildElement(tagNumOfParentsToIncRadius); + params_.numOfParentsToIncreaseRadius = serializeOrElse(element, CN_PTD_NOFPTOINCRAD); + + TiXmlElement* options = getElement(root, tagOptions); + + element = getElement(options, tagLogLevel, tagOptions); + params_.logLevel = serialize(element); +} diff --git a/src/structures/config.h b/src/structures/config.h new file mode 100644 index 0000000..2c21a2f --- /dev/null +++ b/src/structures/config.h @@ -0,0 +1,35 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#include "tinyxml/tinyxml.h" +#include "tinyxml/tinystr.h" + +#include +#include +#include +#include + +struct SearchParams { + float angleLimit; + int distance; + float weight; + unsigned int stepLimit; + float curvatureHeuristicWeight; + bool postsmoother; + float decreaseDistanceFactor; + int distanceMin; + double pivotRadius; + int numOfParentsToIncreaseRadius; + int logLevel; +}; + +class Config { +public: + Config(const char* FileName); + + const SearchParams& params() const; +private: + SearchParams params_; +}; + +#endif diff --git a/src/structures/map.cpp b/src/structures/map.cpp new file mode 100644 index 0000000..f23e7eb --- /dev/null +++ b/src/structures/map.cpp @@ -0,0 +1,107 @@ +#include "../log/xml_utils.h" +#include "map.h" + +namespace { + constexpr auto tagRoot = "root"; + constexpr auto tagMap = "map"; + constexpr auto tagHeight = "height"; + constexpr auto tagWidth = "width"; + constexpr auto tagStartX = "startx"; + constexpr auto tagStartY = "starty"; + constexpr auto tagFinishX = "finishx"; + constexpr auto tagFinishY = "finishy"; + constexpr auto tagCellSize = "cellsize"; + constexpr auto tagGrid = "grid"; + constexpr auto tagRow = "row"; + constexpr auto tagObstacle = 1; +} + +std::vector& Map::operator[](int i) { + return grid_[i]; +} +const std::vector& Map::operator[](int i) const { + return grid_[i]; +} + +bool Map::CellIsTraversable(int curr_i, int curr_j) const { + return (grid_[curr_i][curr_j] != tagObstacle); +} + +bool Map::CellIsObstacle(int curr_i, int curr_j) const { + return (grid_[curr_i][curr_j] == tagObstacle); +} + +bool Map::CellOnGrid(int curr_i, int curr_j) const { + return (curr_i < height_ && curr_i >= 0 && curr_j < width_ && curr_j >= 0); +} + +int Map::getHeight() const { + return height_; +} + +int Map::getWidth() const { + return width_; +} + +double Map::getCellSize() const { + return cellSize_; +} + +Map::Map(const char* FileName) { + TiXmlDocument doc(FileName); + if (!doc.LoadFile()) { + throw std::runtime_error("Error openning input XML file"); + } + + auto root = doc.FirstChildElement(tagRoot); + if (!root) { + std::ostringstream err_oss; + err_oss << "Error! No '" << tagRoot << "' element found in XML file"; + throw std::runtime_error(err_oss.str()); + } + + TiXmlElement *map = getElement(root, tagMap, tagRoot); + + auto element = getElement(map, tagHeight, tagMap); + height_ = std::abs(serialize(element)); + + element = getElement(map, tagWidth, tagMap); + width_ = std::abs(serialize(element)); + + element = getElement(map, tagCellSize, tagMap); + cellSize_ = std::abs(serialize(element)); + + + element = getElement(map, tagStartX, tagMap); + start_j = serialize(element); + + element = getElement(map, tagStartY, tagMap); + start_i = serialize(element); + + element = getElement(map, tagFinishX, tagMap); + goal_j = serialize(element); + + element = getElement(map, tagFinishY, tagMap); + goal_i = serialize(element); + + element = getElement(map, tagGrid, tagMap); + grid_.reserve(height_); + + element = element->FirstChildElement(tagRow); + + for (int curY = 0; curY < height_; ++curY) { + if (!element) { + std::ostringstream err_oss; + err_oss << "Not enough '" << tagRow << "' in '" << tagGrid << "' given."; + throw std::runtime_error(err_oss.str()); + } + + grid_.push_back(serializeVector(element)); + if (grid_.back().size() != width_) { + std::ostringstream err_oss; + err_oss << "Wrong amount of cells in '" << tagRow << "' " << curY << " given."; + throw std::runtime_error(err_oss.str()); + } + element = element->NextSiblingElement(); + } +} diff --git a/map.h b/src/structures/map.h old mode 100755 new mode 100644 similarity index 54% rename from map.h rename to src/structures/map.h index f46d5b4..d003b48 --- a/map.h +++ b/src/structures/map.h @@ -1,10 +1,10 @@ #ifndef MAP_H #define MAP_H -#include "gl_const.h" #include "tinyxml/tinyxml.h" #include "tinyxml/tinystr.h" +#include #include #include #include @@ -12,29 +12,25 @@ class Map { - private: - int ** Grid; - int height, width; - double CellSize; + std::vector> grid_; + int height_, width_; + double cellSize_; public: - Map(); - ~Map(); - bool getMap(const char* FileName); + Map(const char* FileName); - bool CellIsTraversable (int curr_i, int curr_j) const; - bool CellOnGrid (int curr_i, int curr_j) const; + bool CellIsTraversable(int curr_i, int curr_j) const; + bool CellOnGrid(int curr_i, int curr_j) const; bool CellIsObstacle(int curr_i, int curr_j) const; - int* operator [] (int i); - const int* operator [] (int i) const; + std::vector& operator[](int i); + const std::vector& operator[](int i) const; int getWidth() const; int getHeight() const; double getCellSize() const; - int start_i, start_j; int goal_i, goal_j; }; diff --git a/src/structures/mission.cpp b/src/structures/mission.cpp new file mode 100644 index 0000000..0979f30 --- /dev/null +++ b/src/structures/mission.cpp @@ -0,0 +1,159 @@ +#include "mission.h" + +namespace { + void saveSummaryToLog(std::shared_ptr logger, const std::list& path, int numberofsteps, int nodescreated, float length, float length_scaled, + long double time, float max_angle, float accum_angle, int sections) { + auto space = logger->logSpace(Logger::Tags::summary); + if (!space) { + return; + } + + std::string timeValue; + std::stringstream stream; + stream << time; + stream >> timeValue; + stream.clear(); + stream.str(""); + + if (path.size() == 0) { + space->SetAttribute(Logger::Tags::pathFound, Logger::Tags::tagFalse); + } + else { + space->SetAttribute(Logger::Tags::pathFound, Logger::Tags::tagTrue); + } + + space->SetAttribute(Logger::Tags::numberOfSteps, numberofsteps); + space->SetAttribute(Logger::Tags::nodesCreated, nodescreated); + space->SetAttribute(Logger::Tags::sections, sections); + space->SetDoubleAttribute(Logger::Tags::length, length); + space->SetDoubleAttribute(Logger::Tags::lengthScaled, length_scaled); + space->SetAttribute(Logger::Tags::time, timeValue.c_str()); + space->SetDoubleAttribute(Logger::Tags::maxAngle, max_angle); + space->SetDoubleAttribute(Logger::Tags::accumAngle, accum_angle); + } + + void savePathToLog(std::shared_ptr logger, const std::list& path, const std::vector& angles) { + auto space = logger->logSpace(Logger::Tags::lpLevel); + if (!space) { + return; + } + + int64_t index = 0; + for (auto iter = path.cbegin(); iter != path.cend(); ++iter, ++index) { + TiXmlElement point(Logger::Tags::node); + point.SetAttribute(Logger::Tags::number, index); + point.SetAttribute(Logger::Tags::parentX, iter->j); + point.SetAttribute(Logger::Tags::y, iter->i); + space->InsertEndChild(point); + } + + if (angles.size() == 0) return; + + space = logger->logSpace(Logger::Tags::angles); + + for (auto iter = angles.crbegin(); iter != angles.crend(); ++iter) { + TiXmlElement point(Logger::Tags::angle); + point.SetAttribute(Logger::Tags::number, (iter - angles.crbegin())); + point.SetDoubleAttribute(Logger::Tags::value, *iter); + space->InsertEndChild(point); + } + } + + void saveMapToLog(std::shared_ptr logger, const Map& map, const std::list& path) { + auto space = logger->logSpace(Logger::Tags::path); + if (!space) { + return; + } + + std::stringstream stream; + std::string text, value; + std::vector curLine(map.getWidth(), 0); + + for (int i = 0; i < map.getHeight(); i++) { + TiXmlElement msg(Logger::Tags::row); + msg.SetAttribute(Logger::Tags::number, i); + text = ""; + + for (auto iter = path.begin(); iter != path.end(); ++iter) { + if (iter->i == i) { + curLine[iter->j] = 1; + } + } + + for (int j = 0; j < map.getWidth(); j++) { + if (curLine[j] != 1) { + stream << map[i][j]; + stream >> value; + stream.clear(); + stream.str(""); + text = text + value + " "; + } + else { + text = text + "*" + " "; + curLine[j] = 0; + } + } + + msg.InsertEndChild(TiXmlText(text.c_str())); + space->InsertEndChild(msg); + } + } + + void saveToLogHpLevel(std::shared_ptr logger, const std::list& path) { + auto space = logger->logSpace(Logger::Tags::hpLevel); + if (!space) { + return; + } + int partnumber = 0; + auto it = path.cbegin(); + + for (auto iter = ++path.cbegin(); iter != path.cend(); ++iter, ++it) { + TiXmlElement part(Logger::Tags::section); + part.SetAttribute(Logger::Tags::number, partnumber); + part.SetAttribute(Logger::Tags::startX, it->j); + part.SetAttribute(Logger::Tags::startY, it->i); + part.SetAttribute(Logger::Tags::finishX, iter->j); + part.SetAttribute(Logger::Tags::finishY, iter->i); + part.SetDoubleAttribute(Logger::Tags::length, iter->g - it->g); + space->InsertEndChild(part); + ++partnumber; + } + } +} + +Mission::Mission(const char* fName) + : config(fName) + , map(fName) + , search(config.params()) + , logger(std::make_shared(config.params().logLevel, std::string(fName))) + , fileName(fName) {} + +void Mission::startSearch() { + sr = search.startSearch(logger, map); +} + +void Mission::printSearchResultsToConsole() { + std::cout << "Path "; + if (!sr.pathFound) + std::cout << "NOT "; + std::cout << "found!" << std::endl; + std::cout << "nodescreated=" << sr.nodesCreated << std::endl; + std::cout << "numberofsteps=" << sr.numberOfSteps << std::endl; + if (sr.pathFound) { + std::cout << "pathlength=" << sr.pathLength << std::endl; + std::cout << "length_scaled=" << sr.pathLength * map.getCellSize() << std::endl; + } + std::cout << "time=" << sr.time << std::endl; +} + +void Mission::saveSearchResultsToLog() { + saveSummaryToLog(logger, sr.hpPath, sr.numberOfSteps, sr.nodesCreated, sr.pathLength, sr.pathLength * map.getCellSize(), + sr.time, sr.maxAngle, sr.accumAngle, sr.sections); + + if (sr.pathFound) { + savePathToLog(logger, sr.lpPath, sr.angles); + saveMapToLog(logger, map, sr.lpPath); + saveToLogHpLevel(logger, sr.hpPath); + } +} + diff --git a/mission.h b/src/structures/mission.h old mode 100755 new mode 100644 similarity index 52% rename from mission.h rename to src/structures/mission.h index 8429779..eb2fea1 --- a/mission.h +++ b/src/structures/mission.h @@ -2,35 +2,29 @@ #define MISSION_H #include "config.h" -#include "liansearch.h" +#include "../liansearch.h" +#include "../log/logger.h" #include "map.h" -#include "search.h" -#include "searchresult.h" -#include "xmllogger.h" - +#include "search_result.h" #include +#include class Mission { public: Mission(const char* fName); - ~Mission(); - bool getMap(); - bool getConfig(); - bool createLog(); - void createSearch(); void startSearch(); void printSearchResultsToConsole(); void saveSearchResultsToLog(); private: - Map map; - Config config; + Config config; + Map map; - Search *search; - Logger *logger; + LianSearch search; + std::shared_ptr logger; const char* fileName; diff --git a/src/structures/node.h b/src/structures/node.h new file mode 100644 index 0000000..69d77ef --- /dev/null +++ b/src/structures/node.h @@ -0,0 +1,81 @@ +#ifndef NODE_H +#define NODE_H + +#include +#include +#include +#include + +struct circleNode +{ + int i, j; + double heading; + double cost; + + circleNode(): i(-1), j(-1), heading(-1), cost(0) {} + circleNode(int i, int j, double cost): i(i), j(j), heading(-1), cost(cost) {} + circleNode(int i, int j, double heading, double cost): i(i), j(j), heading(heading), cost(cost) {} +}; + +struct Node { +private: + static constexpr int defaultRadius = 4; +public: + Node* parent; + + int i, j; + int radius; + float F; + float g; + + double angle; + + Node() : parent(nullptr), i(-1), j(-1), radius(defaultRadius), + F(std::numeric_limits::infinity()), g(std::numeric_limits::infinity()), angle(0) {} + + Node(int x, int y, float g_=std::numeric_limits::infinity(), float h_=std::numeric_limits::infinity(), + int radius_= defaultRadius, Node *parent_=nullptr, float cweightdist_=0, double ang_=0) : + parent(parent_), i(x), j(y), radius(radius_), g(g_), angle(ang_) { + if (parent) { + F = g + h_ + cweightdist_ * (float)fabs(ang_ - parent->angle); + } else { + F = g + h_; + } + } + + ~Node() { + parent = nullptr; + } + + inline Node& operator=(const Node& other) = default; + + inline bool operator==(const Node& other) const { + return i == other.i && j == other.j; + } + + inline bool operator!=(const Node& other) const { + return !(*this == other); + } + + // Warning: Order in which equal nodes would be is undefined and platform-specific. + // Result in different platforms may differ. + inline bool operator<(const Node& other) const { + return F > other.F || (F == other.F && g < other.g); + } +}; + +template<> +struct std::hash { + std::size_t operator()(const Node& node) const + { + return (size_t)node.i << 32 | (size_t)node.j; + } +}; + +inline bool areFromSameSource(const Node& lhs, const Node& rhs) { + return ((!lhs.parent && !rhs.parent) + || (lhs.parent && rhs.parent + && lhs.parent->i == rhs.parent->i && lhs.parent->j == rhs.parent->j)); +} + +#endif diff --git a/src/structures/search_result.h b/src/structures/search_result.h new file mode 100644 index 0000000..0458251 --- /dev/null +++ b/src/structures/search_result.h @@ -0,0 +1,22 @@ +#ifndef SEARCH_RESULT_H +#define SEARCH_RESULT_H + +#include "node.h" +#include + +struct SearchResult { + + bool pathFound{}; + float pathLength{}; + std::list hpPath; + std::list lpPath; + unsigned int nodesCreated{}; + unsigned int numberOfSteps{}; + std::vector angles; + float accumAngle{}; + double time{}; + float maxAngle{}; + int sections{}; +}; + +#endif // SEARCH_RESULT_H diff --git a/src/structures/search_tree.cpp b/src/structures/search_tree.cpp new file mode 100644 index 0000000..244ee29 --- /dev/null +++ b/src/structures/search_tree.cpp @@ -0,0 +1,135 @@ +#include "search_tree.h" + +SearchTree::SearchTree() {} + +SearchTree::~SearchTree() = default; + +size_t SearchTree::sizeOpen() const { + return open_.size(); +} + +size_t SearchTree::sizeClose() const { + return close_.size(); +} + +bool SearchTree::empty() const { + return open_.empty(); +} + +void SearchTree::addOpen(const Node &new_node) { + open_.push(new_node); + return; +} + +std::optional SearchTree::getOpen() { + while (!open_.empty()) { + Node min1 = open_.top(); + open_.pop(); + + if (close_.find(min1) == close_.end()) { + return std::make_optional(min1); + } + } + return {}; +} + +void SearchTree::addClose(const Node &new_node) { + close_.insert({new_node, new_node}); +} + +Node *SearchTree::findClose(const Node &key) { + auto node_iter = close_.find(key); + return (node_iter != close_.end()) ? &node_iter->second : nullptr; +} + +std::pair SearchTree::findCloseRange(const Node &key) { + return close_.equal_range(key); +} + +void SearchTree::writeToXml(TiXmlNode *child) const { + Node min; + min.F = std::numeric_limits::infinity(); + for (const auto &element: rows_) { + if (element.empty() || element.begin()->F > min.F) continue; + if (element.begin()->F != min.F || element.begin()->g >= min.g) { + min = element.front(); + } + } + if (min.F != std::numeric_limits::infinity()) { + TiXmlElement element(Logger::Tags::node); + element.SetAttribute(Logger::Tags::parentX, min.j); + element.SetAttribute(Logger::Tags::y, min.i); + element.SetDoubleAttribute(Logger::Tags::F, min.F); + element.SetDoubleAttribute(Logger::Tags::g, min.g); + element.SetAttribute(Logger::Tags::parentX, min.parent->j); + element.SetAttribute(Logger::Tags::parentY, min.parent->i); + child->InsertEndChild(element); + } + + for (const auto &i: rows_) { + if (i.empty()) continue; + for (const auto &it: i) { + if (it != min) { + TiXmlElement element1(Logger::Tags::node); + element1.SetAttribute(Logger::Tags::parentX, it.j); + element1.SetAttribute(Logger::Tags::y, it.i); + element1.SetDoubleAttribute(Logger::Tags::F, it.F); + element1.SetDoubleAttribute(Logger::Tags::g, it.g); + if (it.g > 0) { + element1.SetAttribute(Logger::Tags::parentX, it.parent->j); + element1.SetAttribute(Logger::Tags::parentY, it.parent->i); + } + child->InsertEndChild(element1); + } + } + } +} + +void SearchTree::saveToLogOpenAndClose(std::shared_ptr &logger) const { + auto space = logger->logSpace(Logger::Tags::lowLevel); + if (!space) { + return; + } + + int iterate = 0; + TiXmlNode *child = nullptr, *curNode = space; + + while ((child = curNode->IterateChildren(child))) { + iterate++; + } + + { + TiXmlElement element(Logger::Tags::step); + element.SetAttribute(Logger::Tags::number, iterate); + curNode->InsertEndChild(element); + curNode = curNode->LastChild(); + } + + { + + TiXmlElement element(Logger::Tags::open); + curNode->InsertEndChild(element); + child = curNode->LastChild(); + } + + writeToXml(child); + + { + TiXmlElement element(Logger::Tags::close); + curNode->InsertEndChild(element); + child = curNode->LastChild(); + } + + for (const auto &it: close_) { + TiXmlElement element(Logger::Tags::node); + element.SetAttribute(Logger::Tags::parentX, it.second.j); + element.SetAttribute(Logger::Tags::y, it.second.i); + element.SetDoubleAttribute(Logger::Tags::F, it.second.F); + element.SetDoubleAttribute(Logger::Tags::g, it.second.g); + if (it.second.g > 0) { + element.SetAttribute(Logger::Tags::parentX, it.second.parent->j); + element.SetAttribute(Logger::Tags::parentY, it.second.parent->i); + } + child->InsertEndChild(element); + } +} diff --git a/src/structures/search_tree.h b/src/structures/search_tree.h new file mode 100644 index 0000000..6212221 --- /dev/null +++ b/src/structures/search_tree.h @@ -0,0 +1,44 @@ +#ifndef OPEN_LIST_H +#define OPEN_LIST_H + +#include "node.h" +#include "map.h" +#include "../log/logger.h" +#include "tinyxml/tinyxml.h" +#include "tinyxml/tinystr.h" + +#include +#include +#include +#include +#include + +typedef std::unordered_multimap::iterator close_iterator; + +class SearchTree { +public: + SearchTree(); + + ~SearchTree(); + + size_t sizeOpen() const; + size_t sizeClose() const; + bool empty() const; + + void addOpen(const Node& new_node); + std::optional getOpen(); + + void addClose(const Node& new_node); + Node* findClose(const Node &key); + std::pair findCloseRange(const Node &key); +// void pop(Node min); + + void writeToXml(TiXmlNode *child) const; + void saveToLogOpenAndClose(std::shared_ptr &logger) const; +private: + std::vector> rows_; + std::priority_queue open_; + std::unordered_multimap close_; // Close: list of nodes that were already expanded +}; + +#endif // OPEN_LIST_H diff --git a/xmllogger.cpp b/xmllogger.cpp deleted file mode 100755 index 73254d1..0000000 --- a/xmllogger.cpp +++ /dev/null @@ -1,285 +0,0 @@ -#include"xmllogger.h" - -XmlLogger::XmlLogger(float loglvl) { - loglevel = loglvl; - logFileName = ""; - doc = 0; -} - -XmlLogger::~XmlLogger() { - if (doc) { - doc->Clear(); - delete doc; - } -} - -bool XmlLogger::getLog(const char *FileName) { - std::string value; - TiXmlDocument doc_xml(FileName); - - if(!doc_xml.LoadFile()) { - std::cout << "Error opening XML-file in getLog"; - return false; - } - - value = FileName; - size_t dotPos = value.find_last_of("."); - - if(dotPos != std::string::npos){ - value.insert(dotPos,CN_LOG); - } else { - value+=CN_LOG; - } - - logFileName = value; - doc_xml.SaveFile(logFileName.c_str()); - - doc = new TiXmlDocument(logFileName.c_str()); - doc->LoadFile(); - - TiXmlElement *msg; - TiXmlElement *root; - - root = doc->FirstChildElement(CNS_TAG_ROOT); - TiXmlElement *log = new TiXmlElement(CNS_TAG_LOG); - root->LinkEndChild(log); - - msg = new TiXmlElement(CNS_TAG_MAPFN); - msg->LinkEndChild(new TiXmlText(FileName)); - log->LinkEndChild(msg); - - msg = new TiXmlElement(CNS_TAG_SUM); - log->LinkEndChild(msg); - - if (loglevel > CN_LOGLVL_TINY) { - - TiXmlElement* path = new TiXmlElement(CNS_TAG_PATH); - log->LinkEndChild(path); - - TiXmlElement *angles = new TiXmlElement(CNS_TAG_ANGLES); - log->LinkEndChild(angles); - - TiXmlElement *lplevel = new TiXmlElement(CNS_TAG_LPLEVEL); - log->LinkEndChild(lplevel); - - TiXmlElement *hplevel = new TiXmlElement(CNS_TAG_HPLEVEL); - log->LinkEndChild(hplevel); - } - - if (loglevel >= CN_LOGLVL_MED) { - TiXmlElement *lowlevel = new TiXmlElement(CNS_TAG_LOWLEVEL); - log->LinkEndChild(lowlevel); - } - - if (loglevel - CN_LOGLVL_ITER < 0.001) { - TiXmlElement *iters = new TiXmlElement(CNS_TAG_ITERS); - log->LinkEndChild(iters); - } - - return true; -} - -void XmlLogger::writeToLogIter(int closeSize, const Node &curNode) { - int iterate = 0; - TiXmlElement *element = new TiXmlElement(CNS_TAG_STEP); - TiXmlNode *child = 0, *iters = doc->FirstChild(CNS_TAG_ROOT); - iters = iters->FirstChild(CNS_TAG_LOG)->FirstChild(CNS_TAG_ITERS); - - // while (child = iters->IterateChildren(child)) - //iterate++; - - //element->SetAttribute(CNS_TAG_ATTR_NUM, iterate); - element->SetAttribute(CNS_TAG_STEP, closeSize); - element->SetAttribute(CNS_TAG_ATTR_X, curNode.j); - element->SetAttribute(CNS_TAG_ATTR_Y, curNode.i); - if (curNode.parent) { - element->SetAttribute(CNS_TAG_ATTR_PARX, curNode.parent->j); - element->SetAttribute(CNS_TAG_ATTR_PARY, curNode.parent->i); - } - element->SetDoubleAttribute(CNS_TAG_ATTR_F, curNode.F); - element->SetDoubleAttribute(CNS_TAG_ATTR_G, curNode.g); - - iters->InsertEndChild(*element); -} - -void XmlLogger::writeToLogMap(const Map &map, const std::list &path) { - if (loglevel == CN_LOGLVL_NO || loglevel == CN_LOGLVL_TINY) return; - std::stringstream stream; - std::string text,value; - int *curLine; - std::list::const_iterator iter; - - curLine = new int[map.getWidth()]; - - for(int i=0; iFirstChildElement(CNS_TAG_ROOT); - element = element->FirstChildElement(CNS_TAG_LOG); - element = element->FirstChildElement(CNS_TAG_PATH); - TiXmlElement* msg; - - for(int i = 0; i < map.getHeight(); i++) { - msg = new TiXmlElement(CNS_TAG_ROW); - msg->SetAttribute(CNS_TAG_ATTR_NUM, i); - text = ""; - - for (iter = path.begin(); iter != path.end(); ++iter) { - if (iter->i == i) { - curLine[iter->j] = 1; - } - } - - for (int j = 0; j < map.getWidth(); j++) { - if (curLine[j] != 1) { - stream << map[i][j]; - stream >> value; - stream.clear(); - stream.str(""); - text = text + value + " "; - } else { - text = text + "*" + " "; - curLine[j] = 0; - } - } - - msg->LinkEndChild(new TiXmlText(text.c_str())); - element->LinkEndChild(msg); - } - delete [] curLine; -} - -void XmlLogger::writeToLogOpenClose(const OpenList &open, const std::unordered_multimap& close) { - - if (loglevel == CN_LOGLVL_NO || loglevel == CN_LOGLVL_HIGH || loglevel == CN_LOGLVL_TINY) return; - - int iterate = 0; - TiXmlElement *element = new TiXmlElement(CNS_TAG_STEP); - TiXmlNode *child = 0, *lowlevel = doc->FirstChild(CNS_TAG_ROOT); - lowlevel = lowlevel -> FirstChild(CNS_TAG_LOG) -> FirstChild(CNS_TAG_LOWLEVEL); - - while (child = lowlevel -> IterateChildren(child)) - iterate++; - - element -> SetAttribute(CNS_TAG_ATTR_NUM, iterate); - lowlevel -> InsertEndChild(*element); - lowlevel = lowlevel -> LastChild(); - - element = new TiXmlElement (CNS_TAG_OPEN); - lowlevel -> InsertEndChild(*element); - child = lowlevel -> LastChild(); - - element = open.writeToXml(element, child); - - element = new TiXmlElement (CNS_TAG_CLOSE); - lowlevel -> InsertEndChild(*element); - child = lowlevel -> LastChild(); - - for (std::unordered_map::const_iterator it = close.begin(); it != close.end(); ++it) { - element = new TiXmlElement(CNS_TAG_NODE); - element -> SetAttribute(CNS_TAG_ATTR_X, it->second.j); - element -> SetAttribute(CNS_TAG_ATTR_Y, it->second.i); - element -> SetDoubleAttribute(CNS_TAG_ATTR_F, it->second.F); - element -> SetDoubleAttribute(CNS_TAG_ATTR_G, it->second.g); - if (it->second.g > 0) { - element -> SetAttribute(CNS_TAG_ATTR_PARX, it->second.parent -> j); - element -> SetAttribute(CNS_TAG_ATTR_PARY, it->second.parent -> i); - } - child -> InsertEndChild(*element); - } -} - -void XmlLogger::writeToLogPath(const std::list &path, const std::vector &angles) -{ - if (loglevel == CN_LOGLVL_NO || loglevel == CN_LOGLVL_TINY) return; - TiXmlElement *element = doc->FirstChildElement(CNS_TAG_ROOT); - element = element->FirstChildElement(CNS_TAG_LOG); - element = element->FirstChildElement(CNS_TAG_LPLEVEL); - - TiXmlElement *point; - - std::list::const_iterator iter; - int i = 0; - - for(iter = path.begin(); iter != path.end(); ++iter) { - point = new TiXmlElement(CNS_TAG_NODE); - point->SetAttribute(CNS_TAG_ATTR_NUM, i); - point->SetAttribute(CNS_TAG_ATTR_X, iter->j); - point->SetAttribute(CNS_TAG_ATTR_Y, iter->i); - element->LinkEndChild(point); - ++i; - } - - if (angles.size() == 0) return; - - element=doc->FirstChildElement(CNS_TAG_ROOT); - element = element->FirstChildElement(CNS_TAG_LOG); - element = element->FirstChildElement(CNS_TAG_ANGLES); - - std::vector::const_iterator iter2=angles.end(); - for (i = 0; i < angles.size(); ++i) { - --iter2; - point = new TiXmlElement(CNS_TAG_ANGLE); - point->SetAttribute(CNS_TAG_ATTR_NUM, i); - point->SetDoubleAttribute(CNS_TAG_ATTR_VALUE, *iter2); - element->LinkEndChild(point); - } -} - -void XmlLogger::saveLog() { - //if (loglevel == CN_LOGLVL_NO) return; - doc->SaveFile(logFileName.c_str()); -} - -void XmlLogger::writeToLogHpLevel(const std::list &path) { - if (loglevel == CN_LOGLVL_NO || loglevel == CN_LOGLVL_TINY) return; - int partnumber = 0; - TiXmlElement *part; - TiXmlElement *hplevel = doc->FirstChildElement(CNS_TAG_ROOT); - hplevel = hplevel -> FirstChildElement(CNS_TAG_LOG) -> FirstChildElement(CNS_TAG_HPLEVEL); - std::list ::const_iterator iter = path.begin(); - std::list ::const_iterator it = path.begin(); - - while (iter != --path.end()) { - part = new TiXmlElement(CNS_TAG_SECTION); - part -> SetAttribute(CNS_TAG_ATTR_NUM, partnumber); - part -> SetAttribute(CNS_TAG_ATTR_SX, it->j); - part -> SetAttribute(CNS_TAG_ATTR_SY, it->i); - ++iter; - part -> SetAttribute(CNS_TAG_ATTR_FX, iter->j); - part -> SetAttribute(CNS_TAG_ATTR_FY, iter->i); - part -> SetDoubleAttribute(CNS_TAG_ATTR_LENGTH, iter->g - it->g); - hplevel -> LinkEndChild(part); - ++it; - ++partnumber; - } -} - -void XmlLogger::writeToLogSummary(const std::list &path, int numberofsteps, int nodescreated, float length, float length_scaled, - long double time, float max_angle, float accum_angle, int sections) { - if (loglevel == CN_LOGLVL_NO) return; - std::string timeValue; - std::stringstream stream; - stream << time; - stream >> timeValue; - stream.clear(); - stream.str(""); - TiXmlElement *element = doc->FirstChildElement(CNS_TAG_ROOT); - element = element->FirstChildElement(CNS_TAG_LOG); - element = element->FirstChildElement(CNS_TAG_SUM); - - if (path.size() == 0) { - element->SetAttribute(CNS_TAG_ATTR_PF, CNS_TAG_ATTR_FALSE); - } else { - element->SetAttribute(CNS_TAG_ATTR_PF, CNS_TAG_ATTR_TRUE); - } - - element->SetAttribute(CNS_TAG_ATTR_NUMOFSTEPS, numberofsteps); - element->SetAttribute(CNS_TAG_ATTR_NODESCREATED, nodescreated); - element->SetAttribute(CNS_TAG_ATTR_SECTIONS,sections); - element->SetDoubleAttribute(CNS_TAG_ATTR_LENGTH, length); - element->SetDoubleAttribute(CNS_TAG_ATTR_LENGTHSC, length_scaled); - element->SetAttribute(CNS_TAG_ATTR_TIME, timeValue.c_str()); - element->SetDoubleAttribute(CNS_TAG_ATTR_MAXANGLE, max_angle); - element->SetDoubleAttribute(CNS_TAG_ATTR_ACCUMANGLE, accum_angle); -} diff --git a/xmllogger.h b/xmllogger.h deleted file mode 100755 index e77be18..0000000 --- a/xmllogger.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef XMLLOGGER_H -#define XMLLOGGER_H - -#include "gl_const.h" -#include "logger.h" -#include "tinyxml/tinyxml.h" -#include "tinyxml/tinystr.h" - -#include -#include -#include - -class XmlLogger : public Logger { - -private: - std::string logFileName; - TiXmlDocument *doc; - -public: - XmlLogger(float loglvl); - ~XmlLogger(); - - bool getLog(const char* FileName); - void saveLog(); - void writeToLogIter(int closeSize, const Node &curNode); - void writeToLogMap(const Map &map,const std::list &path); - void writeToLogOpenClose(const OpenList &open, const std::unordered_multimap &close); - void writeToLogPath(const std::list &path, const std::vector &angles); - void writeToLogHpLevel(const std::list &path); - void writeToLogSummary(const std::list &path, int numberofsteps, int nodescreated, float length, float length_scaled, - long double time, float max_angle, float accum_angle, int sections); -}; - -#endif