<commit_before>/**
 *	\file
 */

#pragma once

#include <rleahylib/rleahylib.hpp>
#include <fma.hpp>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <functional>
#include <limits>
#include <unordered_map>
#include <utility>
#include <type_traits>

#pragma GCC optimize ("fast-math")

namespace MCPP {

	/**
	 *	Generates simplex noise.
	 */
	class Simplex {

		private:

			Byte permutation [512];

			template <typename T>
			inline Byte get_random (T & gen, Byte bound) noexcept(noexcept(gen())) {

				typedef typename T::result_type stype;
				typedef typename std::make_unsigned<stype>::type type;

				union {
					type r;
					stype r_in;
				};

				//	If the bound is the maximum integer
				//	for the unsigned result type, it cannot
				//	be affected by modulo bias, so we simply
				//	return the next integer generated
				if (bound==std::numeric_limits<type>::max()) {

					r_in=gen();

					return r;

				}

				type d=static_cast<type>(bound)+1;
				//	This is the maximum number the random
				//	number generator may yield, divided
				//	by the divisor.
				type q=std::numeric_limits<type>::max()/d;

				//	Loop until we retrieve a random
				//	number unaffected by modulo bias
				//	over the given range
				do r_in=gen();
				while(
					//	This eliminates modulo bias by
					//	asking:
					//
					//	If I restrict the inputs to
					//	modulus to the range [(r/d)*d,((r/d)+1)*d)
					//	could I obtain all values within
					//	the bound given the maximum output
					//	of the random number generator?
					//
					//	If the answer is no a new number is
					//	obtained.
					//
					//	Example:
					//
					//	The function is called with a generator
					//	which generates [-128,127] (i.e. the range
					//	of a signed byte), and a bound of 5 (i.e.
					//	the output range is [0,5].
					//
					//	The union of r and r_in will map this range
					//	to an output range of [0,255] (i.e. the range
					//	of an unsigned byte).
					//
					//	If the random number generates a number on
					//	the range [252,255], modulo bias would occur.
					//
					//	Consider the outputs that would be yielded
					//	by blindly applying modulus to this range:
					//
					//	252 = 0
					//	253 = 1
					//	254 = 2
					//	255 = 3
					//
					//	The numbers 4 and 5 from the desired output
					//	range are not present, increasing the likelihood
					//	of obtaining outputs in the range [0,3].
					//
					//	In this case:
					//
					//	d=6
					//	q=255/6=42
					//
					//	Consider the case r=252:
					//
					//	(r/d)+1=43
					//
					//	This means that in order for 252 not to
					//	produce modulo bias (if not discarded)
					//	the random number generator would have to
					//	be capable of generating numbers up to
					//	at least (43*d)-1=257 (which it is not).
					//
					//	But we see that ((r/d)+1)>q, which
					//	causes this result to be discarded.
					((r/d)+1)>q
				);

				//	Current result will not produce
				//	modulo bias, apply modulus and return
				return static_cast<Byte>(r%d);

			}

			template <typename T>
			inline void init (T & gen) noexcept(noexcept(gen())) {

				//	Populate and shuffle the first half
				//	of the table using Fisher-Yates-Knuth
				//	shuffle
				for (Word i=0;i<(sizeof(permutation)/2);++i) {

					permutation[i]=static_cast<Byte>(i);

					std::swap(
						permutation[i],
						permutation[
							get_random(
								gen,
								static_cast<Byte>(i)
							)
						]
					);

				}

				//	Copy the first half of the table
				//	into the last half
				memcpy(
					&permutation[sizeof(permutation)/2],
					permutation,
					sizeof(permutation)/2
				);

			}

		public:

			/**
			 *	Creates a new simplex noise generator
			 *	with a randomly-chosen seed.
			 */
			Simplex ();
			/**
			 *	Creates a new simplex noise generator
			 *	seeded with a certain seed.
			 *
			 *	\param [in] seed
			 *		The seed which shall be used to
			 *		seed the noise generator.
			 */
			explicit Simplex (UInt64 seed) noexcept;
			/**
			 *	Creates a new simplex noise generator
			 *	seeded by a provided random number
			 *	generator.
			 *
			 *	\param [in] gen
			 *		A random number generator which
			 *		shall be used to seed the noise
			 *		generator.
			 */
			template <typename T, typename=typename std::enable_if<!std::numeric_limits<T>::is_integer>::type>
			explicit Simplex (T & gen) noexcept(noexcept(std::declval<Simplex>().init(gen))) {

				init(gen);

			}

			/**
			 *	Retrieves raw 2D noise.
			 *
			 *	\param [in] x
			 *		The x value.
			 *	\param [i] y
			 *		The y value.
			 *
			 *	\return
			 *		The raw noise output.
			 */
			Double operator () (Double x, Double y) const noexcept;
			/**
			 *	Retrieves raw 3D noise.
			 *
			 *	\param [in] x
			 *		The x value.
			 *	\param [in] y
			 *		The y value.
			 *	param [in] z
			 *		The z value.
			 *
			 *	\return
			 *		The raw noise output.
			 */
			Double operator () (Double x, Double y, Double z) const noexcept;
			/**
			 *	Retrieves raw 4D noise.
			 *
			 *	\param [in] w
			 *		The w value.
			 *	\param [in] x
			 *		The x value.
			 *	\param [in] y
			 *		The y value.
			 *	\param [in] z
			 *		The z value.
			 *
			 *	\return
			 *		The raw noise output.
			 */
			Double operator () (Double w, Double x, Double y, Double z) const noexcept;

	};

	/**
	 *	Selects a value from a range based on
	 *	a multiplier between 0 and 1.
	 *
	 *	\param [in] lo
	 *		The low end of the range to choose
	 *		from.
	 *	\param [in] hi
	 *		The high end of the range to choose
	 *		from.
	 *	\param [in] val
	 *		A value between zero and one (inclusive).
	 *		Input values close to one will yield
	 *		results close to \em hi, input values
	 *		close to zero will yield results close
	 *		to \em lo.
	 *
	 *	\return
	 *		A result chosen between \em lo and \em hi
	 *		according to \em val.  The exact algorithm
	 *		used shall be equivalent to ((hi-lo)*val)+lo.
	 */
	inline Double Select (Double lo, Double hi, Double val) noexcept {

		return fma(
			hi-lo,
			val,
			lo
		);

	}

	/**
	 *	Normalizes a value, yielding a value between 0
	 *	and 1 which specifies how far between a maximum
	 *	and minimum it is.
	 *
	 *	\param [in] lo
	 *		The number a \em val of 0 shall yield.
	 *	\param [in] hi
	 *		The number a \em val of 1 shall yield.
	 *	\param [in] val
	 *		A number between 0 and 1.
	 *
	 *	\return
	 *		A result chosen between \em lo and \em hi
	 *		such that this result multiplied by the
	 *		range gives the distance from \em lo.
	 *		For values of \em hi, 1 is returned, for
	 *		values of \em lo, 0 is returned, and all
	 *		values beteween 0 and 1 are placed proportionally
	 *		there between.
	 */
	inline Double Normalize (Double lo, Double hi, Double val) noexcept {

		if (lo==hi) {

			if (val==lo) return 0.5;
			if (val>lo) return 1;
			return 0;

		}

		bool complement;
		if (lo>hi) {

			complement=true;
			std::swap(lo,hi);

		} else {

			complement=false;

		}

		Double difference=val-lo;
		Double range=hi-lo;

		if (difference<0) return complement ? 1 : 0;
		if (difference>range) return complement ? 0 : 1;

		Double result=difference/range;

		return complement ? (1-result) : result;

	}

	/**
	 *	Given an input on a range, adjusts the value
	 *	such that it is on a different range.
	 *
	 *	\param [in] lo
	 *		The low end of the desired range.
	 *	\param [in] hi
	 *		The high end of the desired range.
	 *	\param [in] in_lo
	 *		The low end of the range of the
	 *		input.
	 *	\param [in] in_hi
	 *		The high end of the range of the
	 *		input.
	 *	\param [in] in
	 *		The input.
	 *
	 *	\return
	 *		The value that \em in would have were
	 *		it on the range given by \em lo, \em hi,
	 *		as opposed to the range \em in_lo, \em in_hi.
	 */
	inline Double Scale (Double lo, Double hi, Double in_lo, Double in_hi, Double in) noexcept {

		return Select(
			lo,
			hi,
			Normalize(
				in_lo,
				in_hi,
				in
			)
		);

	}

	/**
	 *	Given a simplex noise generator and
	 *	arguments to forward through to it,
	 *	adjusts the output of the noise
	 *	generator to place it on some
	 *	arbitrary range.
	 *
	 *	\tparam Args
	 *		The types of the arguments which
	 *		shall be forwarded through to the
	 *		simplex noise generator.
	 *
	 *	\param [in] lo
	 *		The low end of the desired range.
	 *	\param [in] hi
	 *		The high end of the desired range.
	 *	\param [in] gen
	 *		A reference to a simplex noise
	 *		generator.
	 *	\param [in] args
	 *		The arguments which shall be forwarded
	 *		through to \em gen.
	 *
	 *	\return
	 *		A noise value adjusted to the range
	 *		\em lo, \em hi.
	 */
	template <typename... Args>
	Double Scale (Double lo, Double hi, const Simplex & gen, Args &&... args) noexcept {

		return fma(
			gen(std::forward<Args>(args)...),
			(hi-lo)/2,
			(hi+lo)/2
		);

	}

	/**
	 *	\cond
	 */

	inline Double octave_helper (Double arg, Double frequency) noexcept {

		return arg*frequency;

	}

	/**
	 *	\endcond
	 */

	/**
	 *	Applies an octave filter to the output
	 *	of a certain generator.
	 *
	 *	\tparam T
	 *		The type of generator whose output
	 *		shall be filtered.
	 *	\tparam Args
	 *		The types of the arguments to forward
	 *		through to the generator.
	 *
	 *	\param [in] octaves
	 *		The number of octaves which shall be
	 *		applied.
	 *	\param [in] persistence
	 *		The persistence of the noise from each
	 *		octave.
	 *	\param [in] frequency
	 *		The starting sampling frequency.
	 *	\param [in] func
	 *		The generator.
	 *	\param [in] args
	 *		The arguments to forward through to
	 *		\em generator.
	 *
	 *	\return
	 *		A filtered value from \em func.
	 */
	template <typename T, typename... Args>
	Double Octave (Word octaves, Double persistence, Double frequency, T && func, const Args &... args) noexcept(
		noexcept(func(args...))
	) {

		Double total=0;
		Double amplitude=1;
		Double max_amp=0;

		for (Word i=0;i<octaves;++i) {

			total=fma(
				func(
					octave_helper(
						args,
						frequency
					)...
				),
				amplitude,
				total
			);

			frequency*=2;
			max_amp+=amplitude;
			amplitude*=persistence;

		}

		return total/max_amp;

	}

	/**
	 *	Applies a bias filter to a value.  Given a value
	 *	on the range [0,1], a bias filter pushes values
	 *	towards 1 for bias values greater than 0.5,
	 *	towards 0 for bias values less than 0.5, and
	 *	does nothing for bias values of exactly 0.5.
	 *
	 *	\param [in] bias
	 *		The bias value.
	 *	\param [in] input
	 *		Input on the range [0,1].
	 *
	 *	\return
	 *		A biased noise value.
	 */
	inline Double Bias (Double bias, Double input) noexcept {

		return pow(
			input,
			log(bias)/log(0.5)
		);

	}

	/**
	 *	Applies a gain filter to a value.  Given a value
	 *	on the range [0,1], a gain filter pushes values
	 *	towards 0 and 1 for gain values greater than 0.5,
	 *	towards 0.5 for gain values less than 0.5, and
	 *	does nothing for gain values of exactly 0.5.
	 *
	 *	\param [in] gain
	 *		The gain value.
	 *	\param [in] input
	 *		Input on the range [0,1].
	 *
	 *	\return
	 *		A filtered noise value.
	 */
	inline Double Gain (Double gain, Double input) noexcept {

		Double complement=1-gain;
		input*=2;

		return (
			(input<0.5)
				?
					(Bias(
						complement,
						input
					)/2)
				:	(1-(Bias(
						complement,
						2-input
					)/2))
		);

	}

	/**
	 *	Creates a ridged multifractal.
	 *
	 *	\param [in] input
	 *		A particular noise value.
	 *
	 *	\return
	 *		The corresponding value mapped to
	 *		a ridged multifractal.
	 */
	inline Double Ridged (Double input) noexcept {

		return 1-fabs(input);

	}

}

#pragma GCC reset_options
<commit_msg>Bias and Gain Improvements<commit_after>/**
 *	\file
 */

#pragma once

#include <rleahylib/rleahylib.hpp>
#include <fma.hpp>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <functional>
#include <limits>
#include <unordered_map>
#include <utility>
#include <type_traits>

#pragma GCC optimize ("fast-math")

