Files
CDAG/Research/core/Util/Path.h

119 lines
3.3 KiB
C++

#pragma once
#include <string>
#include <vector>
#include <fstream>
#include <algorithm>
#include "../Defines.h"
#include "../Serializer.h"
// Path stores a recording detailing any type.
// Interpolator should be a functor with a method specification:
// T operator()(const T& a, const T& b, double time)
// Where a and b should be interpolated between, and time is a value between 0 and 1, indicating the interpolation value
template<typename T, typename Interpolator>
class Path
{
private:
std::vector<T> mFrames;
std::vector<double> mFrameTimes;
public:
Path() : mFrames(std::vector<T>()), mFrameTimes(std::vector<double>()) {}
~Path() {}
void SetStateAtTime(const double& time, const T& state)
{
// Find the position of this new state:
auto pos = std::lower_bound(mFrameTimes.begin(), mFrameTimes.end(), time);
size_t idx = pos - mFrameTimes.begin();
mFrames.insert(mFrames.begin() + idx, state);
mFrameTimes.insert(pos, time);
}
T GetStateAtTime(const double& time) const
{
// Find the frames between which the requested time takes place
size_t afterIdx = std::upper_bound(mFrameTimes.begin(), mFrameTimes.end(), time) - mFrameTimes.begin();
size_t beforeIdx = afterIdx - 1;
if (afterIdx == mFrameTimes.size()) // End of the recording, return the last camera state
return mFrames.back();
if (afterIdx == 0)
return mFrames.front();
// Now linearly interpolate the frames:
double t = ((time - mFrameTimes[beforeIdx]) / (mFrameTimes[afterIdx] - mFrameTimes[beforeIdx]));
const T& before = mFrames[beforeIdx];
const T& after = mFrames[afterIdx];
return Interpolator()(before, after, t);
}
double GetLength() const {
if (Empty()) return 0;
return mFrameTimes.back() - mFrameTimes.front();
}
size_t Size() const { return mFrames.size(); }
/// Removes the camera path node at index i
void RemoveAt(size_t i)
{
mFrames.erase(mFrames.begin() + i);
mFrameTimes.erase(mFrames.begin() + i);
}
/// Removes the last stored camera path node
void RemoveLast()
{
mFrames.pop_back();
mFrameTimes.pop_back();
}
// Makes sure the frametimes go from 0 to the length of the recording
void Normalize()
{
if (Empty()) return;
double offset = mFrameTimes[0];
for (size_t i = 0; i < mFrameTimes.size(); i++)
mFrameTimes[i] = mFrameTimes[i] - offset;
}
bool Empty() const { return mFrames.empty(); }
void Clear()
{
mFrames.clear();
mFrameTimes.clear();
}
void WriteToFile(const std::string& filename)
{
std::ofstream file(filename, std::ios::binary);
unsigned32 recordingLength = (unsigned32)mFrames.size();
Serializer<unsigned32>::Serialize(recordingLength, file);
if (recordingLength > 0)
{
Serializer<T*>::Serialize(&mFrames[0], mFrames.size(), file);
Serializer<double*>::Serialize(&mFrameTimes[0], mFrames.size(), file);
}
file.close();
}
bool ReadFromFile(const std::string& filename)
{
std::ifstream file(filename, std::ios::binary);
if (file.good()) {
unsigned32 recordingLength = 0;
Serializer<unsigned32>::Deserialize(recordingLength, file);
mFrames.resize(recordingLength);
mFrameTimes.resize(recordingLength);
if (recordingLength > 0)
{
Serializer<T*>::Deserialize(&mFrames[0], mFrames.size(), file);
Serializer<double*>::Deserialize(&mFrameTimes[0], mFrames.size(), file);
}
file.close();
return true;
}
return false;
}
};