mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-20 15:04:54 +02:00
Labels: - every button, field, tooltip and message of the editor reread for a beginner: short nouns for the fields with the sentence in the tooltip, the less used options under More settings, one glossary (field of work, template, track, grade), sides told from directions, degrees and per mille as signs, messages built from one format string - plural forms through STR_P; the last entry of a po file is read too, and the Polish plural rule is in the header - lang/editor_pl.po: the code pass entries merged, 391 entries no code uses and 3 duplicates dropped - roads: the notes and errors naming a road translated; the build options and the layout fold the texture length, kerb sizes, traffic share, roundabout part, height over the ground and junction speed limit under More settings, the lanes start folded Grade lines fitted to the terrain model (NMT), a line or the whole scenery at once: - fit_ground: a robust fit on nodes 5 m apart; the ground parting from the line (a bridge, a viaduct, a wall) is gone past rather than followed, the bends keep off the switches as far as their curves reach, a run of bends the same way is one point of intersection, a curve with no room is as sharp as the room lets it be - the ends of the line and the other paths of its switches leading into lines fitted already bound the line by what the tracks between can climb; ends meeting in the plan, laid apart in height by hand, are joined before the run - apply_profile: the paths are split where split_path takes the parameter (a straight was cut in the wrong place, into pieces of a few centimetres the game takes for zero length), no piece shorter than the margin; the paths off the ends of the route are brought to its height - Bytom: 1600 lines, grade breaks at the joints 89 -> 20; Tarnowskie Gory: 848 lines, 223 -> 3; no step over 2 cm
466 lines
14 KiB
C++
466 lines
14 KiB
C++
/*
|
|
This Source Code Form is subject to the
|
|
terms of the Mozilla Public License, v.
|
|
2.0. If a copy of the MPL was not
|
|
distributed with this file, You can
|
|
obtain one at
|
|
http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "stdafx.h"
|
|
#include "utilities/parser.h"
|
|
|
|
/*rozne takie duperele do operacji na stringach w paszczalu, pewnie w delfi sa lepsze*/
|
|
/*konwersja zmiennych na stringi, funkcje matematyczne, logiczne, lancuchowe, I/O etc*/
|
|
|
|
template <typename T> T sign(T x)
|
|
{
|
|
return x < static_cast<T>(0) ? static_cast<T>(-1) : x > static_cast<T>(0) ? static_cast<T>(1) : static_cast<T>(0);
|
|
}
|
|
|
|
template <typename T> constexpr T sq(T v)
|
|
{
|
|
return v * v;
|
|
}
|
|
|
|
// returns the per-user config path for `filename` (platform-specific dir),
|
|
// or an empty path if the home/appdata env var is unavailable
|
|
std::filesystem::path user_config_path(const std::string &filename);
|
|
|
|
// TODO: Shouldn't this be in globals?
|
|
namespace paths
|
|
{
|
|
inline constexpr const char *scenery = "scenery/";
|
|
inline constexpr const char *textures = "textures/";
|
|
inline constexpr const char *models = "models/";
|
|
inline constexpr const char *dynamic = "dynamic/";
|
|
inline constexpr const char *sounds = "sounds/";
|
|
inline constexpr const char *data = "data/";
|
|
}
|
|
|
|
#define MAKE_ID4(a, b, c, d) (((std::uint32_t)(d) << 24) | ((std::uint32_t)(c) << 16) | ((std::uint32_t)(b) << 8) | (std::uint32_t)(a))
|
|
|
|
extern bool DebugModeFlag;
|
|
extern bool FreeFlyModeFlag;
|
|
extern bool EditorModeFlag;
|
|
extern bool DebugCameraFlag;
|
|
extern bool DebugTractionFlag;
|
|
|
|
/*funkcje matematyczne*/
|
|
inline double Sign(double x)
|
|
{
|
|
return x >= 0 ? 1.0 : -1.0;
|
|
}
|
|
|
|
inline long Round(double const f)
|
|
{
|
|
return (long)(f + 0.5);
|
|
// return lround(f);
|
|
}
|
|
|
|
double Random(double a, double b);
|
|
int Random(int min, int max);
|
|
|
|
/// @author lxvia
|
|
/// @brief Returns a random seed from the system's entropy source, if available. Otherwise, falls back to a clock-based seed.
|
|
/// @return A random seed as 64 bit unsigned integer
|
|
std::uint64_t true_random_seed();
|
|
|
|
// The seed a word stands for. A number is taken as it is written; anything else is
|
|
// hashed the way Java's String.hashCode does it, so the same word gives the same
|
|
// world on every platform and every build - which is the whole point of telling
|
|
// somebody a seed.
|
|
std::uint32_t seed_of(std::string const &Text);
|
|
|
|
std::string generate_uuid_v4();
|
|
double LocalRandom(double a, double b);
|
|
|
|
inline double Random()
|
|
{
|
|
return Random(0.0, 1.0);
|
|
}
|
|
|
|
inline double Random(double b)
|
|
{
|
|
return Random(0.0, b);
|
|
}
|
|
|
|
inline double LocalRandom()
|
|
{
|
|
return LocalRandom(0.0, 1.0);
|
|
}
|
|
|
|
inline double LocalRandom(double b)
|
|
{
|
|
return LocalRandom(0.0, b);
|
|
}
|
|
|
|
inline double BorlandTime()
|
|
{
|
|
auto timesinceepoch = std::time(nullptr);
|
|
return static_cast<double>(timesinceepoch) / (24.0 * 60 * 60);
|
|
/*
|
|
// std alternative
|
|
auto timesinceepoch = std::chrono::system_clock::now().time_since_epoch();
|
|
return std::chrono::duration_cast<std::chrono::seconds>( timesinceepoch ).count() / (24.0 * 60 * 60);
|
|
*/
|
|
}
|
|
|
|
std::string Now();
|
|
|
|
double CompareTime(double t1h, double t1m, double t2h, double t2m);
|
|
|
|
/*funkcje logiczne*/
|
|
inline bool TestFlag(int const Flag, int const Value)
|
|
{
|
|
return (Flag & Value) == Value;
|
|
}
|
|
inline bool TestFlagAny(int const Flag, int const Value)
|
|
{
|
|
return (Flag & Value) != 0;
|
|
}
|
|
bool SetFlag(int &Flag, int const Value);
|
|
bool ClearFlag(int &Flag, int const Value);
|
|
|
|
bool FuzzyLogic(double Test, double Threshold, double Probability);
|
|
/*jesli Test>Threshold to losowanie*/
|
|
bool FuzzyLogicAI(double Test, double Threshold, double Probability);
|
|
/*to samo ale zawsze niezaleznie od DebugFlag*/
|
|
|
|
/*operacje na stringach*/
|
|
std::vector<std::string> Split(std::string_view s, char delim);
|
|
std::pair<std::string, int> split_string_and_number(std::string const &Key);
|
|
|
|
std::string to_string(int Value, int width);
|
|
std::string to_string(double Value, int precision);
|
|
std::string to_string(double Value, int precision, int width);
|
|
std::string to_hex_str(int const Value, int const width = 4);
|
|
std::string to_minutes_str(float const Minutes, bool const Leadingzero, int const Width);
|
|
|
|
template <std::same_as<bool> T> // Without this line this function can be used with other types implicit casted to boolean which may create hard to debug bugs.
|
|
inline std::string to_string(T Value)
|
|
{
|
|
return Value == true ? "true" : "false";
|
|
}
|
|
|
|
template <typename Type_, glm::precision Precision_ = glm::defaultp> std::string to_string(glm::tvec3<Type_, Precision_> const &Value)
|
|
{
|
|
return to_string(Value.x, 2) + ", " + to_string(Value.y, 2) + ", " + to_string(Value.z, 2);
|
|
}
|
|
|
|
template <typename Type_, glm::precision Precision_ = glm::defaultp> std::string to_string(glm::tvec4<Type_, Precision_> const &Value, int const Width = 2)
|
|
{
|
|
return to_string(Value.x, Width) + ", " + to_string(Value.y, Width) + ", " + to_string(Value.z, Width) + ", " + to_string(Value.w, Width);
|
|
}
|
|
|
|
int stol_def(const std::string &str, const int &DefaultValue);
|
|
|
|
std::string ToLower(std::string const &text);
|
|
std::string ToUpper(std::string const &text);
|
|
|
|
// replaces polish letters with basic ascii
|
|
void win1250_to_ascii(std::string &Input);
|
|
// TODO: unify with win1250_to_ascii()
|
|
std::string Bezogonkow(std::string Input, bool const Underscorestospaces = false);
|
|
|
|
std::string win1250_to_utf8(const std::string &input);
|
|
// decodes utf-8 encoded text into unicode code points. returns false if the text isn't valid utf-8
|
|
bool utf8_to_utf32(std::string const &Text, std::u32string &Output);
|
|
// decodes windows-1250 (ansi) encoded text into unicode code points
|
|
void win1250_to_utf32(std::string const &Text, std::u32string &Output);
|
|
|
|
inline std::string extract_value(std::string const &Key, std::string const &Input)
|
|
{
|
|
// NOTE, HACK: the leading space allows to uniformly look for " variable=" substring
|
|
std::string const input{" " + Input};
|
|
std::string value;
|
|
if (auto lookup = input.find(" " + Key + "="); lookup != std::string::npos)
|
|
{
|
|
value = input.substr(input.find_first_not_of(' ', lookup + Key.size() + 2));
|
|
lookup = value.find(' ');
|
|
if (lookup != std::string::npos)
|
|
{
|
|
// trim everything past the value
|
|
value.erase(lookup);
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
template <typename Type_> bool extract_value(Type_ &Variable, std::string const &Key, std::string const &Input, std::string const &Default)
|
|
{
|
|
|
|
auto value = extract_value(Key, Input);
|
|
if (false == value.empty())
|
|
{
|
|
// set the specified variable to retrieved value
|
|
std::stringstream converter;
|
|
converter << value;
|
|
converter >> Variable;
|
|
return true; // located the variable
|
|
}
|
|
else
|
|
{
|
|
// set the variable to provided default value
|
|
if (false == Default.empty())
|
|
{
|
|
std::stringstream converter;
|
|
converter << Default;
|
|
converter >> Variable;
|
|
}
|
|
return false; // couldn't locate the variable in provided input
|
|
}
|
|
}
|
|
|
|
template <> bool extract_value(bool &Variable, std::string const &Key, std::string const &Input, std::string const &Default);
|
|
|
|
bool FileExists(std::string const &Filename);
|
|
// the file as it's named on the disk when only the case of its name differs, as in the sceneries made on Windows; empty when
|
|
// there's none
|
|
std::string FileCased(std::string const &Filename);
|
|
|
|
std::pair<std::string, std::string> FileExists(std::vector<std::string> const &Names, std::vector<std::string> const &Extensions);
|
|
|
|
// returns time of last modification for specified file
|
|
std::time_t last_modified(std::string const &Filename);
|
|
|
|
// thread-safe replacements for std::localtime and std::gmtime, which hand out a pointer to shared static storage
|
|
std::tm local_tm(std::time_t Time);
|
|
std::tm utc_tm(std::time_t Time);
|
|
|
|
// potentially erases file extension from provided file name. returns: true if extension was removed, false otherwise
|
|
bool erase_extension(std::string &Filename);
|
|
|
|
// potentially erase leading slashes from provided file path
|
|
void erase_leading_slashes(std::string &Filename);
|
|
|
|
// potentially replaces backward slashes in provided file path with unix-compatible forward slashes
|
|
void replace_slashes(std::string &Filename);
|
|
|
|
// returns potential path part from provided file name
|
|
std::string substr_path(std::string const &Filename);
|
|
|
|
// returns common prefix of two provided strings
|
|
std::ptrdiff_t len_common_prefix(std::string_view a, std::string_view b);
|
|
|
|
// returns true if provided string contains another provided string
|
|
bool contains(std::string_view const String, std::string_view Substring);
|
|
bool contains(std::string_view const String, char Character);
|
|
|
|
// TODO: Ideally unique_ptr should be used instead of this (not safe) inline functions
|
|
template <typename T> inline void SafeDelete(T* &Pointer)
|
|
{
|
|
delete Pointer;
|
|
Pointer = nullptr;
|
|
}
|
|
|
|
template <typename T> inline void SafeDeleteArray(T *&Pointer)
|
|
{
|
|
delete[] Pointer;
|
|
Pointer = nullptr;
|
|
}
|
|
|
|
template <typename T> bool is_equal(T const &Left, T const &Right, T const Epsilon = T(1e-5))
|
|
{
|
|
if (Epsilon != T(0))
|
|
return glm::epsilonEqual(Left, Right, Epsilon);
|
|
|
|
return Left == Right;
|
|
}
|
|
|
|
// Tolerant clamp. Unlike std::clamp, this does NOT invoke undefined behaviour when
|
|
// the bounds are inverted (Lo > Hi) - it normalizes them instead. This restores the
|
|
// pre-refactor behaviour of the old custom clamp() for code paths where the bounds are
|
|
// computed at runtime and can legitimately cross (e.g. AI acceleration/braking limits,
|
|
// physics jolt calculations), where std::clamp's UB produced wrong results.
|
|
template <typename Type_>
|
|
constexpr Type_ safe_clamp( Type_ const Value, Type_ const Lo, Type_ const Hi )
|
|
{
|
|
return ( Hi < Lo )
|
|
? std::clamp( Value, Hi, Lo )
|
|
: std::clamp( Value, Lo, Hi );
|
|
}
|
|
|
|
// keeps the provided value in specified range 0-Range, as if the range was circular buffer
|
|
template <typename T> T clamp_circular(T Value, T const Range = T(360))
|
|
{
|
|
if constexpr (std::is_floating_point_v<T>)
|
|
{
|
|
Value = std::fmod(Value, Range);
|
|
}
|
|
else
|
|
{
|
|
Value %= Range;
|
|
}
|
|
|
|
if (Value < T(0))
|
|
Value += Range;
|
|
return Value;
|
|
}
|
|
|
|
// rounds down provided value to nearest power of two
|
|
template <typename T> T clamp_power_of_two(T Value, T const Min = T(1), T const Max = T(16384))
|
|
{
|
|
if (Value < Min)
|
|
return Min;
|
|
|
|
T p2 = std::bit_floor(Value);
|
|
|
|
if (p2 > Max)
|
|
return Max;
|
|
|
|
return p2;
|
|
}
|
|
|
|
template <typename Type_> Type_ quantize(Type_ const Value, Type_ const Step)
|
|
{
|
|
|
|
return static_cast<Type_>(Step * std::round(Value / Step));
|
|
}
|
|
|
|
template <typename T> T min_speed(T const Left, T const Right)
|
|
{
|
|
constexpr T none = T(-1);
|
|
if (Left == none)
|
|
return Right;
|
|
if (Right == none)
|
|
return Left;
|
|
|
|
return std::min(Left, Right);
|
|
}
|
|
|
|
template <typename T> T smoothInterpolate(T const &First, T const &Second, double Factor)
|
|
{
|
|
// Apply smoothing (ease-in-out quadratic)
|
|
Factor = Factor * Factor * (3 - 2 * Factor);
|
|
|
|
return First + (Second - First) * Factor;
|
|
}
|
|
|
|
// tests whether provided points form a degenerate triangle
|
|
template <typename VecType_> bool degenerate(VecType_ const &Vertex1, VecType_ const &Vertex2, VecType_ const &Vertex3)
|
|
{
|
|
|
|
// degenerate( A, B, C, minarea ) = ( ( B - A ).cross( C - A ) ).lengthSquared() < ( 4.0f * minarea * minarea );
|
|
return glm::length2(glm::cross(Vertex2 - Vertex1, Vertex3 - Vertex1)) == 0.0;
|
|
}
|
|
|
|
// calculates bounding box for provided set of points
|
|
template <class Iterator_, class VecType_> void bounding_box(VecType_ &Mincorner, VecType_ &Maxcorner, Iterator_ First, Iterator_ Last)
|
|
{
|
|
|
|
Mincorner = VecType_(std::numeric_limits<typename VecType_::value_type>::max());
|
|
Maxcorner = VecType_(std::numeric_limits<typename VecType_::value_type>::lowest());
|
|
|
|
std::for_each(First, Last,
|
|
[&](typename Iterator_::value_type &point)
|
|
{
|
|
Mincorner = glm::min(Mincorner, VecType_{point});
|
|
Maxcorner = glm::max(Maxcorner, VecType_{point});
|
|
});
|
|
}
|
|
|
|
// finds point on specified segment closest to specified point in 3d space. returns: point on segment as value in range 0-1 where 0 = start and 1 = end of the segment
|
|
template <typename VecType_> typename VecType_::value_type nearest_segment_point(VecType_ const &Segmentstart, VecType_ const &Segmentend, VecType_ const &Point)
|
|
{
|
|
|
|
auto const v = Segmentend - Segmentstart;
|
|
auto const w = Point - Segmentstart;
|
|
|
|
auto const c1 = glm::dot(w, v);
|
|
if (c1 <= 0.0)
|
|
{
|
|
return 0.0;
|
|
}
|
|
auto const c2 = glm::dot(v, v);
|
|
if (c2 <= c1)
|
|
{
|
|
return 1.0;
|
|
}
|
|
return c1 / c2;
|
|
}
|
|
|
|
glm::dvec3 LoadPoint(class cParser &Input);
|
|
|
|
// extracts a group of tokens from provided data stream
|
|
std::string deserialize_random_set(cParser &Input, char const *Break = "\n\r\t ;");
|
|
|
|
// extracts a group of tokens from provided data stream, returns all of them.
|
|
// a single token, not enclosed in "[]", is returned as one element sequence
|
|
std::vector<std::string> deserialize_set(cParser &Input, char const *Break = "\n\r\t ;");
|
|
|
|
// returns hash of provided string, combined with provided seed. FNV-1a, picked for having
|
|
// identical result regardless of platform and standard library, unlike std::hash
|
|
std::uint32_t hash_string(std::string const &String, std::uint32_t const Seed = 0);
|
|
|
|
// extracts a group of <key, value> pairs from provided data stream
|
|
// NOTE: expects no more than single pair per line
|
|
template <typename MapType_> void deserialize_map(MapType_ &Map, cParser &Input)
|
|
{
|
|
|
|
while (Input.ok() && !Input.eof())
|
|
{
|
|
auto const key{Input.getToken<typename MapType_::key_type>(false)};
|
|
auto const value{Input.getToken<typename MapType_::mapped_type>(false, "\n")};
|
|
Map.emplace(key, value);
|
|
}
|
|
}
|
|
|
|
namespace threading
|
|
{
|
|
|
|
// simple POD pairing of a data item and a mutex
|
|
// NOTE: doesn't do any locking itself, it's merely for cleaner argument arrangement and passing
|
|
template <typename Type_> struct lockable
|
|
{
|
|
|
|
Type_ data;
|
|
std::mutex mutex;
|
|
};
|
|
|
|
// basic wrapper simplifying use of std::condition_variable for most typical cases.
|
|
// has its own mutex and secondary variable to ignore spurious wakeups
|
|
class condition_variable
|
|
{
|
|
|
|
public:
|
|
// methods
|
|
void wait()
|
|
{
|
|
std::unique_lock lock(m_mutex);
|
|
m_condition.wait(lock, [this]() { return m_spurious == false; });
|
|
}
|
|
template <class Rep_, class Period_> void wait_for(const std::chrono::duration<Rep_, Period_> &Time)
|
|
{
|
|
std::unique_lock lock(m_mutex);
|
|
m_condition.wait_for(lock, Time, [this]() { return m_spurious == false; });
|
|
}
|
|
void notify_one()
|
|
{
|
|
spurious(false);
|
|
m_condition.notify_one();
|
|
}
|
|
void notify_all()
|
|
{
|
|
spurious(false);
|
|
m_condition.notify_all();
|
|
}
|
|
void spurious(bool const Spurious)
|
|
{
|
|
std::scoped_lock lock(m_mutex);
|
|
m_spurious = Spurious;
|
|
}
|
|
|
|
private:
|
|
// members
|
|
mutable std::mutex m_mutex;
|
|
std::condition_variable m_condition;
|
|
bool m_spurious{true};
|
|
};
|
|
|
|
} // namespace threading
|
|
|
|
//---------------------------------------------------------------------------
|