namespace MCPP {

	/**
	 *	Generates simplex noise.
	 */
	class Simplex {

		private:

			Byte permutation [512];

			template <typename T>
			inline Byte get_random (T & gen, Byte bound) noexcept(noexcept(gen())) {

				typedef typename T::result_type stype;
				typedef typename std::make_unsigned<stype>::type type;

				union {
					type r;
					stype r_in;
				};

				//	If the bound is the maximum integer
				//	for the unsigned result type, it cannot
				//	be affected by modulo bias, so we simply
				//	return the next integer generated
				if (bound==std::numeric_limits<type>::max()) {

					r_in=gen();

					return r;

				}

				type d=static_cast<type>(bound)+1;
				//	This is the maximum number the random
				//	number generator may yield, divided
				//	by the divisor.
				type q=std::numeric_limits<type>::max()/d;

				//	Loop until we retrieve a random
				//	number unaffected by modulo bias
				//	over the given range
				do r_in=gen();
				while(
					//	This eliminates modulo bias by
					//	asking:
					//
					//	If I restrict the inputs to
					//	modulus to the range [(r/d)*d,((r/d)+1)*d)
					//	could I obtain all values within
					//	the bound given the maximum output
					//	of the random number generator?
					//
					//	If the answer is no a new number is
					//	obtained.
					//
					//	Example:
					//
					//	The function is called with a generator
					//	which generates [-128,127] (i.e. the range
					//	of a signed byte), and a bound of 5 (i.e.
					//	the output range is [0,5].
					//
					//	The union of r and r_in will map this range
					//	to an output range of [0,255] (i.e. the range
					//	of an unsigned byte).
					//
					//	If the random number generates a number on
					//	the range [252,255], modulo bias would occur.
					//
					//	Consider the outputs that would be yielded
					//	by blindly applying modulus to this range:
					//
					//	252 = 0
					//	253 = 1
					//	254 = 2
					//	255 = 3
					//
					//	The numbers 4 and 5 from the desired output
					//	range are not present, increasing the likelihood
					//	of obtaining outputs in the range [0,3].
					//
					//	In this case:
					//
					//	d=6
					//	q=255/6=42
					//
					//	Consider the case r=252:
					//
					//	(r/d)+1=43
					//
					//	This means that in order for 252 not to
					//	produce modulo bias (if not discarded)
					//	the random number generator would have to
					//	be capable of generating numbers up to
					//	at least (43*d)-1=257 (which it is not).
					//
					//	But we see that ((r/d)+1)>q, which
					//	causes this result to be discarded.
					((r/d)+1)>q
				);

				//	Current result will not produce
				//	modulo bias, apply modulus and return
				return static_cast<Byte>(r%d);

			}

			template <typename T>
			inline void init (T & gen) noexcept(noexcept(gen())) {

				//	Populate and shuffle the first half
				//	of the table using Fisher-Yates-Knuth
				//	shuffle
				for (Word i=0;i<(sizeof(permutation)/2);++i) {

					permutation[i]=static_cast<Byte>(i);

					std::swap(
						permutation[i],
						permutation[
							get_random(
								gen,
								static_cast<Byte>(i)
							)
						]
					);

				}

				//	Copy the first half of the table
				//	into the last half
				memcpy(
					&permutation[sizeof(permutation)/2],
					permutation,
					sizeof(permutation)/2
				);

			}

		public:

			/**
			 *	Creates a new simplex noise generator
			 *	with a randomly-chosen seed.
			 */
			Simplex ();
			/**
			 *	Creates a new simplex noise generator
			 *	seeded with a certain seed.
			 *
			 *	\param [in] seed
			 *		The seed which shall be used to
			 *		seed the noise generator.
			 */
			explicit Simplex (UInt64 seed) noexcept;
			/**
			 *	Creates a new simplex noise generator
			 *	seeded by a provided random number
			 *	generator.
			 *
			 *	\param [in] gen
			 *		A random number generator which
			 *		shall be used to seed the noise
			 *		generator.
			 */
			template <typename T, typename=typename std::enable_if<!std::numeric_limits<T>::is_integer>::type>
			explicit Simplex (T & gen) noexcept(noexcept(std::declval<Simplex>().init(gen))) {

				init(gen);

			}

			/**
			 *	Retrieves raw 2D noise.
			 *
			 *	\param [in] x
			 *		The x value.
			 *	\param [i] y
			 *		The y value.
			 *
			 *	\return
			 *		The raw noise output.
			 */
			Double operator () (Double x, Double y) const noexcept;
			/**
			 *	Retrieves raw 3D noise.
			 *
			 *	\param [in] x
			 *		The x value.
			 *	\param [in] y
			 *		The y value.
			 *	param [in] z
			 *		The z value.
			 *
			 *	\return
			 *		The raw noise output.
			 */
			Double operator () (Double x, Double y, Double z) const noexcept;
			/**
			 *	Retrieves raw 4D noise.
			 *
			 *	\param [in] w
			 *		The w value.
			 *	\param [in] x
			 *		The x value.
			 *	\param [in] y
			 *		The y value.
			 *	\param [in] z
			 *		The z value.
			 *
			 *	\return
			 *		The raw noise output.
			 */
			Double operator () (Double w, Double x, Double y, Double z) const noexcept;

	};

	/**
	 *	Selects a value from a range based on
	 *	a multiplier between 0 and 1.
	 *
	 *	\param [in] lo
	 *		The low end of the range to choose
	 *		from.
	 *	\param [in] hi
	 *		The high end of the range to choose
	 *		from.
	 *	\param [in] val
	 *		A value between zero and one (inclusive).
	 *		Input values close to one will yield
	 *		results close to \em hi, input values
	 *		close to zero will yield results close
	 *		to \em lo.
	 *
	 *	\return
	 *		A result chosen between \em lo and \em hi
	 *		according to \em val.  The exact algorithm
	 *		used shall be equivalent to ((hi-lo)*val)+lo.
	 */
	inline Double Select (Double lo, Double hi, Double val) noexcept {

		return fma(
			hi-lo,
			val,
			lo
		);

	}

	/**
	 *	Normalizes a value, yielding a value between 0
	 *	and 1 which specifies how far between a maximum
	 *	and minimum it is.
	 *
	 *	\param [in] lo
	 *		The number a \em val of 0 shall yield.
	 *	\param [in] hi
	 *		The number a \em val of 1 shall yield.
	 *	\param [in] val
	 *		A number between 0 and 1.
	 *
	 *	\return
	 *		A result chosen between \em lo and \em hi
	 *		such that this result multiplied by the
	 *		range gives the distance from \em lo.
	 *		For values of \em hi, 1 is returned, for
	 *		values of \em lo, 0 is returned, and all
	 *		values beteween 0 and 1 are placed proportionally
	 *		there between.
	 */
	inline Double Normalize (Double lo, Double hi, Double val) noexcept {

		if (lo==hi) {

			if (val==lo) return 0.5;
			if (val>lo) return 1;
			return 0;

		}

		bool complement;
		if (lo>hi) {

			complement=true;
			std::swap(lo,hi);

		} else {

			complement=false;

		}

		Double difference=val-lo;
		Double range=hi-lo;

		if (difference<0) return complement ? 1 : 0;
		if (difference>range) return complement ? 0 : 1;

		Double result=difference/range;

		return complement ? (1-result) : result;

	}

	/**
	 *	Given an input on a range, adjusts the value
	 *	such that it is on a different range.
	 *
	 *	\param [in] lo
	 *		The low end of the desired range.
	 *	\param [in] hi
	 *		The high end of the desired range.
	 *	\param [in] in_lo
	 *		The low end of the range of the
	 *		input.
	 *	\param [in] in_hi
	 *		The high end of the range of the
	 *		input.
	 *	\param [in] in
	 *		The input.
	 *
	 *	\return
	 *		The value that \em in would have were
	 *		it on the range given by \em lo, \em hi,
	 *		as opposed to the range \em in_lo, \em in_hi.
	 */
	inline Double Scale (Double lo, Double hi, Double in_lo, Double in_hi, Double in) noexcept {

		return Select(
			lo,
			hi,
			Normalize(
				in_lo,
				in_hi,
				in
			)
		);

	}

	/**
	 *	Given a simplex noise generator and
	 *	arguments to forward through to it,
	 *	adjusts the output of the noise
	 *	generator to place it on some
	 *	arbitrary range.
	 *
	 *	\tparam Args
	 *		The types of the arguments which
	 *		shall be forwarded through to the
	 *		simplex noise generator.
	 *
	 *	\param [in] lo
	 *		The low end of the desired range.
	 *	\param [in] hi
	 *		The high end of the desired range.
	 *	\param [in] gen
	 *		A reference to a simplex noise
	 *		generator.
	 *	\param [in] args
	 *		The arguments which shall be forwarded
	 *		through to \em gen.
	 *
	 *	\return
	 *		A noise value adjusted to the range
	 *		\em lo, \em hi.
	 */
	template <typename... Args>
	Double Scale (Double lo, Double hi, const Simplex & gen, Args &&... args) noexcept {

		return fma(
			gen(std::forward<Args>(args)...),
			(hi-lo)/2,
			(hi+lo)/2
		);

	}

	/**
	 *	\cond
	 */

	inline Double octave_helper (Double arg, Double frequency) noexcept {

		return arg*frequency;

	}

	/**
	 *	\endcond
	 */

	/**
	 *	Applies an octave filter to the output
	 *	of a certain generator.
	 *
	 *	\tparam T
	 *		The type of generator whose output
	 *		shall be filtered.
	 *	\tparam Args
	 *		The types of the arguments to forward
	 *		through to the generator.
	 *
	 *	\param [in] octaves
	 *		The number of octaves which shall be
	 *		applied.
	 *	\param [in] persistence
	 *		The persistence of the noise from each
	 *		octave.
	 *	\param [in] frequency
	 *		The starting sampling frequency.
	 *	\param [in] func
	 *		The generator.
	 *	\param [in] args
	 *		The arguments to forward through to
	 *		\em generator.
	 *
	 *	\return
	 *		A filtered value from \em func.
	 */
	template <typename T, typename... Args>
	Double Octave (Word octaves, Double persistence, Double frequency, T && func, const Args &... args) noexcept(
		noexcept(func(args...))
	) {

		Double total=0;
		Double amplitude=1;
		Double max_amp=0;

		for (Word i=0;i<octaves;++i) {

			total=fma(
				func(
					octave_helper(
						args,
						frequency
					)...
				),
				amplitude,
				total
			);

			frequency*=2;
			max_amp+=amplitude;
			amplitude*=persistence;

		}

		return total/max_amp;

	}

	/**
	 *	Applies a bias filter to a value.  Given a value
	 *	on the range [0,1], a bias filter pushes values
	 *	towards 1 for bias values greater than 0.5,
	 *	towards 0 for bias values less than 0.5, and
	 *	does nothing for bias values of exactly 0.5.
	 *
	 *	\param [in] bias
	 *		The bias value.
	 *	\param [in] input
	 *		Input on the range [0,1].
	 *
	 *	\return
	 *		A biased noise value.
	 */
	inline Double Bias (Double bias, Double input) noexcept {

		//	Do not do expensive pow + log x2 for
		//	bias values which would not change
		//	the input anyway.
		if (bias==0.5) return input;

		return pow(
			input,
			log(bias)/log(0.5)
		);

	}

	/**
	 *	Applies a gain filter to a value.  Given a value
	 *	on the range [0,1], a gain filter pushes values
	 *	towards 0 and 1 for gain values greater than 0.5,
	 *	towards 0.5 for gain values less than 0.5, and
	 *	does nothing for gain values of exactly 0.5.
	 *
	 *	\param [in] gain
	 *		The gain value.
	 *	\param [in] input
	 *		Input on the range [0,1].
	 *
	 *	\return
	 *		A filtered noise value.
	 */
	inline Double Gain (Double gain, Double input) noexcept {

		//	Do not do expensive bias etc. for gain
		//	values which would not change the input
		//	anyway
		if (gain==0.5) return input;

		Double complement=1-gain;
		input*=2;

		return (
			(input<0.5)
				?
					(Bias(
						complement,
						input
					)/2)
				:	(1-(Bias(
						complement,
						2-input
					)/2))
		);

	}

	/**
	 *	Creates a ridged multifractal.
	 *
	 *	\param [in] input
	 *		A particular noise value.
	 *
	 *	\return
	 *		The corresponding value mapped to
	 *		a ridged multifractal.
	 */
	inline Double Ridged (Double input) noexcept {

		return 1-fabs(input);

	}

}

#pragma GCC reset_options
<|endoftext|>


<commit_before>#include "stdafx.h"

#include "Model.h"

