mirror of https://github.com/Poikilos/b3view
610 lines
19 KiB
C++
610 lines
19 KiB
C++
#include "Debug.h"
|
|
#include "Utility.h"
|
|
|
|
#include <algorithm>
|
|
#include <clocale>
|
|
#include <cmath>
|
|
#include <cwctype> // #include <cwtype>
|
|
#include <iostream>
|
|
#include <filesystem>
|
|
#include <locale>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <assert.h>
|
|
#include <sys/stat.h>
|
|
// NOTE: to use filesystem, you must also include the fs library such
|
|
// as via the `-lstdc++fs` linker option -- see b3view.pro
|
|
// #include <filesystem> // requires C++17
|
|
#include <experimental/filesystem> // requires C++14 such as gcc 8.2.1
|
|
|
|
|
|
// C++14: namespace filesystem = std::experimental::filesystem;
|
|
// namespace fs = std::filesystem; // doesn't work (not a namespace in gcc's C++17)
|
|
// using namespace std::filesystem; // doesn't work (not a namespace in gcc's C++17)
|
|
namespace fs = std::experimental::filesystem;
|
|
// namespace fs = std::filesystem; // doesn't work (not a namespace in gcc's C++17)
|
|
|
|
using namespace irr::core;
|
|
using namespace irr::scene;
|
|
using namespace irr::video;
|
|
using namespace std;
|
|
|
|
const std::string Utility::WHITESPACE = " \n\r\t\f\v";
|
|
|
|
void Utility::dumpVectorToConsole(const vector3df& vector)
|
|
{
|
|
debug() << "X: " << vector.X << " Y: " << vector.Y << " Z: " << vector.Z << endl;
|
|
}
|
|
|
|
int Utility::getTextureCount(const SMaterial& material) {
|
|
int count = 0;
|
|
for (irr::u32 ti = 0; ti < MATERIAL_MAX_TEXTURES; ti++)
|
|
if (material.getTexture(ti) != nullptr)
|
|
count++;
|
|
return count;
|
|
}
|
|
int Utility::getTextureCount(IAnimatedMeshSceneNode* node) {
|
|
int count = 0;
|
|
for (irr::u32 matIndex = 0; matIndex < node->getMaterialCount(); matIndex++) {
|
|
count += getTextureCount(node->getMaterial(matIndex));
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void Utility::dumpMeshInfoToConsole(IAnimatedMeshSceneNode* node)
|
|
{
|
|
if (node == nullptr) {
|
|
debug() << "[MESH]: # node: nullptr" << endl;
|
|
return;
|
|
}
|
|
if (node->getMesh() == nullptr) {
|
|
debug() << "[MESH]: # node->getMesh(): nullptr" << endl;
|
|
return;
|
|
}
|
|
// Dump some information about the mesh to the console
|
|
IAnimatedMesh* mesh = node->getMesh();
|
|
|
|
debug() << "[MESH]: # of frames : " << mesh->getFrameCount()
|
|
<< endl;
|
|
debug() << "[MESH]: # of materials : " << node->getMaterialCount()
|
|
<< endl;
|
|
for (irr::u32 matIndex = 0; matIndex < node->getMaterialCount(); matIndex++) {
|
|
debug() << "[MESH]: Material # " << matIndex << endl;
|
|
const SMaterial& material = node->getMaterial(matIndex);
|
|
debug() << "[MESH]: Diffuse Color : A"
|
|
<< material.DiffuseColor.getAlpha()
|
|
<< " R" << material.DiffuseColor.getRed()
|
|
<< " G" << material.DiffuseColor.getGreen()
|
|
<< " B" << material.DiffuseColor.getBlue() << endl;
|
|
debug() << "[MESH]: Specular Color : A"
|
|
<< material.SpecularColor.getAlpha()
|
|
<< " R" << material.SpecularColor.getRed()
|
|
<< " G" << material.SpecularColor.getGreen()
|
|
<< " B" << material.SpecularColor.getBlue() << endl;
|
|
debug() << "[MESH]: Specular Shininess : "
|
|
<< material.Shininess << endl;
|
|
|
|
// check for # textures
|
|
debug() << "[MESH]: # of textures : " << Utility::getTextureCount(material) << endl;
|
|
}
|
|
}
|
|
|
|
std::wstring Utility::parentOfPath(const wstring& path)
|
|
{
|
|
std::wstring ret = L".";
|
|
if (path == L".") {
|
|
ret = L"..";
|
|
} else {
|
|
std::wstring::size_type lastSlashPos = path.find_last_of(L"/");
|
|
if (lastSlashPos == std::wstring::npos) {
|
|
lastSlashPos = path.find_last_of(L"\\");
|
|
}
|
|
if (lastSlashPos != std::wstring::npos) {
|
|
ret = path.substr(0, lastSlashPos);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/// Filename without path (may still have extension)
|
|
wstring Utility::basename(const wstring& path)
|
|
{
|
|
std::wstring ret = path;
|
|
std::wstring::size_type lastSlashPos = path.find_last_of(L"/");
|
|
if (lastSlashPos == std::wstring::npos) {
|
|
lastSlashPos = path.find_last_of(L"\\");
|
|
}
|
|
if (lastSlashPos != std::wstring::npos) {
|
|
ret = path.substr(lastSlashPos + 1);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/// Get any substring to the right of the first delimiter.
|
|
/// allIfNotFound: Return whole string on no delimiter, vs empty string.
|
|
wstring Utility::rightOf(const wstring& path, const wstring& delimiter, bool allIfNotFound)
|
|
{
|
|
std::wstring ret = allIfNotFound ? path : L"";
|
|
std::wstring::size_type lastPos = path.find(delimiter);
|
|
if (lastPos != std::wstring::npos) {
|
|
ret = path.substr(lastPos + delimiter.length());
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/// Get any substring to the right of the last delimiter.
|
|
/// allIfNotFound: Return whole string on no delimiter, vs empty string.
|
|
wstring Utility::rightOfLast(const wstring& path, const wstring& delimiter, bool allIfNotFound)
|
|
{
|
|
std::wstring ret = allIfNotFound ? path : L"";
|
|
std::wstring::size_type lastPos = path.find_last_of(delimiter);
|
|
if (lastPos != std::wstring::npos) {
|
|
ret = path.substr(lastPos + delimiter.length());
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/// Get any substring to the left of the first delimiter.
|
|
/// allIfNotFound: Return whole string on no delimiter, vs empty string.
|
|
wstring Utility::leftOf(const wstring& path, const wstring& delimiter, bool allIfNotFound)
|
|
{
|
|
std::wstring ret = allIfNotFound ? path : L"";
|
|
std::wstring::size_type lastPos = path.find(delimiter);
|
|
if (lastPos != std::wstring::npos) {
|
|
ret = path.substr(0, lastPos);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool Utility::startsWith(const std::wstring& haystack, const std::wstring& needle) {
|
|
bool found = false;
|
|
if (haystack.length() >= needle.length()) {
|
|
if (haystack.substr(0, needle.length())==needle) {
|
|
found = true;
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
bool Utility::endsWith(const std::wstring& haystack, const std::wstring& needle) {
|
|
bool found = false;
|
|
if (haystack.length() >= needle.length()) {
|
|
if (haystack.substr(haystack.length()-needle.length())==needle) {
|
|
found = true;
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
bool Utility::startsWith(const std::string& haystack, const std::string& needle) {
|
|
bool found = false;
|
|
if (haystack.length() >= needle.length()) {
|
|
if (haystack.substr(0, needle.length())==needle) {
|
|
found = true;
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
bool Utility::endsWith(const std::string& haystack, const std::string& needle) {
|
|
bool found = false;
|
|
if (haystack.length() >= needle.length()) {
|
|
if (haystack.substr(haystack.length()-needle.length())==needle) {
|
|
found = true;
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
wstring Utility::replaceAll(const wstring &subject, const wstring &from, const wstring &to)
|
|
{
|
|
size_t i = 0;
|
|
if (from.length() == 0) {
|
|
return subject;
|
|
}
|
|
wstring result = subject;
|
|
while (i < result.length()) {
|
|
if (result.substr(i, from.length()) == from) {
|
|
result = result.substr(0, i) + to + result.substr(i + from.length());
|
|
i += to.length();
|
|
}
|
|
else {
|
|
i++;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::string Utility::replaceAll(const std::string &subject, const std::string &from, const std::string &to)
|
|
{
|
|
size_t i = 0;
|
|
if (from.length() == 0) {
|
|
return subject;
|
|
}
|
|
std::string result = subject;
|
|
while (i < result.length()) {
|
|
if (result.substr(i, from.length()) == from) {
|
|
result = result.substr(0, i) + to + result.substr(i + from.length());
|
|
i += to.length();
|
|
}
|
|
else {
|
|
i++;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool Utility::startsWithAny(const std::wstring& haystack, const std::vector<std::wstring>& needles, bool CI) {
|
|
return getPrefix(haystack, needles, CI).length() > 0;
|
|
}
|
|
|
|
bool Utility::endsWithAny(const std::wstring& haystack, const std::vector<std::wstring>& needles, bool CI) {
|
|
return getSuffix(haystack, needles, CI).length() > 0;
|
|
}
|
|
|
|
std::wstring Utility::getPrefix(const std::wstring& haystack, const std::vector<std::wstring>& needles, bool CI) {
|
|
if (CI) {
|
|
std::wstring haystackLower = Utility::toLower(haystack);
|
|
for (auto needle : needles) {
|
|
if (Utility::startsWith(haystackLower, Utility::toLower(needle)))
|
|
return needle;
|
|
}
|
|
}
|
|
else {
|
|
for (auto needle : needles) {
|
|
if (Utility::startsWith(haystack, needle))
|
|
return needle;
|
|
}
|
|
}
|
|
return L"";
|
|
}
|
|
|
|
std::wstring Utility::getSuffix(const std::wstring& haystack, const std::vector<std::wstring>& needles, bool CI) {
|
|
if (CI) {
|
|
std::wstring haystackLower = Utility::toLower(haystack);
|
|
for (auto needle : needles) {
|
|
if (Utility::endsWith(haystackLower, Utility::toLower(needle)))
|
|
return needle;
|
|
}
|
|
}
|
|
else {
|
|
for (auto needle : needles) {
|
|
if (Utility::endsWith(haystack, needle))
|
|
return needle;
|
|
}
|
|
}
|
|
return L"";
|
|
}
|
|
|
|
/// Get any substring to the left of the last delimiter.
|
|
/// allIfNotFound: Return whole string on no delimiter, vs empty string.
|
|
wstring Utility::leftOfLast(const wstring& path, const wstring& delimiter, bool allIfNotFound)
|
|
{
|
|
std::wstring ret = allIfNotFound ? path : L"";
|
|
std::wstring::size_type lastPos = path.find_last_of(delimiter);
|
|
if (lastPos != std::wstring::npos) {
|
|
ret = path.substr(0, lastPos);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
wstring Utility::withoutExtension(const wstring& path)
|
|
{
|
|
std::wstring ret = path;
|
|
std::wstring::size_type lastDotPos = path.find_last_of(L".");
|
|
if (lastDotPos != std::wstring::npos) {
|
|
std::wstring::size_type lastSlashPos = path.find_last_of(L"/");
|
|
if (lastSlashPos == std::wstring::npos) {
|
|
lastSlashPos = path.find_last_of(L"\\");
|
|
}
|
|
if (lastSlashPos != std::wstring::npos) {
|
|
if (lastDotPos > lastSlashPos)
|
|
ret = path.substr(0, lastDotPos);
|
|
} else
|
|
ret = path.substr(0, lastDotPos);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
wstring Utility::extensionOf(const wstring& path)
|
|
{
|
|
std::wstring ret = L"";
|
|
std::wstring::size_type lastDotPos = path.find_last_of(L".");
|
|
if (lastDotPos != std::wstring::npos) {
|
|
std::wstring::size_type lastSlashPos = path.find_last_of(L"/");
|
|
if (lastSlashPos == std::wstring::npos) {
|
|
lastSlashPos = path.find_last_of(L"\\");
|
|
}
|
|
if (lastSlashPos != std::wstring::npos) {
|
|
if (lastDotPos > lastSlashPos)
|
|
ret = path.substr(lastDotPos + 1);
|
|
} else
|
|
ret = path.substr(lastDotPos + 1);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
wstring Utility::delimiter(const wstring& path)
|
|
{
|
|
std::wstring ret = L"/";
|
|
std::wstring::size_type lastSlashPos = path.find_last_of(L"/");
|
|
if (lastSlashPos == std::wstring::npos) {
|
|
// ret = L"/";
|
|
} else {
|
|
std::wstring::size_type lastSlashPos = path.find_last_of(L"\\");
|
|
if (lastSlashPos != std::wstring::npos) {
|
|
ret = L"\\";
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool Utility::isFile(const std::string& name)
|
|
{
|
|
if (FILE* file = fopen(name.c_str(), "r")) {
|
|
fclose(file);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::string Utility::toString(const std::wstring& ws)
|
|
{
|
|
std::string ret;
|
|
if (ws.length() > 0) {
|
|
// std::string str = "Hello";
|
|
ret = std::string(ws.length(), L' '); // Make room for characters
|
|
// Copy string to wstring.
|
|
std::copy(ws.begin(), ws.end(), ret.begin());
|
|
}
|
|
return ret;
|
|
|
|
// below sometimes results in "internal_utf8_loop_single: Assertion
|
|
// `inptr - bytebuf > (state->__count & 7)' failed."
|
|
// on the converter.out call:
|
|
// if (ws.length() > 0) {
|
|
// // convert to w_string using locale: see Phillipp on
|
|
// // <https://stackoverflow.com/questions/4804298/how-to-convert-wstring-into-string>
|
|
// std::setlocale(LC_ALL, "");
|
|
// const std::locale locale("");
|
|
// typedef std::codecvt<wchar_t, char, std::mbstate_t> converter_type;
|
|
// const converter_type& converter = std::use_facet<converter_type>(locale);
|
|
// std::vector<char> to(ws.length() * converter.max_length());
|
|
// std::mbstate_t state;
|
|
// const wchar_t* from_next = nullptr;
|
|
// char* to_next = nullptr;
|
|
// const converter_type::result result = converter.out(
|
|
// state, ws.data(),
|
|
// ws.data() + ws.length(),
|
|
// from_next,
|
|
// &to[0],
|
|
// &to[0] + to.size(),
|
|
// to_next
|
|
// );
|
|
// if (result == converter_type::ok or result == converter_type::noconv) {
|
|
// const std::string s(&to[0], to_next);
|
|
// // std::cout <<"std::string = "<<s<<std::endl;
|
|
// ret = s;
|
|
// }
|
|
//return ret;
|
|
}
|
|
|
|
std::string Utility::toLower(const std::string& s)
|
|
{
|
|
std::string ret = s;
|
|
std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower);
|
|
return ret;
|
|
}
|
|
|
|
wstring Utility::toLower(const wstring& s)
|
|
{
|
|
wstring ret = s;
|
|
std::transform(ret.begin(), ret.end(), ret.begin(), towlower);
|
|
return ret;
|
|
}
|
|
|
|
wstring Utility::toWstring(irr::f32 val)
|
|
{
|
|
return std::to_wstring(val);
|
|
}
|
|
|
|
wstring Utility::toWstring(int val)
|
|
{
|
|
return std::to_wstring(val);
|
|
}
|
|
|
|
wstring Utility::toWstring(const std::string& str)
|
|
{
|
|
std::wstring ret;
|
|
if (str.length() > 0) {
|
|
// std::string str = "Hello";
|
|
ret = std::wstring(str.length(), L' '); // Make room for characters
|
|
// Copy string to wstring.
|
|
std::copy(str.begin(), str.end(), ret.begin());
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
std::string Utility::dateTimePathString(const time_t& rawtime)
|
|
{
|
|
// see http://www.cplusplus.com/reference/ctime/strftime/
|
|
std::string ret = "";
|
|
struct tm * timeinfo;
|
|
char buffer[80];
|
|
timeinfo = localtime (&rawtime);
|
|
strftime (buffer,80,"%F_%H%M%S",timeinfo);
|
|
// %F is same as %Y-%m-%d (zero-padded)
|
|
ret.assign(buffer);
|
|
return ret;
|
|
}
|
|
|
|
std::string Utility::dateTimeNowPathString()
|
|
{
|
|
time_t rawtime;
|
|
time (&rawtime);
|
|
return dateTimePathString(rawtime);
|
|
}
|
|
|
|
irr::f32 Utility::toF32(wstring val)
|
|
{
|
|
std::wstringstream ss(val);
|
|
irr::f32 ret = 0;
|
|
ss >> ret;
|
|
return ret;
|
|
}
|
|
|
|
irr::f32 Utility::distance(const vector3df &start, const vector3df &end)
|
|
{
|
|
vector3df offsetVec3(end.X - start.X, end.Y - start.Y, end.Z - start.Z);
|
|
return offsetVec3.getLength();
|
|
}
|
|
|
|
bool Utility::isFile(const std::wstring& name)
|
|
{
|
|
std::string name_s = toString(name);
|
|
if (FILE* file = fopen(name_s.c_str(), "r")) {
|
|
fclose(file);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool Utility::is_directory(const std::string &path)
|
|
{
|
|
return fs::is_directory(fs::status(path));
|
|
/*
|
|
//pre C++17:
|
|
struct stat info;
|
|
if (stat(path, &info) != 0) {
|
|
// TODO: ^ fails on OSX 10.11 according to a comment on the
|
|
// accepted answer on
|
|
// https://stackoverflow.com/questions/18100097/portable-way-to-check-if-directory-exists-windows-linux-c
|
|
}
|
|
*/
|
|
}
|
|
bool Utility::is_directory(const std::wstring &path)
|
|
{
|
|
return fs::is_directory(fs::status(path));
|
|
/*
|
|
//pre C++17:
|
|
struct stat info;
|
|
if (stat(path, &info) != 0) {
|
|
// TODO: ^ fails on OSX 10.11 according to a comment on the
|
|
// accepted answer on
|
|
// https://stackoverflow.com/questions/18100097/portable-way-to-check-if-directory-exists-windows-linux-c
|
|
}
|
|
*/
|
|
}
|
|
|
|
void Utility::create_directory(const std::string &path) {
|
|
fs::create_directory(path);
|
|
}
|
|
|
|
std::string Utility::toString(int val)
|
|
{
|
|
return std::to_string(val);
|
|
// The only throw is std::bad_alloc according to
|
|
// <https://en.cppreference.com/w/cpp/string/basic_string/to_string>
|
|
}
|
|
|
|
std::string Utility::toString(irr::f32 val)
|
|
{
|
|
return std::to_string(val);
|
|
// The only throw is std::bad_alloc according to
|
|
// <https://en.cppreference.com/w/cpp/string/basic_string/to_string>
|
|
}
|
|
|
|
std::string Utility::ltrim(const std::string& s)
|
|
{
|
|
// based on https://www.techiedelight.com/trim-string-cpp-remove-leading-trailing-spaces/
|
|
size_t start = s.find_first_not_of(Utility::WHITESPACE);
|
|
return (start == std::string::npos) ? "" : s.substr(start);
|
|
}
|
|
|
|
std::string Utility::rtrim(const std::string& s)
|
|
{
|
|
// based on https://www.techiedelight.com/trim-string-cpp-remove-leading-trailing-spaces/
|
|
size_t end = s.find_last_not_of(Utility::WHITESPACE);
|
|
return (end == std::string::npos) ? "" : s.substr(0, end + 1);
|
|
}
|
|
|
|
std::string Utility::trim(const std::string& s)
|
|
{
|
|
// based on https://www.techiedelight.com/trim-string-cpp-remove-leading-trailing-spaces/
|
|
return rtrim(ltrim(s));
|
|
}
|
|
|
|
// don't do late instantiation (see header file)
|
|
// template<typename T>
|
|
// bool Utility::equalsApprox(T f1, T f2)
|
|
// {
|
|
// return abs(f2-f1) < .00000001; // TODO: kEpsilon? (see also
|
|
// // <https://en.wikipedia.org/wiki/Machine_epsilon#How_to_determine_machine_epsilon>)
|
|
// }
|
|
TestUtility::TestUtility() {
|
|
std::cerr << "TestUtility..." << std::flush;
|
|
testReplaceAll(L"***water_dragon***", L"_", L"", L"***waterdragon***");
|
|
testReplaceAll(L"*water_dragon*", L"*", L"***", L"***water_dragon***");
|
|
|
|
testReplaceAll(L"***water_dragon***", L"***", L"", L"water_dragon");
|
|
testReplaceAll(L"***water_dragon***", L"", L"***", L"***water_dragon***"); // do nothing
|
|
testLTrim(" apple", "apple");
|
|
testLTrim(" banana ", "banana ");
|
|
testRTrim(" cantelope ", " cantelope");
|
|
testRTrim("dragon fruit ", "dragon fruit");
|
|
testTrim(" elderberry ", "elderberry");
|
|
testTrim("fig ", "fig");
|
|
testTrim(" grape", "grape");
|
|
testTrim("horned melon ", "horned melon");
|
|
std::cerr << "OK" << std::endl;
|
|
}
|
|
|
|
void TestUtility::testReplaceAll(const wstring &subject, const wstring &from, const wstring &to, const wstring &expectedResult)
|
|
{
|
|
this->assertEqual(Utility::replaceAll(subject, from, to), expectedResult);
|
|
};
|
|
void TestUtility::testReplaceAll(const std::string &subject, const std::string &from, const std::string &to, const std::string &expectedResult)
|
|
{
|
|
std::string result = Utility::replaceAll(subject, from, to);
|
|
this->assertEqual(result, expectedResult);
|
|
}
|
|
|
|
void TestUtility::testTrim(const std::string &subject, const std::string &expectedResult)
|
|
{
|
|
assertEqual(Utility::trim(subject), expectedResult);
|
|
}
|
|
|
|
void TestUtility::testLTrim(const std::string &subject, const std::string &expectedResult)
|
|
{
|
|
assertEqual(Utility::ltrim(subject), expectedResult);
|
|
}
|
|
|
|
void TestUtility::testRTrim(const std::string &subject, const std::string &expectedResult)
|
|
{
|
|
assertEqual(Utility::rtrim(subject), expectedResult);
|
|
}
|
|
|
|
void TestUtility::assertEqual(const wstring& subject, const wstring& expectedResult)
|
|
{
|
|
if (subject != expectedResult) {
|
|
cerr << "The test expected \"" << Utility::toString(expectedResult) << "\" but got \"" << Utility::toString(subject) << "\"" << std::endl;
|
|
}
|
|
assert(subject == expectedResult);
|
|
}
|
|
void TestUtility::assertEqual(const std::string subject, const std::string expectedResult)
|
|
{
|
|
if (subject != expectedResult) {
|
|
cerr << "The test expected \"" << expectedResult << "\" but got \"" << subject << "\"" << std::endl;
|
|
}
|
|
assert(subject == expectedResult);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
static TestUtility testutility; // Run tests (Creating the first instance runs the static constructor).
|
|
#elif QT_DEBUG
|
|
static TestUtility testutility; // Run tests (Creating the first instance runs the static constructor).
|
|
#endif
|