//void ConvertOBJ(std::string &f_path, std::string &f_out)
//{
//    try
//    {
//        std::vector<glm::vec3> temp_vertex;
//        std::vector<glm::vec2> temp_uv;
//        std::vector<glm::vec3> temp_normal;
//
//        std::vector<std::string> l_materialNames;
//        std::vector<std::string> l_materialTextureNames;
//        std::vector<unsigned char> l_materialTypes;
//        int l_currentMaterial = -1;
//        bool l_defaultMaterial = true;
//
//        std::ifstream l_objectFile;
//        l_objectFile.open(f_path + ".obj", std::ios::in);
//        if(l_objectFile.fail()) throw std::exception("Unable to load input file");
//
//        std::string l_buffer;
//
//        std::ifstream l_materialFile;
//        l_materialFile.open(f_path + ".mtl", std::ios::in);
//        if(!l_materialFile.fail())
//        {
//            l_defaultMaterial = false;
//            //Materials parsing
//            while(std::getline(l_materialFile, l_buffer))
//            {
//                if(l_buffer.empty()) continue;
//                if(l_buffer.at(0U) == 'N' || l_buffer.at(0U) == 'K' || l_buffer.at(0U) == 'd' || l_buffer.at(0U) == 'i') continue;
//                if(l_buffer.find("newmtl ") != std::string::npos)
//                {
//                    std::string l_materialName = l_buffer.substr(7U);
//                    l_materialNames.push_back(l_materialName);
//                    continue;
//                }
//                if(l_buffer.find("type ") != std::string::npos) // this is custom line, you have to insert it in file with value based on material bits
//                {
//                    std::string l_typeString = l_buffer.substr(5U);
//                    l_materialTypes.push_back(static_cast<unsigned char>(std::stoi(l_typeString)));
//                    continue;
//                }
//                if(l_buffer.find("map_Kd ") != std::string::npos || l_buffer.find("map_kd ") != std::string::npos)
//                {
//                    std::string l_textureName = l_buffer.substr(7U);
//                    l_textureName.insert(0U, "textures/");
//                    l_materialTextureNames.push_back(l_textureName);
//                    continue;
//                }
//            }
//            l_materialFile.close();
//        }
//        else std::cout << "No .mtl file, assumed that all materials are default" << std::endl;
//
//        if(l_materialNames.size() == 0U)
//        {
//            l_materialNames.push_back("DefaultMaterial");
//            l_currentMaterial = 0;
//        }
//
//        if(l_materialTextureNames.size() < l_materialNames.size())
//        {
//            for(size_t i = 0U, j = l_materialNames.size() - l_materialTextureNames.size(); i < j; i++)
//            {
//                l_materialTextureNames.push_back("");
//            }
//        }
//
//        if(l_materialTypes.size() < l_materialNames.size())
//        {
//            for(size_t i = 0U, j = l_materialNames.size() - l_materialTypes.size(); i < j; i++)
//            {
//                l_materialTypes.push_back(3U);
//            }
//        }
//
//        std::ofstream l_outputFile(f_out, std::ios::out | std::ios::binary);
//        if(l_outputFile.fail()) throw std::exception("Unable to create output file");
//
//        l_outputFile.write("ROC", 3U);
//        unsigned char l_setter = 0x1U;
//        l_outputFile.write(reinterpret_cast<char*>(&l_setter), sizeof(unsigned char));
//
//        bool l_dataParsed = false;
//        bool l_materialsSizeParsed = false;
//        std::vector<Face> l_faceVector;
//        std::vector<unsigned char> l_compressedData;
//
//        while(std::getline(l_objectFile, l_buffer))
//        {
//            if(l_buffer.empty()) continue;
//            if(l_buffer.at(0U) == '#' || l_buffer.at(0U) == 's' || l_buffer.at(0U) == 'm' || l_buffer.at(0U) == 'o') continue;
//            if(l_buffer.find("v ") != std::string::npos)
//            {
//                std::stringstream l_vertexStream(l_buffer.substr(2U));
//                glm::vec3 l_vert;
//                l_vertexStream >> l_vert.x >> l_vert.y >> l_vert.z;
//                temp_vertex.push_back(l_vert);
//                continue;
//            }
//            if(l_buffer.find("vn ") != std::string::npos)
//            {
//                std::stringstream l_normalStream(l_buffer.substr(3U));
//                glm::vec3 l_norm;
//                l_normalStream >> l_norm.x >> l_norm.y >> l_norm.z;
//                temp_normal.push_back(l_norm);
//                continue;
//            }
//            if(l_buffer.find("vt ") != std::string::npos)
//            {
//                std::stringstream l_uvStream(l_buffer.substr(3U));
//                glm::vec2 l_uv;
//                l_uvStream >> l_uv.x >> l_uv.y;
//                l_uv.y = 1.f - l_uv.y;
//                temp_uv.push_back(l_uv);
//                continue;
//            }
//            if(!l_dataParsed)
//            {
//                l_dataParsed = true;
//
//                unsigned int l_origSize = temp_vertex.size()*sizeof(glm::vec3);
//                unsigned int l_maxSize = GetMaxCompressedLen(l_origSize);
//                l_compressedData.resize(static_cast<size_t>(l_maxSize));
//                unsigned int l_compressedSize = CompressData(temp_vertex.data(), l_origSize, l_compressedData.data(), l_maxSize);
//                if(l_compressedSize == 0U) throw std::exception("Unable to compress vertices");
//                l_outputFile.write(reinterpret_cast<char*>(&l_compressedSize), sizeof(unsigned int));
//                l_outputFile.write(reinterpret_cast<char*>(&l_origSize), sizeof(unsigned int));
//                l_outputFile.write(reinterpret_cast<char*>(l_compressedData.data()), l_compressedSize);
//                std::cout << temp_vertex.size() << " vertices" << std::endl;
//
//                l_origSize = temp_uv.size()*sizeof(glm::vec2);
//                l_maxSize = GetMaxCompressedLen(l_origSize);
//                l_compressedData.resize(static_cast<size_t>(l_maxSize));
//                l_compressedSize = CompressData(temp_uv.data(), l_origSize, l_compressedData.data(), l_maxSize);
//                if(l_compressedSize == 0U) throw std::exception("Unable to compress UVs");
//                l_outputFile.write(reinterpret_cast<char*>(&l_compressedSize), sizeof(unsigned int));
//                l_outputFile.write(reinterpret_cast<char*>(&l_origSize), sizeof(unsigned int));
//                l_outputFile.write(reinterpret_cast<char*>(l_compressedData.data()), l_compressedSize);
//                std::cout << temp_vertex.size() << " UVs" << std::endl;
//
//                l_origSize = temp_normal.size()*sizeof(glm::vec3);
//                l_maxSize = GetMaxCompressedLen(l_origSize);
//                l_compressedData.resize(static_cast<size_t>(l_maxSize));
//                l_compressedSize = CompressData(temp_normal.data(), l_origSize, l_compressedData.data(), l_maxSize);
//                if(l_compressedSize == -1) throw std::exception("Unable to compress normals");
//                l_outputFile.write(reinterpret_cast<char*>(&l_compressedSize), sizeof(unsigned int));
//                l_outputFile.write(reinterpret_cast<char*>(&l_origSize), sizeof(unsigned int));
//                l_outputFile.write(reinterpret_cast<char*>(l_compressedData.data()), l_compressedSize);
//                std::cout << temp_vertex.size() << " normals" << std::endl;
//            }
//            if(l_buffer.find("usemtl ") != std::string::npos)
//            {
//                if(!l_materialsSizeParsed)
//                {
//                    int l_materialsSizeI = static_cast<int>(l_materialNames.size());
//                    l_outputFile.write(reinterpret_cast<char*>(&l_materialsSizeI), sizeof(int));
//                    l_materialsSizeParsed = true;
//                    std::cout << l_materialsSizeI << " material(s)" << std::endl;
//                }
//                if(l_defaultMaterial) continue;
//                if(l_currentMaterial != -1)
//                {
//                    std::cout << "Building material " << l_currentMaterial << " ..." << std::endl;
//
//                    l_setter = static_cast<unsigned char>(l_materialTypes[static_cast<size_t>(l_currentMaterial)]);
//                    l_outputFile.write(reinterpret_cast<char*>(&l_setter), sizeof(unsigned char));
//                    glm::vec4 l_params(1.f);
//                    l_outputFile.write(reinterpret_cast<char*>(&l_params), sizeof(glm::vec4));
//                    l_setter = static_cast<unsigned char>(l_materialTextureNames[static_cast<size_t>(l_currentMaterial)].size());
//                    l_outputFile.write(reinterpret_cast<char*>(&l_setter), sizeof(unsigned char));
//                    if(l_setter) l_outputFile.write(l_materialTextureNames[static_cast<size_t>(l_currentMaterial)].data(), l_setter);
//
//                    unsigned int l_origSize = l_faceVector.size()*sizeof(Face);
//                    unsigned int l_maxSize = GetMaxCompressedLen(l_origSize);
//                    l_compressedData.resize(static_cast<size_t>(l_maxSize));
//                    unsigned int l_compressedSize = CompressData(l_faceVector.data(), l_origSize, l_compressedData.data(), l_maxSize);
//                    if(l_compressedSize == 0U) throw std::exception("Unable to compress faces");
//                    l_outputFile.write(reinterpret_cast<char*>(&l_compressedSize), sizeof(unsigned int));
//                    l_outputFile.write(reinterpret_cast<char*>(&l_origSize), sizeof(unsigned int));
//                    l_outputFile.write(reinterpret_cast<char*>(l_compressedData.data()), l_compressedSize);
//                    std::cout << l_faceVector.size() << " faces" << std::endl;
//                    l_faceVector.clear();
//                }
//                std::string l_textureName = l_buffer.substr(7U);
//                l_currentMaterial = static_cast<int>(std::distance(l_materialNames.begin(), std::find(l_materialNames.begin(), l_materialNames.end(), l_textureName)));
//                if(l_currentMaterial >= int(l_materialNames.size()) || l_currentMaterial < 0) throw std::exception("Unable to parse materials");
//                continue;
//            }
//            if(l_buffer.find("f ") != std::string::npos)
//            {
//                if(!l_materialsSizeParsed)
//                {
//                    int l_materialsSizeI = static_cast<int>(l_materialNames.size());
//                    l_outputFile.write(reinterpret_cast<char*>(&l_materialsSizeI), sizeof(int));
//                    l_materialsSizeParsed = true;
//                    std::cout << l_materialsSizeI << " material(s)" << std::endl;
//                }
//                std::string l_faceString = l_buffer.substr(2U);
//                std::replace(l_faceString.begin(), l_faceString.end(), '/', ' ');
//                std::stringstream l_faceStream(l_faceString);
//                Face l_face;
//                l_faceStream >>
//                    l_face.m_materialIndices[0] >> l_face.m_materialIndices[3] >> l_face.m_materialIndices[6] >>
//                    l_face.m_materialIndices[1] >> l_face.m_materialIndices[4] >> l_face.m_materialIndices[7] >>
//                    l_face.m_materialIndices[2] >> l_face.m_materialIndices[5] >> l_face.m_materialIndices[8];
//                if(l_faceStream.fail()) throw std::exception("Invalid face line");
//
//                for(int i = 0; i < 9; i++) l_face.m_materialIndices[i]--;
//                l_faceVector.push_back(l_face);
//                continue;
//            }
//        }
//        if(l_currentMaterial != -1)
//        {
//            std::cout << "Building material " << l_currentMaterial << " ..." << std::endl;
//
//            l_setter = static_cast<unsigned char>(l_materialTypes[static_cast<size_t>(l_currentMaterial)]);
//            l_outputFile.write(reinterpret_cast<char*>(&l_setter), sizeof(unsigned char));
//            glm::vec4 l_params(1.f);
//            l_outputFile.write(reinterpret_cast<char*>(&l_params), sizeof(glm::vec4));
//            l_setter = static_cast<unsigned char>(l_materialTextureNames[static_cast<size_t>(l_currentMaterial)].size());
//            l_outputFile.write(reinterpret_cast<char*>(&l_setter), sizeof(unsigned char));
//            if(l_setter) l_outputFile.write(l_materialTextureNames[static_cast<size_t>(l_currentMaterial)].data(), l_setter);
//
//            unsigned int l_origSize = l_faceVector.size()*sizeof(Face);
//            unsigned int l_maxSize = GetMaxCompressedLen(l_origSize);
//            l_compressedData.resize(static_cast<size_t>(l_maxSize));
//            unsigned int l_compressedSize = CompressData(l_faceVector.data(), l_origSize, l_compressedData.data(), l_maxSize);
//            if(l_compressedSize == 0U) throw std::exception("Unable to compress faces");
//            l_outputFile.write(reinterpret_cast<char*>(&l_compressedSize), sizeof(unsigned int));
//            l_outputFile.write(reinterpret_cast<char*>(&l_origSize), sizeof(unsigned int));
//            l_outputFile.write(reinterpret_cast<char*>(l_compressedData.data()), l_compressedSize);
//            std::cout << l_faceVector.size() << " faces" << std::endl;
//            l_faceVector.clear();
//        }
//        l_objectFile.close();
//        l_outputFile.flush();
//        l_outputFile.close();
//
//        l_compressedData.clear();
//
//        std::cout << "Converted to " << f_out << std::endl;
//    }
//    catch(const std::exception &e)
//    {
//        std::cout << "Failed: " << e.what() << std::endl;
//
//        std::cout << "Cleaning ..." << std::endl;
//        std::remove(f_out.c_str());
//    }
//}

int main(int argc, char *argv[])
{
    if(argc > 1)
    {
        std::string l_inputFile(argv[1]);

        Model *l_model = new Model();
        bool l_loadResult = false;
        if(l_inputFile.find(".obj") != std::string::npos) l_loadResult = l_model->LoadOBJ(l_inputFile);
        else if(l_inputFile.find(".json") != std::string::npos) l_loadResult = l_model->LoadJSON(l_inputFile);
        else std::cout << "Error: Unknown model file for conversion. Only OBJ and THREE.js are supported" << std::endl;
        if(l_loadResult)
        {
            std::string l_outputFile(l_inputFile + ".rmf");
            l_model->Generate(l_outputFile);
        }
        delete l_model;
    }
    else std::cout << "Usage: [input_file]" << std::endl;
    std::cout << "Press any key to exit" << std::endl;
    std::getchar();
    return EXIT_SUCCESS;
}
<commit_msg>Forgotten commented old code<commit_after>#include "stdafx.h"

#include "Model.h"

int main(int argc, char *argv[])
{
    if(argc > 1)
    {
        std::string l_inputFile(argv[1]);

        Model *l_model = new Model();
        bool l_loadResult = false;
        if(l_inputFile.find(".obj") != std::string::npos) l_loadResult = l_model->LoadOBJ(l_inputFile);
        else if(l_inputFile.find(".json") != std::string::npos) l_loadResult = l_model->LoadJSON(l_inputFile);
        else std::cout << "Error: Unknown model file for conversion. Only OBJ and THREE.js are supported" << std::endl;
        if(l_loadResult)
        {
            std::string l_outputFile(l_inputFile + ".rmf");
            l_model->Generate(l_outputFile);
        }
        delete l_model;
    }
    else std::cout << "Usage: [input_file]" << std::endl;
    std::cout << "Press any key to exit" << std::endl;
    std::getchar();
    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>#include "file.h"

#include <sys/stat.h>

#include "format.h"
#include "log.h"
#ifdef _WIN32
#include <windows.h>
#endif

#include <unistd.h>
#include <iomanip>
#include <dirent.h>
#include <stdlib.h>

#ifdef APPLE
#include <mach-o/dyld.h>
#endif

#include <mutex>

#ifndef PATH_MAX
#define PATH_MAX 4096
#endif

namespace utils {

using namespace std;

File File::NO_FILE;

File File::appDir ("/usr/share/" APP_NAME_STR);
File File::userDir;
File File::cacheDir;
File File::configDir;
File File::exeDir;
File File::homeDir;

static mutex fm;

File::File() : size(-1), writeFP(nullptr), readFP(nullptr) {}

File::File(const string &name, const Mode mode) : fileName(rstrip(name, '/')), size(-1), writeFP(nullptr), readFP(nullptr) {
	if(mode != NONE)
		open(mode);
}

File::File(const string &parent, const string &name, const Mode mode) : size(-1), writeFP(nullptr), readFP(nullptr) {
	if(parent == "")
		fileName = rstrip(name, '/');
	else
		fileName = rstrip(parent, '/') + "/" + rstrip(name, '/');
	if(mode != NONE)
		open(mode);
}

vector<File> File::listFiles() const {
	DIR *dir;
	struct dirent *ent;
	vector<File> rc;
	if( (dir = opendir(fileName.c_str())) != nullptr) {
		while ((ent = readdir (dir)) != nullptr) {
			char *p = ent->d_name;
			if(p[0] == '.' && (p[1] == 0 || (p[1] == '.' && p[2] == 0)))
				continue;
			rc.emplace_back(fileName + "/" + ent->d_name);
		}
	}
	closedir(dir);
	return rc;
}

vector<uint8_t> File::readAll() {
	vector<uint8_t> data;
	seek(0);
	data.resize(getSize());
	int rc = read(&data[0], data.size());
	if(rc != data.size())
		throw io_exception{};
	return data;
}

void File::open(const Mode mode) {
	if(mode == READ) {
		if(!readFP) {
			readFP = fopen(fileName.c_str(), "rb");
			if(!readFP)
				throw file_not_found_exception(fileName);
		}
	} else if(mode == WRITE) {
		if(!writeFP) {
            //makedirs(fileName);
			writeFP = fopen(fileName.c_str(), "wb");
			if(!writeFP)
				throw io_exception { "Could not open file for writing" };
		}
	} else
		throw io_exception { "Can't open file with no mode" };

}

bool File::isChildOf(const File &f) const {
	string myPath = resolvePath(getName());
	string parentPath = resolvePath(f.getName());
	return (myPath.find(parentPath) == 0);
}

void File::seek(int64_t where) {
	open(READ);
	if(!readFP)
		throw file_not_found_exception(fileName);
	fseek(readFP, where, SEEK_SET);
}
int64_t File::tell() {
	open(READ);
	if(!readFP)
		throw file_not_found_exception(fileName);
	return ftell(readFP);
}

vector<string> File::getLines() {
	vector<string> lines;
	auto data = readAll();
	string source { reinterpret_cast<char*>(&data[0]), (unsigned int)data.size() };
	stringstream ss(source);
	string to;

	while(getline(ss, to)) {
		auto l = to.length()-1;
		while(to[l] == 10 || to[l] == 13)
			to = to.substr(0, l--);
		lines.push_back(to);
	}
	return lines;
}

void File::write(const uint8_t *data, const int size) {
	open(WRITE);
	fwrite(data, 1, size, writeFP);
}

void File::write(const string &data) {
	open(WRITE);
	fwrite(data.c_str(), 1, data.length(), writeFP);
}

void File::copyFrom(File &otherFile) {
	open(WRITE);
	const auto data = otherFile.readAll();
	fwrite(&data[0], 1, data.size(), writeFP);
}

void File::copyFrom(const string &other) {
	File f { other };
	copyFrom(f);
	f.close();
}

void File::close() {
	if(writeFP)
		fclose(writeFP);
	else if(readFP)
		fclose(readFP);
	writeFP = readFP = nullptr;
}

bool File::exists() const {
	struct stat ss;
	return (stat(fileName.c_str(), &ss) == 0);
}

bool File::exists(const string &fileName) {
	struct stat ss;
	return (stat(fileName.c_str(), &ss) == 0);
}

string File::makePath(vector<File> files) {
	string path = "";
	string sep = "";
	for(const File& f : files) {
		path = path + sep + f.getName();
		sep = PATH_SEPARATOR_STR;
	}
	return path;

}

File File::findFile(const string &path, const string &name) {
	LOGD("Find '%s'", name);
	if(name == "")
		return NO_FILE;
	auto parts = split(path, PATH_SEPARATOR_STR);
	for(string p : parts) {
		if(p.length() > 0) {
			if(p[p.length()-1] != '/')
				p += "/";
			LOGD("...in path %s", p);
			File f { p + name };
			if(f.exists())
				return f;
		}
	}
	return NO_FILE;
}

const File& File::getHomeDir() {
	if(!homeDir) {
	#ifdef _WIN32
		char path[MAX_PATH];
		string h = getenv("HOMEPATH");
		if(h[0] == '\\') {
			h = string("C:") + h;
			replace_char(h, '\\', '/');
		}
		homeDir = File(h);
	#else
		homeDir = File(getenv("HOME"));
	#endif
	}
	return homeDir;
}

static std::string getHome() { return File::getHomeDir().getName(); }

#ifdef APP_NAME

const File& File::getCacheDir() {
	lock_guard<mutex> lock(fm);
	if(!cacheDir) {
		string home = getHome();
#ifdef _WIN32
		replace_char(home, '\\', '/');
#endif
	auto d = format("%s/.cache/" APP_NAME_STR, home);
		LOGD("CACHE: %s", d);
		if(!exists(d))
			utils::makedirs(d);
		cacheDir = File(d);
	}
	return cacheDir;
}

const File& File::getConfigDir() {
	lock_guard<mutex> lock(fm);
	if(!configDir) {
		std::string home = getHome();
#ifdef _WIN32
		replace_char(home, '\\', '/');
#endif
	auto d = format("%s/.config/" APP_NAME_STR, home);
	LOGD("CACHE: %s", d);
	if(!exists(d))
	utils::makedirs(d);
	configDir = File(d);
	}
	return configDir;
}

const std::string File::getUserDir() {
	std::string home = getHome();
#ifdef _WIN32
	replace_char(home, '\\', '/');
#endif
	auto d = format("%s/" APP_NAME_STR, home);
	if(!exists(d))
	utils::makedirs(d);
	return d + "/";
}

#endif

uint64_t File::getModified() const {
	struct stat ss;
	if(stat(fileName.c_str(), &ss) != 0)
		throw io_exception {"Could not stat file"};
	return (uint64_t)ss.st_mtime;
}

uint64_t File::getModified(const std::string &fileName) {
	struct stat ss;
	if(stat(fileName.c_str(), &ss) != 0)
		throw io_exception {"Could not stat file"};
	return (uint64_t)ss.st_mtime;
}

int64_t File::getSize() const {
	if(size < 0) {
		struct stat ss;
		if(stat(fileName.c_str(), &ss) != 0)
			throw io_exception {"Could not stat file"};
		size = (uint64_t)ss.st_size;
	}
	return size;
}

bool File::isDir() const {
	struct stat ss;
	if(stat(fileName.c_str(), &ss) != 0)
		throw io_exception {"Could not stat file"};
	return S_ISDIR(ss.st_mode);
}

void File::remove() {
	close();
	if(std::remove(fileName.c_str()) != 0)
		throw io_exception {"Could not delete file"};
}

void File::rename(const std::string &newName) {
	if(std::rename(fileName.c_str(), newName.c_str()) != 0)
		throw io_exception {"Could not rename file"};
	fileName = newName;
}

void File::remove(const std::string &fileName) {
	if(std::remove(fileName.c_str()) != 0)
		throw io_exception {"Could not delete file"};
}

std::string File::read() {
	open(READ);
	auto data = readAll();
	return std::string(reinterpret_cast<const char *>(&data[0]), (unsigned long)data.size());
}

void File::copy(const std::string &from, const std::string &to) {
	File f0 { from };
	File f1 { to };
	f1.copyFrom(f0);
}

std::string File::resolvePath(const std::string &fileName) {
	char temp[PATH_MAX];
#ifdef _WIN32
	//if(GetFullPathNameA(fileName.c_str(), PATH_MAX, temp, NULL) > 0)
	if(_fullpath(temp, fileName.c_str(), PATH_MAX)) {
		replace_char(temp, '\\', '/');
		return std::string(temp);
	}
#else
	if(::realpath(fileName.c_str(), temp))
		return std::string(temp);
#endif
	return fileName;
}

File& File::resolve() {
	char temp[PATH_MAX];
#ifdef _WIN32
	if(_fullpath(temp, fileName.c_str(), PATH_MAX)) {
		replace_char(temp, '\\', '/');
		fileName = temp;
	}
#else
	if(::realpath(fileName.c_str(), temp)) {
		fileName = temp;
	}
#endif
	else
		fileName = "";
	return *this;
}

File File::cwd() {
	char temp[PATH_MAX];
	if(::getcwd(temp, sizeof(temp))) {
#ifdef _WIN32
		replace_char(temp, '\\', '/');
#endif
		return File(temp);
	}
	throw io_exception {"Could not get current directory"};
}

File::~File() {
	if(readFP)
		fclose(readFP);
	if(writeFP)
		fclose(writeFP);
}

const File& File::getExeDir() {
	lock_guard<mutex> lock(fm);

	if(!exeDir) {
		static char buf[1024];
	#if defined _WIN32
		GetModuleFileName(nullptr, buf, sizeof(buf)-1);
		replace_char(buf, '\\', '/');
		char *ptr = &buf[strlen(buf)-1];
		while(ptr > buf && *ptr != '/')
			*ptr-- = 0;
		*ptr = 0;
		exeDir = File(buf);
	#elif defined APPLE
		uint32_t size = sizeof(buf);
		if(_NSGetExecutablePath(buf, &size) == 0) {
			exeDir = File(path_directory(buf));
		}
	#elif defined UNIX
		int rc = readlink("/proc/self/exe", buf, sizeof(buf)-1);
		if(rc >= 0) {
			buf[rc] = 0;
			exeDir = File(path_directory(buf));
		}
	#endif
	}
	LOGD("EXEDIR:%s", exeDir.getName());
	exeDir.resolve();
	return exeDir;
}

void File::setAppDir(const std::string &a) {
	lock_guard<mutex> lock(fm);
	LOGD("Setting appdir to %s", a);
	appDir = a;
}

const File& File::getAppDir() {
	lock_guard<mutex> lock(fm);
	if(!appDir) {
		if(APP_NAME_STR != "")
			appDir = File("/usr/share/" APP_NAME_STR);
		else
			throw io_exception("No appDir specified");
	}
	return appDir;
}

void File::writeln(const std::string &line) {
	write(line + "\n");
}

File File::changeSuffix(const std::string &ext) {
	int dot = fileName.find_last_of('.');
	if(dot != string::npos)
		return File(fileName.substr(0, dot) + ext);
	return File(fileName + ext);
}

std::string File::suffix() const {

	int dot = fileName.find_last_of('.');
	if(dot != string::npos)
		return fileName.substr(dot);
	return "";
}

} // namespace

#ifdef UNIT_TEST

#include "catch.hpp"

TEST_CASE("utils::File", "File operations") {

	using namespace utils;
	using namespace std;

	// Delete to be safe
	std::remove("temp.text");

	// File
	File file { "temp.text" };

	REQUIRE(file.getName() == "temp.text");

	file.write("First line\nSecond line");
	file.close();
	REQUIRE(file.exists());
	REQUIRE(file.getSize() > 5);
	REQUIRE(file.getSize() < 50);

	file = File { "temp.text" };

	auto data = file.readAll();
	REQUIRE(data.size() > 0);

	vector<string> lines = file.getLines();

	REQUIRE(lines.size() == 2);

	file.remove();

	REQUIRE(!file.exists());

}

#endif
<commit_msg>Mem trash fix in File::getLines<commit_after>#include "file.h"

#include <sys/stat.h>

#include "format.h"
#include "log.h"
#ifdef _WIN32
#include <windows.h>
#endif

#include <unistd.h>
#include <iomanip>
#include <dirent.h>
#include <stdlib.h>

#ifdef APPLE
#include <mach-o/dyld.h>
#endif

#include <mutex>

#ifndef PATH_MAX
#define PATH_MAX 4096
#endif

namespace utils {

using namespace std;

File File::NO_FILE;

File File::appDir ("/usr/share/" APP_NAME_STR);
File File::userDir;
File File::cacheDir;
File File::configDir;
File File::exeDir;
File File::homeDir;

static mutex fm;

File::File() : size(-1), writeFP(nullptr), readFP(nullptr) {}

File::File(const string &name, const Mode mode) : fileName(rstrip(name, '/')), size(-1), writeFP(nullptr), readFP(nullptr) {
	if(mode != NONE)
		open(mode);
}

File::File(const string &parent, const string &name, const Mode mode) : size(-1), writeFP(nullptr), readFP(nullptr) {
	if(parent == "")
		fileName = rstrip(name, '/');
	else
		fileName = rstrip(parent, '/') + "/" + rstrip(name, '/');
	if(mode != NONE)
		open(mode);
}

vector<File> File::listFiles() const {
	DIR *dir;
	struct dirent *ent;
	vector<File> rc;
	if( (dir = opendir(fileName.c_str())) != nullptr) {
		while ((ent = readdir (dir)) != nullptr) {
			char *p = ent->d_name;
			if(p[0] == '.' && (p[1] == 0 || (p[1] == '.' && p[2] == 0)))
				continue;
			rc.emplace_back(fileName + "/" + ent->d_name);
		}
	}
	closedir(dir);
	return rc;
}

vector<uint8_t> File::readAll() {
	vector<uint8_t> data;
	seek(0);
	data.resize(getSize());
	int rc = read(&data[0], data.size());
	if(rc != data.size())
		throw io_exception{};
	return data;
}

void File::open(const Mode mode) {
	if(mode == READ) {
		if(!readFP) {
			readFP = fopen(fileName.c_str(), "rb");
			if(!readFP)
				throw file_not_found_exception(fileName);
		}
	} else if(mode == WRITE) {
		if(!writeFP) {
            //makedirs(fileName);
			writeFP = fopen(fileName.c_str(), "wb");
			if(!writeFP)
				throw io_exception { "Could not open file for writing" };
		}
	} else
		throw io_exception { "Can't open file with no mode" };

}

bool File::isChildOf(const File &f) const {
	string myPath = resolvePath(getName());
	string parentPath = resolvePath(f.getName());
	return (myPath.find(parentPath) == 0);
}

void File::seek(int64_t where) {
	open(READ);
	if(!readFP)
		throw file_not_found_exception(fileName);
	fseek(readFP, where, SEEK_SET);
}
int64_t File::tell() {
	open(READ);
	if(!readFP)
		throw file_not_found_exception(fileName);
	return ftell(readFP);
}

vector<string> File::getLines() {
	vector<string> lines;
	auto data = readAll();
	string source { reinterpret_cast<char*>(&data[0]), (unsigned int)data.size() };
	stringstream ss(source);
	string to;

	while(getline(ss, to)) {
		auto l = to.length();
		while(l > 0 && (to[l-1] == 10 || to[l-1] == 13)) l--;
		lines.push_back(to.substr(0,l));
	}
	return lines;
}

void File::write(const uint8_t *data, const int size) {
	open(WRITE);
	fwrite(data, 1, size, writeFP);
}

void File::write(const string &data) {
	open(WRITE);
	fwrite(data.c_str(), 1, data.length(), writeFP);
}

void File::copyFrom(File &otherFile) {
	open(WRITE);
	const auto data = otherFile.readAll();
	fwrite(&data[0], 1, data.size(), writeFP);
}

void File::copyFrom(const string &other) {
	File f { other };
	copyFrom(f);
	f.close();
}

void File::close() {
	if(writeFP)
		fclose(writeFP);
	else if(readFP)
		fclose(readFP);
	writeFP = readFP = nullptr;
}

bool File::exists() const {
	struct stat ss;
	return (stat(fileName.c_str(), &ss) == 0);
}

bool File::exists(const string &fileName) {
	struct stat ss;
	return (stat(fileName.c_str(), &ss) == 0);
}

string File::makePath(vector<File> files) {
	string path = "";
	string sep = "";
	for(const File& f : files) {
		path = path + sep + f.getName();
		sep = PATH_SEPARATOR_STR;
	}
	return path;

}

File File::findFile(const string &path, const string &name) {
	LOGD("Find '%s'", name);
	if(name == "")
		return NO_FILE;
	auto parts = split(path, PATH_SEPARATOR_STR);
	for(string p : parts) {
		if(p.length() > 0) {
			if(p[p.length()-1] != '/')
				p += "/";
			LOGD("...in path %s", p);
			File f { p + name };
			if(f.exists())
				return f;
		}
	}
	return NO_FILE;
}

const File& File::getHomeDir() {
	if(!homeDir) {
	#ifdef _WIN32
		char path[MAX_PATH];
		string h = getenv("HOMEPATH");
		if(h[0] == '\\') {
			h = string("C:") + h;
			replace_char(h, '\\', '/');
		}
		homeDir = File(h);
	#else
		homeDir = File(getenv("HOME"));
	#endif
	}
	return homeDir;
}

static std::string getHome() { return File::getHomeDir().getName(); }

#ifdef APP_NAME

const File& File::getCacheDir() {
	lock_guard<mutex> lock(fm);
	if(!cacheDir) {
		string home = getHome();
#ifdef _WIN32
		replace_char(home, '\\', '/');
#endif
	auto d = format("%s/.cache/" APP_NAME_STR, home);
		LOGD("CACHE: %s", d);
		if(!exists(d))
			utils::makedirs(d);
		cacheDir = File(d);
	}
	return cacheDir;
}

const File& File::getConfigDir() {
	lock_guard<mutex> lock(fm);
	if(!configDir) {
		std::string home = getHome();
#ifdef _WIN32
		replace_char(home, '\\', '/');
#endif
	auto d = format("%s/.config/" APP_NAME_STR, home);
	LOGD("CACHE: %s", d);
	if(!exists(d))
	utils::makedirs(d);
	configDir = File(d);
	}
	return configDir;
}

const std::string File::getUserDir() {
	std::string home = getHome();
#ifdef _WIN32
	replace_char(home, '\\', '/');
#endif
	auto d = format("%s/" APP_NAME_STR, home);
	if(!exists(d))
	utils::makedirs(d);
	return d + "/";
}

#endif

uint64_t File::getModified() const {
	struct stat ss;
	if(stat(fileName.c_str(), &ss) != 0)
		throw io_exception {"Could not stat file"};
	return (uint64_t)ss.st_mtime;
}

uint64_t File::getModified(const std::string &fileName) {
	struct stat ss;
	if(stat(fileName.c_str(), &ss) != 0)
		throw io_exception {"Could not stat file"};
	return (uint64_t)ss.st_mtime;
}

int64_t File::getSize() const {
	if(size < 0) {
		struct stat ss;
		if(stat(fileName.c_str(), &ss) != 0)
			throw io_exception {"Could not stat file"};
		size = (uint64_t)ss.st_size;
	}
	return size;
}

bool File::isDir() const {
	struct stat ss;
	if(stat(fileName.c_str(), &ss) != 0)
		throw io_exception {"Could not stat file"};
	return S_ISDIR(ss.st_mode);
}

void File::remove() {
	close();
	if(std::remove(fileName.c_str()) != 0)
		throw io_exception {"Could not delete file"};
}

void File::rename(const std::string &newName) {
	if(std::rename(fileName.c_str(), newName.c_str()) != 0)
		throw io_exception {"Could not rename file"};
	fileName = newName;
}

void File::remove(const std::string &fileName) {
	if(std::remove(fileName.c_str()) != 0)
		throw io_exception {"Could not delete file"};
}

std::string File::read() {
	open(READ);
	auto data = readAll();
	return std::string(reinterpret_cast<const char *>(&data[0]), (unsigned long)data.size());
}

void File::copy(const std::string &from, const std::string &to) {
	File f0 { from };
	File f1 { to };
	f1.copyFrom(f0);
}

std::string File::resolvePath(const std::string &fileName) {
	char temp[PATH_MAX];
#ifdef _WIN32
	//if(GetFullPathNameA(fileName.c_str(), PATH_MAX, temp, NULL) > 0)
	if(_fullpath(temp, fileName.c_str(), PATH_MAX)) {
		replace_char(temp, '\\', '/');
		return std::string(temp);
	}
#else
	if(::realpath(fileName.c_str(), temp))
		return std::string(temp);
#endif
	return fileName;
}

File& File::resolve() {
	char temp[PATH_MAX];
#ifdef _WIN32
	if(_fullpath(temp, fileName.c_str(), PATH_MAX)) {
		replace_char(temp, '\\', '/');
		fileName = temp;
	}
#else
	if(::realpath(fileName.c_str(), temp)) {
		fileName = temp;
	}
#endif
	else
		fileName = "";
	return *this;
}

File File::cwd() {
	char temp[PATH_MAX];
	if(::getcwd(temp, sizeof(temp))) {
#ifdef _WIN32
		replace_char(temp, '\\', '/');
#endif
		return File(temp);
	}
	throw io_exception {"Could not get current directory"};
}

File::~File() {
	if(readFP)
		fclose(readFP);
	if(writeFP)
		fclose(writeFP);
}

const File& File::getExeDir() {
	lock_guard<mutex> lock(fm);

	if(!exeDir) {
		static char buf[1024];
	#if defined _WIN32
		GetModuleFileName(nullptr, buf, sizeof(buf)-1);
		replace_char(buf, '\\', '/');
		char *ptr = &buf[strlen(buf)-1];
		while(ptr > buf && *ptr != '/')
			*ptr-- = 0;
		*ptr = 0;
		exeDir = File(buf);
	#elif defined APPLE
		uint32_t size = sizeof(buf);
		if(_NSGetExecutablePath(buf, &size) == 0) {
			exeDir = File(path_directory(buf));
		}
	#elif defined UNIX
		int rc = readlink("/proc/self/exe", buf, sizeof(buf)-1);
		if(rc >= 0) {
			buf[rc] = 0;
			exeDir = File(path_directory(buf));
		}
	#endif
	}
	LOGD("EXEDIR:%s", exeDir.getName());
	exeDir.resolve();
	return exeDir;
}

void File::setAppDir(const std::string &a) {
	lock_guard<mutex> lock(fm);
	LOGD("Setting appdir to %s", a);
	appDir = a;
}

const File& File::getAppDir() {
	lock_guard<mutex> lock(fm);
	if(!appDir) {
		if(APP_NAME_STR != "")
			appDir = File("/usr/share/" APP_NAME_STR);
		else
			throw io_exception("No appDir specified");
	}
	return appDir;
}

void File::writeln(const std::string &line) {
	write(line + "\n");
}

File File::changeSuffix(const std::string &ext) {
	int dot = fileName.find_last_of('.');
	if(dot != string::npos)
		return File(fileName.substr(0, dot) + ext);
	return File(fileName + ext);
}

std::string File::suffix() const {

	int dot = fileName.find_last_of('.');
	if(dot != string::npos)
		return fileName.substr(dot);
	return "";
}

} // namespace

#ifdef UNIT_TEST

#include "catch.hpp"

TEST_CASE("utils::File", "File operations") {

	using namespace utils;
	using namespace std;

	// Delete to be safe
	std::remove("temp.text");

	// File
	File file { "temp.text" };

	REQUIRE(file.getName() == "temp.text");

	file.write("First line\nSecond line");
	file.close();
	REQUIRE(file.exists());
	REQUIRE(file.getSize() > 5);
	REQUIRE(file.getSize() < 50);

	file = File { "temp.text" };

	auto data = file.readAll();
	REQUIRE(data.size() > 0);

	vector<string> lines = file.getLines();

	REQUIRE(lines.size() == 2);

	file.remove();

	REQUIRE(!file.exists());

}

#endif
<|endoftext|>


<commit_before>#ifndef stack_cpp
#define stack_cpp
#pragma once
#include <iostream>

template <typename T1, typename T2>
void construct(T1 * ptr, T2 const & value) {
    new(ptr) T1 (value);
}

template <typename T>
void destroy(T * ptr) noexcept
{
    ptr->~T();
}

template <typename FwdIter>
void destroy(FwdIter first, FwdIter last) noexcept
{
    for (; first != last; ++first) {
        destroy(&*first);
    }
}

template <typename T>
class allocator
{
protected:
    allocator(size_t size = 0);
    ~allocator();
    auto swap(allocator & other) -> void;

    allocator(allocator const &) = delete;
    auto operator =(allocator const &) -> allocator & = delete;

    T * ptr_;
    size_t size_;
    size_t count_;
};

template <typename T>
allocator<T>::allocator(size_t size) : ptr_((T*)(operator new(size*sizeof(T)))), size_(size), count_(0){};

template<typename T>
allocator<T>::~allocator()
{
	operator delete(ptr_);
}

template<typename T>
auto allocator<T>::swap(allocator & other) -> void
{
	std::swap(ptr_, other.ptr_);
	std::swap(count_, other.count_);
	std::swap(size_, other.size_);
}

template <typename T>
class stack : private allocator<T>
{
public:
	stack(size_t size = 0);								//noexcept
	stack(stack const &);								//strong
	~stack();									//noexcept
	size_t count() const;								//noexcept
	auto push(T const &) -> void;							//strong
	void pop();									//basic
	const T& top();									//strong
	auto operator=(stack const & right)->stack &;					//strong
	auto empty() const -> bool; 							//noexcept
};
template<typename T>
auto newcopy(const T * item, size_t size, size_t count) -> T* 				//strong
{
	T * buff = new T[size];
	try
	{
		std::copy(item, item + count, buff);
	}
	catch (...)
	{
		delete[] buff;
		throw;
	}
	return buff;
}
template <typename T>
size_t stack<T>::count() const
{
	return allocator<T>::count_;
}
template <typename T>
stack<T>::stack(size_t size):allocator<T>(size){}

template<typename T>
stack<T>::stack(stack const & other) : allocator<T>(other.size_)
{
for (size_t i = 0; i < other.count_; i++) construct(allocator<T>::ptr_ + i, other.ptr_[i]);
	allocator<T>::count_ = other.count_;
};

template <typename T>
stack<T>::~stack()
{
	delete[] array_;
}

template<typename T>
void stack<T>::push(T const &item)
{
	if (allocator<T>::count_ == allocator<T>::array_size_)
	{
		size_t array_size = allocator<T>::size_ * 2 + (allocator<T>::size_ == 0);
		stack<T> temp(array_size);
		while (temp.count() < allocator<T>::count_) temp.push(allocator<T>::ptr_[temp.count()]);
		this->swap(temp);
	}
	construct(allocator<T>::ptr_ + allocator<T>::count_, item);
	++allocator<T>::count_;
}
template<typename T>
void stack<T>::pop()
{
	if (allocator<T>::count_ == 0)
	{
		throw ("Stack is empty!");
	}
	else
	{
		allocator<T>::count_--;
	}
}

template<typename T>
const T& stack<T>::top()
{
	if (allocator<T>::count_ == 0)
	{
		throw ("Stack is empty!");
	}
	return allocator<T>::ptr_[allocator<T>::count_ - 1];
}

template<typename T>
auto stack<T>::operator=(stack const & right) -> stack &
{
	if (this != &right)
	{
	stack<T> temp (right.size_);
	while (temp.count_ < right.count_)
	{
		construct(temp.ptr_ + temp.count_, right.ptr_[temp.count_]);
		++temp.count_;
	}
	this -> swap(temp);
	}
	return *this;
}

template<typename T>
auto stack<T>::empty() const -> bool
{
if (allocator<T>::count_ == 0)
	{
		return true;
	}
	else
	{
		return false;
	}
}
#endif
<commit_msg>Update stack.cpp<commit_after>#ifndef stack_cpp
#define stack_cpp
#pragma once
#include <iostream>

template <typename T1, typename T2>
void construct(T1 * ptr, T2 const & value) {
    new(ptr) T1 (value);
}

template <typename T>
void destroy(T * ptr) noexcept
{
    ptr->~T();
}

template <typename FwdIter>
void destroy(FwdIter first, FwdIter last) noexcept
{
    for (; first != last; ++first) {
        destroy(&*first);
    }
}

template <typename T>
class allocator
{
protected:
    allocator(size_t size = 0);
    ~allocator();
    auto swap(allocator & other) -> void;

    allocator(allocator const &) = delete;
    auto operator =(allocator const &) -> allocator & = delete;

    T * ptr_;
    size_t size_;
    size_t count_;
};

template <typename T>
allocator<T>::allocator(size_t size) : ptr_((T*)(operator new(size*sizeof(T)))), size_(size), count_(0){};

template<typename T>
allocator<T>::~allocator()
{
	operator delete(ptr_);
}

template<typename T>
auto allocator<T>::swap(allocator & other) -> void
{
	std::swap(ptr_, other.ptr_);
	std::swap(count_, other.count_);
	std::swap(size_, other.size_);
}

template <typename T>
class stack : private allocator<T>
{
public:
	stack(size_t size = 0);								//noexcept
	stack(stack const &);								//strong
	~stack();									//noexcept
	size_t count() const;								//noexcept
	auto push(T const &) -> void;							//strong
	void pop();									//basic
	const T& top();									//strong
	auto operator=(stack const & right)->stack &;					//strong
	auto empty() const -> bool; 							//noexcept
};
template<typename T>
auto newcopy(const T * item, size_t size, size_t count) -> T* 				//strong
{
	T * buff = new T[size];
	try
	{
		std::copy(item, item + count, buff);
	}
	catch (...)
	{
		delete[] buff;
		throw;
	}
	return buff;
}
template <typename T>
size_t stack<T>::count() const
{
	return allocator<T>::count_;
}
template <typename T>
stack<T>::stack(size_t size):allocator<T>(size){}

template<typename T>
stack<T>::stack(stack const & other) : allocator<T>(other.size_)
{
for (size_t i = 0; i < other.count_; i++) construct(allocator<T>::ptr_ + i, other.ptr_[i]);
	allocator<T>::count_ = other.count_;
};

template <typename T>
stack<T>::~stack()
{
	delete[] array_;
}

template<typename T>
void stack<T>::push(T const &item)
{
	if (allocator<T>::count_ == allocator<T>::size_)
	{
		size_t array_size = allocator<T>::size_ * 2 + (allocator<T>::size_ == 0);
		stack<T> temp(array_size);
		while (temp.count() < allocator<T>::count_) temp.push(allocator<T>::ptr_[temp.count()]);
		this->swap(temp);
	}
	construct(allocator<T>::ptr_ + allocator<T>::count_, item);
	++allocator<T>::count_;
}
template<typename T>
void stack<T>::pop()
{
	if (allocator<T>::count_ == 0)
	{
		throw ("Stack is empty!");
	}
	else
	{
		allocator<T>::count_--;
	}
}

template<typename T>
const T& stack<T>::top()
{
	if (allocator<T>::count_ == 0)
	{
		throw ("Stack is empty!");
	}
	return allocator<T>::ptr_[allocator<T>::count_ - 1];
}

template<typename T>
auto stack<T>::operator=(stack const & right) -> stack &
{
	if (this != &right)
	{
	stack<T> temp (right.size_);
	while (temp.count_ < right.count_)
	{
		construct(temp.ptr_ + temp.count_, right.ptr_[temp.count_]);
		++temp.count_;
	}
	this -> swap(temp);
	}
	return *this;
}

template<typename T>
auto stack<T>::empty() const -> bool
{
if (allocator<T>::count_ == 0)
	{
		return true;
	}
	else
	{
		return false;
	}
}
#endif
<|endoftext|>


<commit_before>

#include "../Flare.h"
#include "FlareTravel.h"
#include "FlareWorld.h"
#include "FlareGame.h"
#include "FlareGameTools.h"
#include "../Player/FlarePlayerController.h"
#include "../UI/Components/FlareNotification.h"
#include "../Economy/FlareCargoBay.h"

static const double TRAVEL_DURATION_PER_PHASE_KM = 0.4;
static const double TRAVEL_DURATION_PER_ALTITUDE_KM = 6;

#define LOCTEXT_NAMESPACE "FlareTravelInfos"

/*----------------------------------------------------
	Constructor
----------------------------------------------------*/

UFlareTravel::UFlareTravel(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
}

void UFlareTravel::Load(const FFlareTravelSave& Data)
{
	Game = Cast<UFlareWorld>(GetOuter())->GetGame();
	TravelData = Data;
	TravelShips.Empty();

	Fleet = Game->GetGameWorld()->FindFleet(TravelData.FleetIdentifier);
	DestinationSector = Game->GetGameWorld()->FindSector(TravelData.DestinationSectorIdentifier);
	OriginSector = Game->GetGameWorld()->FindSector(TravelData.OriginSectorIdentifier);

	for (int ShipIndex = 0; ShipIndex < Fleet->GetShips().Num(); ShipIndex++)
	{
		TravelShips.Add(Fleet->GetShips()[ShipIndex]);
	}

	Fleet->SetCurrentTravel(this);
	GenerateTravelDuration();

	FFlareSectorOrbitParameters OrbitParameters;
	OrbitParameters = *OriginSector->GetOrbitParameters();

	SectorDescription.Name = LOCTEXT("TravelSectorName", "Travelling ...");
	SectorDescription.Description = LOCTEXT("TravelSectorDescription", "Travel local sector");
	SectorDescription.Identifier=Fleet->GetIdentifier();
	SectorDescription.Phase = 0;
	SectorDescription.IsPeaceful = true;
	SectorDescription.IsIcy = false;
	SectorDescription.IsGeostationary = false;
	SectorDescription.IsSolarPoor = false;
	SectorDescription.LevelName = NAME_None;
	SectorDescription.DebrisFieldInfo.DebrisCatalog = NULL;
	SectorDescription.LevelTrack = EFlareMusicTrack::Pacific;
	SectorDescription.DebrisFieldInfo.DebrisFieldDensity = 0;
	SectorDescription.DebrisFieldInfo.MaxDebrisSize = 0;
	SectorDescription.DebrisFieldInfo.MinDebrisSize = 0;

	TravelSector = NewObject<UFlareSimulatedSector>(this, UFlareSimulatedSector::StaticClass());
	TravelSector->Load(&SectorDescription, Data.SectorData, OrbitParameters);

	Fleet->SetCurrentSector(TravelSector);
	TravelSector->AddFleet(Fleet);

}

FFlareTravelSave* UFlareTravel::Save()
{
	return &TravelData;
}

/*----------------------------------------------------
	Gameplay
----------------------------------------------------*/

void UFlareTravel::Simulate()
{
	if (GetRemainingTravelDuration() <= 0)
	{
		EndTravel();
	}
}

void UFlareTravel::EndTravel()
{
	Fleet->SetCurrentSector(DestinationSector);
	DestinationSector->AddFleet(Fleet);

	// Place correctly new ships to avoid collision

	// TODO People migration

	// Price migration
	for(int32 ResourceIndex = 0; ResourceIndex < Game->GetResourceCatalog()->Resources.Num(); ResourceIndex++)
	{
		FFlareResourceDescription* Resource = &Game->GetResourceCatalog()->Resources[ResourceIndex]->Data;

		float ContaminationFactor = 0.00f;

		for (int ShipIndex = 0; ShipIndex < Fleet->GetShips().Num(); ShipIndex++)
		{
			UFlareSimulatedSpacecraft* Ship = Fleet->GetShips()[ShipIndex];
			if (Ship->GetCargoBay()->GetResourceQuantity(Resource, Ship->GetCompany()) > 0)
			{
				ContaminationFactor += Ship->GetCargoBay()->GetResourceQuantity(Resource, Ship->GetCompany()) / 1000.f;
				//TODO scale bay world stock/flow
			}
		}

		ContaminationFactor = FMath::Min(ContaminationFactor, 1.f);

		float OriginPrice = OriginSector->GetPreciseResourcePrice(Resource);
		float DestinationPrice = DestinationSector->GetPreciseResourcePrice(Resource);

		float Mean = (OriginPrice + DestinationPrice) / 2.f;

		float NewOriginPrice = (OriginPrice * (1 - ContaminationFactor)) + (ContaminationFactor * Mean);
		float NewDestinationPrice = (DestinationPrice * (1 - ContaminationFactor)) + (ContaminationFactor * Mean);

		//FLOGV("Travel start from %s. %s price ajusted from %f to %f (Mean: %f)", *OriginSector->GetSectorName().ToString(), *Resource->Name.ToString(), OriginPrice/100., NewOriginPrice/100., Mean/100.);
		//FLOGV("Travel end from %s. %s price ajusted from %f to %f (Mean: %f)", *DestinationSector->GetSectorName().ToString(), *Resource->Name.ToString(), DestinationPrice/100., NewDestinationPrice/100., Mean/100.);

		OriginSector->SetPreciseResourcePrice(Resource, NewOriginPrice);
		DestinationSector->SetPreciseResourcePrice(Resource, NewDestinationPrice);

	}

	Game->GetGameWorld()->DeleteTravel(this);

	// Notify travel ended
	if (Fleet->GetFleetCompany() == Game->GetPC()->GetCompany() && Fleet->GetCurrentTradeRoute() == NULL)
	{
		FFlareMenuParameterData Data;
		Data.Sector = DestinationSector;
		Game->GetPC()->Notify(LOCTEXT("TravelEnded", "Travel ended"),
			FText::Format(LOCTEXT("TravelEndedFormat", "{0} arrived at {1}"),
				Fleet->GetFleetName(),
				DestinationSector->GetSectorName()),
			FName("travel-end"),
			EFlareNotification::NT_Economy,
			false,
			EFlareMenu::MENU_Sector,
			Data);
	}
}

int64 UFlareTravel::GetElapsedTime()
{
	return Game->GetGameWorld()->GetDate() - TravelData.DepartureDate;
}

int64 UFlareTravel::GetRemainingTravelDuration()
{
	return TravelDuration - GetElapsedTime();
}

void UFlareTravel::ChangeDestination(UFlareSimulatedSector* NewDestinationSector)
{
	if (!CanChangeDestination())
	{
		return;
	}

	DestinationSector = NewDestinationSector;

	TravelData.DestinationSectorIdentifier = DestinationSector->GetIdentifier();

	// Reset travel duration
	// TODO intelligent travel remaining duration change
	TravelData.DepartureDate = Game->GetGameWorld()->GetDate();
	GenerateTravelDuration();
}

bool UFlareTravel::CanChangeDestination()
{
	// Travel not possible once started
	return GetElapsedTime() <= 0;
}

void UFlareTravel::GenerateTravelDuration()
{
	TravelDuration = ComputeTravelDuration(Game->GetGameWorld(), OriginSector, DestinationSector);
}

int64 UFlareTravel::ComputeTravelDuration(UFlareWorld* World, UFlareSimulatedSector* OriginSector, UFlareSimulatedSector* DestinationSector)
{
	int64 TravelDuration = 0;

	if (OriginSector == DestinationSector)
	{
		return 0;
	}

	double OriginAltitude;
	double DestinationAltitude;
	double OriginPhase;
	double DestinationPhase;
	FName OriginCelestialBodyIdentifier;
	FName DestinationCelestialBodyIdentifier;

	OriginAltitude = OriginSector->GetOrbitParameters()->Altitude;
	OriginCelestialBodyIdentifier = OriginSector->GetOrbitParameters()->CelestialBodyIdentifier;
	OriginPhase = OriginSector->GetOrbitParameters()->Phase;

	DestinationAltitude = DestinationSector->GetOrbitParameters()->Altitude;
	DestinationCelestialBodyIdentifier = DestinationSector->GetOrbitParameters()->CelestialBodyIdentifier;
	DestinationPhase = DestinationSector->GetOrbitParameters()->Phase;

	if (OriginCelestialBodyIdentifier == DestinationCelestialBodyIdentifier && OriginAltitude == DestinationAltitude)
	{
		// Phase change travel
		FFlareCelestialBody* CelestialBody = World->GetPlanerarium()->FindCelestialBody(OriginCelestialBodyIdentifier);
		TravelDuration = ComputePhaseTravelDuration(World, CelestialBody, OriginAltitude, OriginPhase, DestinationPhase) / UFlareGameTools::SECONDS_IN_DAY;
	}
	else
	{
		// Altitude change travel
		FFlareCelestialBody* OriginCelestialBody = World->GetPlanerarium()->FindCelestialBody(OriginCelestialBodyIdentifier);
		FFlareCelestialBody* DestinationCelestialBody = World->GetPlanerarium()->FindCelestialBody(DestinationCelestialBodyIdentifier);

		TravelDuration = ComputeAltitudeTravelDuration(World, OriginCelestialBody, OriginAltitude, DestinationCelestialBody, DestinationAltitude) / UFlareGameTools::SECONDS_IN_DAY;
	}

	return FMath::Max((int64) 1, TravelDuration);
}

double UFlareTravel::ComputeSphereOfInfluenceAltitude(UFlareWorld* World, FFlareCelestialBody* CelestialBody)
{
	FFlareCelestialBody* ParentCelestialBody = World->GetPlanerarium()->FindParent(CelestialBody);
	return CelestialBody->OrbitDistance * pow(CelestialBody->Mass / ParentCelestialBody->Mass, 0.4) - CelestialBody->Radius;
}

int64 UFlareTravel::ComputePhaseTravelDuration(UFlareWorld* World, FFlareCelestialBody* CelestialBody, double Altitude, double OriginPhase, double DestinationPhase)
{
	double TravelPhase =  FMath::Abs(FMath::UnwindDegrees(DestinationPhase - OriginPhase));

	double OrbitRadius = CelestialBody->Radius + Altitude;
	double OrbitPerimeter = 2 * PI * OrbitRadius;
	double TravelDistance = OrbitPerimeter * TravelPhase / 360;

	return TRAVEL_DURATION_PER_PHASE_KM * TravelDistance;
}

int64 UFlareTravel::ComputeAltitudeTravelDuration(UFlareWorld* World, FFlareCelestialBody* OriginCelestialBody, double OriginAltitude, FFlareCelestialBody* DestinationCelestialBody, double DestinationAltitude)
{
	double TravelAltitude;

	if (OriginCelestialBody == DestinationCelestialBody)
	{
		TravelAltitude = ComputeAltitudeTravelDistance(World, OriginAltitude, DestinationAltitude);
	}
	else if (World->GetPlanerarium()->IsSatellite(DestinationCelestialBody, OriginCelestialBody))
	{
		// Planet to moon
		TravelAltitude = ComputeAltitudeTravelToMoonDistance(World, OriginCelestialBody, OriginAltitude, DestinationCelestialBody) +
			ComputeAltitudeTravelToSoiDistance(World, DestinationCelestialBody, DestinationAltitude);
	}
	else if (World->GetPlanerarium()->IsSatellite(OriginCelestialBody, DestinationCelestialBody))
	{
		// Moon to planet
		TravelAltitude = ComputeAltitudeTravelToSoiDistance(World, OriginCelestialBody, OriginAltitude) +
				ComputeAltitudeTravelToMoonDistance(World, DestinationCelestialBody, DestinationAltitude, OriginCelestialBody);
	}
	else
	{
		TravelAltitude = ComputeAltitudeTravelToSoiDistance(World, OriginCelestialBody, OriginAltitude) +
				ComputeAltitudeTravelMoonToMoonDistance(World, OriginCelestialBody, DestinationCelestialBody) +
				ComputeAltitudeTravelToSoiDistance(World, DestinationCelestialBody, DestinationAltitude);
	}

	return TRAVEL_DURATION_PER_ALTITUDE_KM * TravelAltitude;
}

double UFlareTravel::ComputeAltitudeTravelDistance(UFlareWorld* World, double OriginAltitude, double DestinationAltitude)
{
	// Altitude change in same celestial body
	return FMath::Abs(DestinationAltitude - OriginAltitude);
}

double UFlareTravel::ComputeAltitudeTravelToSoiDistance(UFlareWorld* World, FFlareCelestialBody* CelestialBody, double Altitude)
{
	double MoonSoiAltitude = ComputeSphereOfInfluenceAltitude(World, CelestialBody);
	// Travel from moon SOI to moon target altitude
	return FMath::Abs(Altitude - MoonSoiAltitude);
}

double UFlareTravel::ComputeAltitudeTravelToMoonDistance(UFlareWorld* World, FFlareCelestialBody* ParentCelestialBody, double Altitude, FFlareCelestialBody* MoonCelestialBody)
{
	double MoonAltitude = MoonCelestialBody->OrbitDistance - ParentCelestialBody->Radius;
	// Travel to moon altitude
	return FMath::Abs(MoonAltitude - Altitude);
}

double UFlareTravel::ComputeAltitudeTravelMoonToMoonDistance(UFlareWorld* World, FFlareCelestialBody* OriginCelestialBody, FFlareCelestialBody* DestinationCelestialBody)
{
	// Moon1 orbit  to moon2 orbit
	return FMath::Abs(DestinationCelestialBody->OrbitDistance - OriginCelestialBody->OrbitDistance);
}

void UFlareTravel::InitTravelSector(FFlareSectorSave& NewSectorData)
{
	// Init new travel sector
	NewSectorData.GivenName = FText();
	NewSectorData.Identifier = TEXT("Travel");
	NewSectorData.LocalTime = 0;
	NewSectorData.IsTravelSector = true;

	// Init population
	NewSectorData.PeopleData.Population = 0;
	NewSectorData.PeopleData.BirthPoint = 0;
	NewSectorData.PeopleData.DeathPoint = 0;
	NewSectorData.PeopleData.FoodStock = 0;
	NewSectorData.PeopleData.FuelStock = 0;
	NewSectorData.PeopleData.ToolStock = 0;
	NewSectorData.PeopleData.TechStock = 0;
	NewSectorData.PeopleData.FoodConsumption = 0;
	NewSectorData.PeopleData.FuelConsumption = 0;
	NewSectorData.PeopleData.ToolConsumption = 0;
	NewSectorData.PeopleData.TechConsumption = 0;
	NewSectorData.PeopleData.HappinessPoint = 0;
	NewSectorData.PeopleData.HungerPoint = 0;
	NewSectorData.PeopleData.Money = 0;
	NewSectorData.PeopleData.Dept = 0;
}

#undef LOCTEXT_NAMESPACE
<commit_msg>#415 Fix not consistent travel duration<commit_after>

#include "../Flare.h"
#include "FlareTravel.h"
#include "FlareWorld.h"
#include "FlareGame.h"
#include "FlareGameTools.h"
#include "../Player/FlarePlayerController.h"
#include "../UI/Components/FlareNotification.h"
#include "../Economy/FlareCargoBay.h"

static const double TRAVEL_DURATION_PER_PHASE_KM = 0.4;
static const double TRAVEL_DURATION_PER_ALTITUDE_KM = 6.0;

#define LOCTEXT_NAMESPACE "FlareTravelInfos"

/*----------------------------------------------------
	Constructor
----------------------------------------------------*/

UFlareTravel::UFlareTravel(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
}

void UFlareTravel::Load(const FFlareTravelSave& Data)
{
	Game = Cast<UFlareWorld>(GetOuter())->GetGame();
	TravelData = Data;
	TravelShips.Empty();

	Fleet = Game->GetGameWorld()->FindFleet(TravelData.FleetIdentifier);
	DestinationSector = Game->GetGameWorld()->FindSector(TravelData.DestinationSectorIdentifier);
	OriginSector = Game->GetGameWorld()->FindSector(TravelData.OriginSectorIdentifier);

	for (int ShipIndex = 0; ShipIndex < Fleet->GetShips().Num(); ShipIndex++)
	{
		TravelShips.Add(Fleet->GetShips()[ShipIndex]);
	}

	Fleet->SetCurrentTravel(this);
	GenerateTravelDuration();

	FFlareSectorOrbitParameters OrbitParameters;
	OrbitParameters = *OriginSector->GetOrbitParameters();

	SectorDescription.Name = LOCTEXT("TravelSectorName", "Travelling ...");
	SectorDescription.Description = LOCTEXT("TravelSectorDescription", "Travel local sector");
	SectorDescription.Identifier=Fleet->GetIdentifier();
	SectorDescription.Phase = 0;
	SectorDescription.IsPeaceful = true;
	SectorDescription.IsIcy = false;
	SectorDescription.IsGeostationary = false;
	SectorDescription.IsSolarPoor = false;
	SectorDescription.LevelName = NAME_None;
	SectorDescription.DebrisFieldInfo.DebrisCatalog = NULL;
	SectorDescription.LevelTrack = EFlareMusicTrack::Pacific;
	SectorDescription.DebrisFieldInfo.DebrisFieldDensity = 0;
	SectorDescription.DebrisFieldInfo.MaxDebrisSize = 0;
	SectorDescription.DebrisFieldInfo.MinDebrisSize = 0;

	TravelSector = NewObject<UFlareSimulatedSector>(this, UFlareSimulatedSector::StaticClass());
	TravelSector->Load(&SectorDescription, Data.SectorData, OrbitParameters);

	Fleet->SetCurrentSector(TravelSector);
	TravelSector->AddFleet(Fleet);

}

FFlareTravelSave* UFlareTravel::Save()
{
	return &TravelData;
}

/*----------------------------------------------------
	Gameplay
----------------------------------------------------*/

void UFlareTravel::Simulate()
{
	if (GetRemainingTravelDuration() <= 0)
	{
		EndTravel();
	}
}

void UFlareTravel::EndTravel()
{
	Fleet->SetCurrentSector(DestinationSector);
	DestinationSector->AddFleet(Fleet);

	// Place correctly new ships to avoid collision

	// TODO People migration

	// Price migration
	for(int32 ResourceIndex = 0; ResourceIndex < Game->GetResourceCatalog()->Resources.Num(); ResourceIndex++)
	{
		FFlareResourceDescription* Resource = &Game->GetResourceCatalog()->Resources[ResourceIndex]->Data;

		float ContaminationFactor = 0.00f;

		for (int ShipIndex = 0; ShipIndex < Fleet->GetShips().Num(); ShipIndex++)
		{
			UFlareSimulatedSpacecraft* Ship = Fleet->GetShips()[ShipIndex];
			if (Ship->GetCargoBay()->GetResourceQuantity(Resource, Ship->GetCompany()) > 0)
			{
				ContaminationFactor += Ship->GetCargoBay()->GetResourceQuantity(Resource, Ship->GetCompany()) / 1000.f;
				//TODO scale bay world stock/flow
			}
		}

		ContaminationFactor = FMath::Min(ContaminationFactor, 1.f);

		float OriginPrice = OriginSector->GetPreciseResourcePrice(Resource);
		float DestinationPrice = DestinationSector->GetPreciseResourcePrice(Resource);

		float Mean = (OriginPrice + DestinationPrice) / 2.f;

		float NewOriginPrice = (OriginPrice * (1 - ContaminationFactor)) + (ContaminationFactor * Mean);
		float NewDestinationPrice = (DestinationPrice * (1 - ContaminationFactor)) + (ContaminationFactor * Mean);

		//FLOGV("Travel start from %s. %s price ajusted from %f to %f (Mean: %f)", *OriginSector->GetSectorName().ToString(), *Resource->Name.ToString(), OriginPrice/100., NewOriginPrice/100., Mean/100.);
		//FLOGV("Travel end from %s. %s price ajusted from %f to %f (Mean: %f)", *DestinationSector->GetSectorName().ToString(), *Resource->Name.ToString(), DestinationPrice/100., NewDestinationPrice/100., Mean/100.);

		OriginSector->SetPreciseResourcePrice(Resource, NewOriginPrice);
		DestinationSector->SetPreciseResourcePrice(Resource, NewDestinationPrice);

	}

	Game->GetGameWorld()->DeleteTravel(this);

	// Notify travel ended
	if (Fleet->GetFleetCompany() == Game->GetPC()->GetCompany() && Fleet->GetCurrentTradeRoute() == NULL)
	{
		FFlareMenuParameterData Data;
		Data.Sector = DestinationSector;
		Game->GetPC()->Notify(LOCTEXT("TravelEnded", "Travel ended"),
			FText::Format(LOCTEXT("TravelEndedFormat", "{0} arrived at {1}"),
				Fleet->GetFleetName(),
				DestinationSector->GetSectorName()),
			FName("travel-end"),
			EFlareNotification::NT_Economy,
			false,
			EFlareMenu::MENU_Sector,
			Data);
	}
}

int64 UFlareTravel::GetElapsedTime()
{
	return Game->GetGameWorld()->GetDate() - TravelData.DepartureDate;
}

int64 UFlareTravel::GetRemainingTravelDuration()
{
	return TravelDuration - GetElapsedTime();
}

void UFlareTravel::ChangeDestination(UFlareSimulatedSector* NewDestinationSector)
{
	if (!CanChangeDestination())
	{
		return;
	}

	DestinationSector = NewDestinationSector;

	TravelData.DestinationSectorIdentifier = DestinationSector->GetIdentifier();

	// Reset travel duration
	// TODO intelligent travel remaining duration change
	TravelData.DepartureDate = Game->GetGameWorld()->GetDate();
	GenerateTravelDuration();
}

bool UFlareTravel::CanChangeDestination()
{
	// Travel not possible once started
	return GetElapsedTime() <= 0;
}

void UFlareTravel::GenerateTravelDuration()
{
	TravelDuration = ComputeTravelDuration(Game->GetGameWorld(), OriginSector, DestinationSector);
}

int64 UFlareTravel::ComputeTravelDuration(UFlareWorld* World, UFlareSimulatedSector* OriginSector, UFlareSimulatedSector* DestinationSector)
{
	int64 TravelDuration = 0;

	if (OriginSector == DestinationSector)
	{
		return 0;
	}

	double OriginAltitude;
	double DestinationAltitude;
	double OriginPhase;
	double DestinationPhase;
	FName OriginCelestialBodyIdentifier;
	FName DestinationCelestialBodyIdentifier;

	OriginAltitude = OriginSector->GetOrbitParameters()->Altitude;
	OriginCelestialBodyIdentifier = OriginSector->GetOrbitParameters()->CelestialBodyIdentifier;
	OriginPhase = OriginSector->GetOrbitParameters()->Phase;

	DestinationAltitude = DestinationSector->GetOrbitParameters()->Altitude;
	DestinationCelestialBodyIdentifier = DestinationSector->GetOrbitParameters()->CelestialBodyIdentifier;
	DestinationPhase = DestinationSector->GetOrbitParameters()->Phase;

	if (OriginCelestialBodyIdentifier == DestinationCelestialBodyIdentifier && OriginAltitude == DestinationAltitude)
	{
		// Phase change travel
		FFlareCelestialBody* CelestialBody = World->GetPlanerarium()->FindCelestialBody(OriginCelestialBodyIdentifier);
		TravelDuration = ComputePhaseTravelDuration(World, CelestialBody, OriginAltitude, OriginPhase, DestinationPhase) / UFlareGameTools::SECONDS_IN_DAY;
	}
	else
	{
		// Altitude change travel
		FFlareCelestialBody* OriginCelestialBody = World->GetPlanerarium()->FindCelestialBody(OriginCelestialBodyIdentifier);
		FFlareCelestialBody* DestinationCelestialBody = World->GetPlanerarium()->FindCelestialBody(DestinationCelestialBodyIdentifier);

		TravelDuration = (UFlareGameTools::SECONDS_IN_DAY/2 + ComputeAltitudeTravelDuration(World, OriginCelestialBody, OriginAltitude, DestinationCelestialBody, DestinationAltitude)) / UFlareGameTools::SECONDS_IN_DAY;
	}

	return FMath::Max((int64) 1, TravelDuration);
}

double UFlareTravel::ComputeSphereOfInfluenceAltitude(UFlareWorld* World, FFlareCelestialBody* CelestialBody)
{
	FFlareCelestialBody* ParentCelestialBody = World->GetPlanerarium()->FindParent(CelestialBody);
	return CelestialBody->OrbitDistance * pow(CelestialBody->Mass / ParentCelestialBody->Mass, 0.4) - CelestialBody->Radius;
}

int64 UFlareTravel::ComputePhaseTravelDuration(UFlareWorld* World, FFlareCelestialBody* CelestialBody, double Altitude, double OriginPhase, double DestinationPhase)
{
	double TravelPhase =  FMath::Abs(FMath::UnwindDegrees(DestinationPhase - OriginPhase));

	double OrbitRadius = CelestialBody->Radius + Altitude;
	double OrbitPerimeter = 2 * PI * OrbitRadius;
	double TravelDistance = OrbitPerimeter * TravelPhase / 360;

	return TRAVEL_DURATION_PER_PHASE_KM * TravelDistance;
}

int64 UFlareTravel::ComputeAltitudeTravelDuration(UFlareWorld* World, FFlareCelestialBody* OriginCelestialBody, double OriginAltitude, FFlareCelestialBody* DestinationCelestialBody, double DestinationAltitude)
{
	double TravelAltitude;

	if (OriginCelestialBody == DestinationCelestialBody)
	{
		TravelAltitude = ComputeAltitudeTravelDistance(World, OriginAltitude, DestinationAltitude);
	}
	else if (World->GetPlanerarium()->IsSatellite(DestinationCelestialBody, OriginCelestialBody))
	{
		// Planet to moon
		TravelAltitude = ComputeAltitudeTravelToMoonDistance(World, OriginCelestialBody, OriginAltitude, DestinationCelestialBody) +
			ComputeAltitudeTravelToSoiDistance(World, DestinationCelestialBody, DestinationAltitude);
	}
	else if (World->GetPlanerarium()->IsSatellite(OriginCelestialBody, DestinationCelestialBody))
	{
		// Moon to planet
		TravelAltitude = ComputeAltitudeTravelToSoiDistance(World, OriginCelestialBody, OriginAltitude) +
				ComputeAltitudeTravelToMoonDistance(World, DestinationCelestialBody, DestinationAltitude, OriginCelestialBody);
	}
	else
	{
		TravelAltitude = ComputeAltitudeTravelToSoiDistance(World, OriginCelestialBody, OriginAltitude) +
				ComputeAltitudeTravelMoonToMoonDistance(World, OriginCelestialBody, DestinationCelestialBody) +
				ComputeAltitudeTravelToSoiDistance(World, DestinationCelestialBody, DestinationAltitude);
	}
	return TRAVEL_DURATION_PER_ALTITUDE_KM * TravelAltitude;
}

double UFlareTravel::ComputeAltitudeTravelDistance(UFlareWorld* World, double OriginAltitude, double DestinationAltitude)
{
	// Altitude change in same celestial body
	return FMath::Abs(DestinationAltitude - OriginAltitude);
}

double UFlareTravel::ComputeAltitudeTravelToSoiDistance(UFlareWorld* World, FFlareCelestialBody* CelestialBody, double Altitude)
{
	double MoonSoiAltitude = ComputeSphereOfInfluenceAltitude(World, CelestialBody);
	// Travel from moon SOI to moon target altitude
	return FMath::Abs(Altitude - MoonSoiAltitude);
}

double UFlareTravel::ComputeAltitudeTravelToMoonDistance(UFlareWorld* World, FFlareCelestialBody* ParentCelestialBody, double Altitude, FFlareCelestialBody* MoonCelestialBody)
{
	double MoonAltitude = MoonCelestialBody->OrbitDistance - ParentCelestialBody->Radius;
	// Travel to moon altitude
	return FMath::Abs(MoonAltitude - Altitude);
}

double UFlareTravel::ComputeAltitudeTravelMoonToMoonDistance(UFlareWorld* World, FFlareCelestialBody* OriginCelestialBody, FFlareCelestialBody* DestinationCelestialBody)
{
	// Moon1 orbit  to moon2 orbit
	return FMath::Abs(DestinationCelestialBody->OrbitDistance - OriginCelestialBody->OrbitDistance);
}

void UFlareTravel::InitTravelSector(FFlareSectorSave& NewSectorData)
{
	// Init new travel sector
	NewSectorData.GivenName = FText();
	NewSectorData.Identifier = TEXT("Travel");
	NewSectorData.LocalTime = 0;
	NewSectorData.IsTravelSector = true;

	// Init population
	NewSectorData.PeopleData.Population = 0;
	NewSectorData.PeopleData.BirthPoint = 0;
	NewSectorData.PeopleData.DeathPoint = 0;
	NewSectorData.PeopleData.FoodStock = 0;
	NewSectorData.PeopleData.FuelStock = 0;
	NewSectorData.PeopleData.ToolStock = 0;
	NewSectorData.PeopleData.TechStock = 0;
	NewSectorData.PeopleData.FoodConsumption = 0;
	NewSectorData.PeopleData.FuelConsumption = 0;
	NewSectorData.PeopleData.ToolConsumption = 0;
	NewSectorData.PeopleData.TechConsumption = 0;
	NewSectorData.PeopleData.HappinessPoint = 0;
	NewSectorData.PeopleData.HungerPoint = 0;
	NewSectorData.PeopleData.Money = 0;
	NewSectorData.PeopleData.Dept = 0;
}

#undef LOCTEXT_NAMESPACE
<|endoftext|>