This commit is contained in:
Aria 2025-03-21 22:23:30 +11:00
commit 9c94d113d3
Signed by untrusted user who does not match committer: aria
GPG key ID: 19AB7AA462B8AB3B
10260 changed files with 1237388 additions and 0 deletions

View file

@ -0,0 +1,68 @@
#ifndef STAR_COORD_SYS_H
#define STAR_COORD_SYS_H
#include "StarMathCommon.hpp"
#include "StarVector3.hpp"
namespace Star {
class CoordinateSystem3 {
public:
static inline CoordinateSystem3 opengl() {
return CoordinateSystem3(-3, 1, -2);
}
static inline CoordinateSystem3 northEastDown() {
return CoordinateSystem3(1, 2, 3);
}
static inline CoordinateSystem3 eastNorthUp() {
return CoordinateSystem3(2, 1, -3);
}
static inline Vec3D getDirection(int v) {
Vec3D dir(0, 0, 0);
if(v < 0)
dir[-v - 1] = -1.0f;
else
dir[v - 1] = 1.0f;
return dir;
}
inline CoordinateSystem3(int forward, int right, int down) {
if((forward < -3 || forward > 3 || forward == 0) ||
(right < -3 || right > 3 || right == 0) ||
(down < -3 || down > 3 || down == 0)) {
throw MathException("Value out of range [-3, -1], [1, -3] on "
"CoordinateSystem3 constructor");
}
m_forward = forward;
m_right = right;
m_down = down;
}
inline Vec3D getForward() const { return getDirection(m_forward); }
inline Vec3D getRight() const { return getDirection(m_right); }
inline Vec3D getDown() const { return getDirection(m_down); }
size_t getForwardIndex() const { return abs(m_forward) - 1; }
size_t getRightIndex() const { return abs(m_right) - 1; }
size_t getDownIndex() const { return abs(m_down) - 1; }
int getForwardSign() const { return m_forward / abs(m_forward); }
int getRightSign() const { return m_right / abs(m_right); }
int getDownSign() const { return m_down / abs(m_down); }
bool operator==(CoordinateSystem3 const& ref) const {
return m_forward == ref.m_forward &&
m_right == ref.m_right &&
m_down == ref.m_down;
}
private:
int m_forward;
int m_right;
int m_down;
};
}
#endif

173
attic/unneeded/StarFFT.hpp Normal file
View file

@ -0,0 +1,173 @@
#ifndef STAR_FFT_HPP
#define STAR_FFT_HPP
#include "StarMathCommon.hpp"
#include <complex>
#include <vector>
namespace Star {
template<typename Float>
class FFT {
public:
typedef std::complex<Float> Complex;
typedef std::vector<Complex> Vector;
static Float intensity(Complex c);
static Float phase(Complex c);
FFT(int n);
FFT();
// Initializes FFT, n will be rounded up to the nearest power of 2.
void init(int n);
int size() const;
Float freq(int i, Float sampleRate = 1.0f) const;
// Computes DFT, buffer will be resized to current size.
void transform(Vector& data) const;
// Computes inverse DFT, buffer will be resized to current size.
void itransform(Vector& data) const;
// Make vector complex conjugate symmetric so that the output of a (forward)
// transform will be real.
void makeSymmetric(Vector& data) const;
private:
int m_n, m_lgN;
Vector m_omega;
Vector m_iomega;
void bitReverse(Vector& dest) const;
};
template<typename Float>
Float FFT<Float>::intensity(Complex c) {
return std::abs(c);
}
template<typename Float>
Float FFT<Float>::phase(Complex c) {
return std::arg(c);
}
template<typename Float>
FFT<Float>::FFT() : m_n(0), m_lgN(0) {}
template<typename Float>
FFT<Float>::FFT(int n) {
init(n);
}
template<typename Float>
void FFT<Float>::init(int n) {
if (m_n == n)
return;
m_n = ceilPowerOf2(n);
m_lgN = 0;
for (int i = m_n; i > 1; i /= 2)
++m_lgN;
m_omega.resize(m_lgN);
int m = 1;
for (int s = 0; s < m_lgN; ++s) {
m *= 2;
m_omega[s] = std::exp(Complex(0, -2.0 * Constants::pi / m));
}
m_iomega.resize(m_lgN);
m = 1;
for (int s = 0; s < m_lgN; ++s) {
m *= 2;
m_iomega[s] = std::exp(Complex(0, 2.0 * Constants::pi / m));
}
}
template<typename Float>
int FFT<Float>::size() const {
return m_n;
}
template<typename Float>
Float FFT<Float>::freq(int i, Float sampleRate) const {
return sampleRate * i / m_n;
}
template<typename Float>
void FFT<Float>::transform(Vector& data) const {
data.resize(m_n);
bitReverse(data);
int m = 1;
for (int s = 0; s < m_lgN; ++s) {
m *= 2;
for (int k = 0; k < m_n; k += m) {
Complex current_omega = 1;
for (int j = 0; j < (m / 2); ++j) {
Complex t = current_omega * data[k + j + (m / 2)];
Complex u = data[k + j];
data[k + j] = u + t;
data[k + j + (m / 2)] = u - t;
current_omega *= m_omega[s];
}
}
}
for (int i = 0; i < m_n; ++i)
data[i] /= m_n;
}
template<typename Float>
void FFT<Float>::itransform(Vector& data) const {
data.resize(m_n);
bitReverse(data);
int m = 1;
for (int s = 0; s < m_lgN; ++s) {
m *= 2;
for (int k = 0; k < m_n; k += m) {
Complex current_omega = 1;
for (int j = 0; j < (m / 2); ++j) {
Complex t = current_omega * data[k + j + (m / 2)];
Complex u = data[k + j];
data[k + j] = u + t;
data[k + j + (m / 2)] = u - t;
current_omega *= m_iomega[s];
}
}
}
}
template<typename Float>
void FFT<Float>::makeSymmetric(Vector& data) const {
data.resize(m_n);
for (int i = 0; i <= m_n / 2; ++i) {
if (i == 0 || i == m_n / 2)
data[i] = data[i].real();
else
data[m_n - i] = std::conj(data[i]);
}
}
template<typename Float>
void FFT<Float>::bitReverse(Vector& data) const {
for (int i = 0; i < m_n; ++i) {
int index = i, rev = 0;
for (int j = 0; j < m_lgN; ++j) {
rev = (rev << 1) | (index & 1);
index >>= 1;
}
if (i < rev)
std::swap(data[i], data[rev]);
}
}
typedef FFT<float> FFTF;
typedef FFT<double> FFTD;
}
#endif

139
attic/unneeded/StarFifo.cpp Normal file
View file

@ -0,0 +1,139 @@
#include "StarFifo.hpp"
#include "StarFormat.hpp"
namespace Star {
pair<FifoPtr, FifoPtr> Fifo::makePair(bool blocking, size_t bufferSize) {
auto left = make_shared<Fifo>(blocking, bufferSize);
auto right = make_shared<Fifo>(blocking, bufferSize);
left->m_isPaired = true;
left->m_pairedFifo = right;
right->m_isPaired = true;
right->m_pairedFifo = left;
return {left, right};
}
Fifo::Fifo(bool blocking, size_t bufferSize) {
m_buffer.resize(bufferSize, 0);
m_blocking = blocking;
m_readPos = 0;
m_writePos = 0;
m_writeFull = false;
m_readEmpty = true;
m_isPaired = false;
setMode(ReadWrite);
}
void Fifo::close() {
setMode(Closed);
disconnect();
}
size_t Fifo::read(char* data, size_t len) {
if (len == 0)
return 0;
MutexLocker locker(m_mutex);
size_t ramt = 0;
while (isOpen()) {
while (!m_readEmpty && ramt < len) {
*data = m_buffer[m_readPos];
++data;
++ramt;
m_readPos = (m_readPos + 1) % m_buffer.size();
m_writeFull = false;
if (m_readPos == m_writePos)
m_readEmpty = true;
}
// Signal any waiting writes.
m_condition.signal();
// If we are blocking, and haven't read all we need, wait for writers.
if (m_blocking && ramt < len) {
// Make sure to check that we're not disconnected if we're out of data to
// read, and need to subsequently close.
if (m_isPaired && m_pairedFifo.expired()) {
close();
throw FifoClosedException("Fifo::read called on closed Fifo");
} else {
m_condition.wait(m_mutex);
}
} else {
break;
}
}
if (!isOpen() && ramt == 0)
throw FifoClosedException("Fifo::read called on closed Fifo");
return ramt;
}
size_t Fifo::write(char const* data, size_t len) {
if (len == 0)
return 0;
if (m_isPaired) {
if (auto paired = m_pairedFifo.lock()) {
return paired->doWrite(data, len);
} else {
close();
throw FifoClosedException("Fifo::write called on closed Fifo");
}
} else {
return doWrite(data, len);
}
}
String Fifo::deviceName() const {
return strf("Fifo <%s>", this);
}
size_t Fifo::doWrite(char const* data, size_t len) {
MutexLocker locker(m_mutex);
size_t wamt = 0;
while (isOpen()) {
while (!m_writeFull && wamt < len) {
m_buffer[m_writePos] = *data;
++data;
++wamt;
m_writePos = (m_writePos + 1) % m_buffer.size();
m_readEmpty = false;
if (m_writePos == m_readPos)
m_writeFull = true;
}
// Signal any waiting reads.
m_condition.signal();
// If we are blocking, and haven't written all we need, wait for readers.
if (m_blocking && wamt < len)
m_condition.wait(m_mutex);
else
break;
}
if (!isOpen() && wamt == 0)
throw FifoClosedException("Fifo::write called on closed Fifo");
return wamt;
}
void Fifo::disconnect() {
if (auto paired = m_pairedFifo.lock()) {
MutexLocker locker(m_mutex);
m_pairedFifo.reset();
paired->disconnect();
// Wake up all readers / writers so they can fail.
m_condition.signal();
}
}
}

View file

@ -0,0 +1,60 @@
#ifndef STAR_FIFO_HPP
#define STAR_FIFO_HPP
#include "StarIODevice.hpp"
#include "StarThread.hpp"
namespace Star {
STAR_EXCEPTION(FifoClosedException, DeviceClosedException);
STAR_CLASS(Fifo);
// First in first out IODevice. Bytes written to the device can be read from
// it first byte in, first byte out. If it set to blocking, then reads that
// read past the end of the available bytes -- or writes that go over the
// available buffer size -- will block until write is called, or the device is
// closed.
//
// A paired set of Fifos work just like a single Fifo, except each Fifo writes
// to the other and reads from the other.
class Fifo : public IODevice {
public:
// If either one of the paired Fifo objects is destroyed, the other one will
// immediately be marked as closed.
static pair<FifoPtr, FifoPtr> makePair(bool blocking = false, size_t bufferSize = 1024);
Fifo(bool blocking = false, size_t bufferSize = 1024);
// If this is a paired fifo, then this will close both sides.
void close() override;
size_t read(char* data, size_t len) override;
size_t write(char const* data, size_t len) override;
String deviceName() const override;
private:
size_t doWrite(char const* data, size_t len);
// Disconnects the other side of a paired fifo.
void disconnect();
Mutex m_mutex;
ConditionVariable m_condition;
ByteArray m_buffer;
bool m_blocking;
size_t m_readPos;
size_t m_writePos;
bool m_writeFull;
bool m_readEmpty;
bool m_isPaired;
weak_ptr<Fifo> m_pairedFifo;
};
}
#endif

View file

@ -0,0 +1,356 @@
#ifndef STAR_GUARDED_PTR_HPP
#define STAR_GUARDED_PTR_HPP
#include "StarException.hpp"
#include "StarCasting.hpp"
namespace Star {
STAR_EXCEPTION(GuardedPtrInvalidAccess, StarException);
STAR_EXCEPTION(BadGuardedPtrCast, StarException);
// Simple guarded pointer class that, when the object that it points to is
// destructed, automatically sets itself to invalid. Similar to
// enable_shared_from_this, with the benefit of not requiring that the class be
// managed by a shared_ptr. Accessing the guarded pointer is thread safe, with
// one huge caveat: Because this model of guarded pointers relies on putting
// no restrictions on object destruction, there is no thread safe way of
// *locking* the pointer from destruction. The pattern of if (guardedPtr) {
// guardedPtr->doThings(); } is inherently unsafe in a context where the
// original object that guardedPtr points to could be deconstructed at any
// time. If you need thread safety from destruction, then the only viable way
// to do this is enable_shared_from_this and weak_ptr.
template<typename T>
class GuardedPtr {
public:
typedef typename std::decay<T>::type Type;
typedef Type* PtrType;
typedef Type& RefType;
GuardedPtr();
GuardedPtr(PtrType ptr, shared_ptr<atomic<bool>> sharedValid);
template<typename Other>
GuardedPtr(GuardedPtr<Other> const& other);
template<typename Other>
GuardedPtr(GuardedPtr<Other>&& other);
template<typename Other>
GuardedPtr& operator=(GuardedPtr<Other> const& other);
template<typename Other>
GuardedPtr& operator=(GuardedPtr<Other>&& other);
bool valid() const;
explicit operator bool() const;
PtrType get() const;
template<typename T2>
GuardedPtr<T2> as() const;
template<typename T2>
GuardedPtr<T2> convert() const;
void reset();
PtrType operator->() const;
RefType operator*() const;
bool operator==(GuardedPtr const& ptr) const;
bool operator!=(GuardedPtr const& ptr) const;
bool operator==(PtrType ptr) const;
bool operator!=(PtrType ptr) const;
private:
template<typename Other>
friend class GuardedPtr;
PtrType m_ptr;
shared_ptr<atomic<bool>> m_sharedValid;
};
// Holds a pointer and enables construction of GuardedPtr versions of that
// pointer. If constructed with autoDelete, then the pointer will be
// automatically deleted on destruction.
template<typename T>
class GuardedHolder {
public:
typedef typename std::decay<T>::type Type;
typedef Type* PtrType;
GuardedHolder();
GuardedHolder(PtrType ptr, bool autoDelete);
~GuardedHolder();
GuardedHolder(GuardedHolder const& rhs) = delete;
GuardedHolder& operator=(GuardedHolder const& rhs) = delete;
GuardedHolder(GuardedHolder&& guardedHolder);
GuardedHolder& operator=(GuardedHolder&& guardedHolder);
PtrType pointer() const;
GuardedPtr<Type> guardedPtr() const;
// Clears the held pointer and invalidates any GuardedPtrs that reference it.
void clear();
// Clears the held pointer and invalidates any GuardedPtrs that reference it,
// then updates the pointer value to a new value.
void reset(PtrType ptr, bool autoDelete);
private:
bool m_autoDelete;
PtrType m_ptr;
shared_ptr<atomic<bool>> m_sharedValid;
};
// Easy way to automatically support guardedPtrs to this.
class GuardedHolderBase {
public:
GuardedHolderBase();
virtual ~GuardedHolderBase();
// Invalidates all existing GuardedPtrs to this, and resets the this holder
// to 'this' again.
void invalidateExistingGuardedPointers();
template<typename T>
friend GuardedPtr<T> guardedPtr(T& t);
template<typename T>
friend GuardedPtr<T const> guardedPtr(T const& t);
template<typename T>
friend GuardedPtr<T> guardedPtr(T* t);
template<typename T>
friend GuardedPtr<T const> guardedPtr(T const* t);
template<typename T>
friend GuardedPtr<T> guardedPtr(shared_ptr<T> t);
template<typename T>
friend GuardedPtr<T const> guardedPtr(shared_ptr<T const> t);
private:
GuardedHolder<GuardedHolderBase> m_thisHolder;
};
template<typename T1, typename T2>
GuardedPtr<T1> as(GuardedPtr<T2> const& t);
template<typename T1, typename T2>
GuardedPtr<T1 const> as(GuardedPtr<T2 const> const& t);
template<typename T>
GuardedPtr<T>::GuardedPtr()
: m_ptr(nullptr) {}
template<typename T>
GuardedPtr<T>::GuardedPtr(PtrType ptr, shared_ptr<atomic<bool>> sharedValid)
: m_ptr(move(ptr)), m_sharedValid(move(sharedValid)) {}
template<typename T>
template<typename Other>
GuardedPtr<T>::GuardedPtr(GuardedPtr<Other> const& other) {
m_ptr = other.m_ptr;
m_sharedValid = other.m_sharedValid;
}
template<typename T>
template<typename Other>
GuardedPtr<T>::GuardedPtr(GuardedPtr<Other>&& other) {
m_ptr = other.m_ptr;
m_sharedValid = move(other.m_sharedValid);
}
template<typename T>
template<typename Other>
GuardedPtr<T>& GuardedPtr<T>::operator=(GuardedPtr<Other> const& other) {
m_ptr = other.m_ptr;
m_sharedValid = other.m_sharedValid;
return *this;
}
template<typename T>
template<typename Other>
GuardedPtr<T>& GuardedPtr<T>::operator=(GuardedPtr<Other>&& other) {
m_ptr = other.m_ptr;
m_sharedValid = move(other.m_sharedValid);
return *this;
}
template<typename T>
bool GuardedPtr<T>::valid() const {
return m_sharedValid && *m_sharedValid;
}
template<typename T>
GuardedPtr<T>::operator bool() const {
return valid();
}
template<typename T>
auto GuardedPtr<T>::get() const -> PtrType {
if (valid())
return m_ptr;
else
return nullptr;
}
template<typename T>
template<typename T2>
GuardedPtr<T2> GuardedPtr<T>::convert() const {
if (auto p = as<T2>())
return p;
throw BadGuardedPtrCast();
}
template<typename T>
template<typename T2>
GuardedPtr<T2> GuardedPtr<T>::as() const {
if (valid()) {
if (auto p = Star::as<T2>(m_ptr))
return GuardedPtr<T2>(p, m_sharedValid);
}
return {};
}
template<typename T>
void GuardedPtr<T>::reset() {
m_sharedValid.reset();
}
template<typename T>
auto GuardedPtr<T>::operator->() const -> PtrType {
if (auto p = get())
return p;
else
throw GuardedPtrInvalidAccess("Invalid access to invalid GuardedPtr");
}
template<typename T>
auto GuardedPtr<T>::operator*() const -> RefType {
return *operator->();
}
template<typename T>
bool GuardedPtr<T>::operator==(GuardedPtr const& ptr) const {
return get() == ptr.get();
}
template<typename T>
bool GuardedPtr<T>::operator!=(GuardedPtr const& ptr) const {
return get() != ptr.get();
}
template<typename T>
bool GuardedPtr<T>::operator==(PtrType ptr) const {
return get() == ptr;
}
template<typename T>
bool GuardedPtr<T>::operator!=(PtrType ptr) const {
return get() != ptr;
}
template<typename T>
GuardedHolder<T>::GuardedHolder(PtrType ptr, bool autoDelete)
: m_autoDelete(false), m_ptr(nullptr) {
reset(ptr, autoDelete);
}
template<typename T>
GuardedHolder<T>::~GuardedHolder() {
clear();
}
template<typename T>
GuardedHolder<T>::GuardedHolder(GuardedHolder&& guardedHolder) {
operator=(move(guardedHolder));
}
template<typename T>
GuardedHolder<T>& GuardedHolder<T>::operator=(GuardedHolder&& guardedHolder) {
clear();
m_autoDelete = move(guardedHolder.m_autoDelete);
m_ptr = move(guardedHolder.m_ptr);
m_sharedValid = move(guardedHolder.m_sharedValid);
return *this;
}
template<typename T>
auto GuardedHolder<T>::pointer() const -> PtrType {
return m_ptr;
}
template<typename T>
auto GuardedHolder<T>::guardedPtr() const -> GuardedPtr<Type> {
return GuardedPtr<T>(m_ptr, m_sharedValid);
}
template<typename T>
void GuardedHolder<T>::clear() {
reset(nullptr, false);
}
template<typename T>
void GuardedHolder<T>::reset(PtrType ptr, bool autoDelete) {
if (m_autoDelete && m_ptr)
delete m_ptr;
if (m_sharedValid) {
*m_sharedValid = false;
m_sharedValid.reset();
}
m_autoDelete = autoDelete;
m_ptr = ptr;
if (m_ptr)
m_sharedValid = make_shared<atomic<bool>>(true);
}
inline GuardedHolderBase::GuardedHolderBase()
: m_thisHolder(this, false) {}
inline GuardedHolderBase::~GuardedHolderBase() {}
inline void GuardedHolderBase::invalidateExistingGuardedPointers() {
m_thisHolder.reset(this, false);
}
template<typename T>
GuardedPtr<T> guardedPtr(T& t) {
return t.m_thisHolder.guardedPtr().template convert<T>();
}
template<typename T>
GuardedPtr<T const> guardedPtr(T const& t) {
return t.m_thisHolder.guardedPtr().template convert<T const>();
}
template<typename T>
GuardedPtr<T> guardedPtr(T* t) {
return t->m_thisHolder.guardedPtr().template convert<T>();
}
template<typename T>
GuardedPtr<T const> guardedPtr(T const* t) {
return t->m_thisHolder.guardedPtr().template convert<T const>();
}
template<typename T>
GuardedPtr<T> guardedPtr(shared_ptr<T> t) {
return t->m_thisHolder.guardedPtr().template convert<T>();
}
template<typename T>
GuardedPtr<T const> guardedPtr(shared_ptr<T const> t) {
return t->m_thisHolder.guardedPtr().template convert<T const>();
}
}
#endif

View file

@ -0,0 +1,338 @@
#ifndef STAR_MATRIX4_HPP
#define STAR_MATRIX4_HPP
#include "StarMatrix3.hpp"
#include "StarVector4.hpp"
namespace Star {
template<typename T1> class Matrix4 {
public:
static Matrix4<T1> identity();
static Matrix4<T1> rotAroundPoint(Matrix3<T1> rot, Vector3<T1> point);
static Matrix4<T1> frustrum(T1 l, T1 r, T1 b, T1 t, T1 n, T1 f);
static Matrix4<T1> frustrumInv(T1 l, T1 r, T1 b, T1 t, T1 n, T1 f);
public:
typedef Vector4<T1> RowT;
RowT r1, r2, r3, r4;
Matrix4();
Matrix4(T1 r1c1, T1 r1c2, T1 r1c3, T1 r1c4,
T1 r2c1, T1 r2c2, T1 r2c3, T1 r2c4,
T1 r3c1, T1 r3c2, T1 r3c3, T1 r3c4,
T1 r4c1, T1 r4c2, T1 r4c3, T1 r4c4 );
Matrix4(RowT const& r1, RowT const& r2,
RowT const& r3, RowT const& r4);
template<typename T2> Matrix4(Matrix4<T2> const& m);
template<typename T2> explicit Matrix4(Matrix3<T2> const& m);
Matrix3<T1> mat3() const;
T1* ptr();
T1 const* ptr() const;
// Copy to an existing array
void copy(T1 *loc) const;
Vector4<T1>& operator[](size_t const i);
Vector4<T1> const& operator[](size_t const i) const;
Vector4<T1> const& row(size_t r) const;
template<typename T2>
void setRow(unsigned int i, Vector4<T2> const& v);
Vector4<T1> col(size_t c) const;
template<typename T2>
void setCol(unsigned int i, Vector4<T2> const& v);
template<typename T2> Matrix4& operator+=(Matrix4<T2> const& m);
template<typename T2> Matrix4& operator-=(Matrix4<T2> const& m);
T1 det();
Matrix4& transpose();
Matrix4& opengl();
Matrix4& inverse();
Vector4<T1> trace() const;
Matrix4& operator*=(T1 const& s);
Matrix4& operator/=(T1 const& s);
Matrix4 operator*(T1 const& s);
Matrix4 operator/(T1 const& s);
template<typename T2>
Matrix4 operator+(Matrix4<T2> const& m2) const;
template<typename T2>
Matrix4 operator-(Matrix4<T2> const& m2) const;
template<typename T2>
Matrix4 operator*(Matrix4<T2> const& m2) const;
template<typename T2>
Vector4<T1> operator*(Vector4<T2> const& v) const;
};
typedef Matrix4<float> Mat4F;
typedef Matrix4<double> Mat4D;
template<typename T>
std::ostream& operator<<(std::ostream &os, Matrix4<T> mat) {
os << mat[0] << std::endl;
os << mat[1] << std::endl;
os << mat[2] << std::endl;
os << mat[3];
return os;
}
template<typename T1>
Matrix4<T1> Matrix4<T1>::identity() {
return Matrix4(1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
}
template<typename T1>
Vector4<T1> Matrix4<T1>::trace() const {
return Vector4<T1>(r1[0], r2[1], r3[2], r4[3]);
}
template<typename T>
inline Matrix4<T>::Matrix4() {}
template<typename T>
Matrix4<T>::Matrix4(
T r1c1, T r1c2, T r1c3, T r1c4,
T r2c1, T r2c2, T r2c3, T r2c4,
T r3c1, T r3c2, T r3c3, T r3c4,
T r4c1, T r4c2, T r4c3, T r4c4 ) {
r1 = Vector4<T>(r1c1, r1c2, r1c3, r1c4);
r2 = Vector4<T>(r2c1, r2c2, r2c3, r2c4);
r3 = Vector4<T>(r3c1, r3c2, r3c3, r3c4);
r4 = Vector4<T>(r4c1, r4c2, r4c3, r4c4);
}
template<typename T>
Matrix4<T>::Matrix4(const RowT& pr1, const RowT& pr2,
const RowT& pr3, const RowT& pr4) {
r1 = pr1;
r2 = pr2;
r3 = pr3;
r4 = pr4;
}
template<typename T1>
Vector4<T1>& Matrix4<T1>::operator[](const size_t i) {
return *(&r1 + i);
}
template<typename T1>
const Vector4<T1>& Matrix4<T1>::operator[](const size_t i) const {
return *(&r1 + i);
}
template<typename T1>
T1* Matrix4<T1>::ptr() {
return &r1[0];
}
template<typename T1>
const T1* Matrix4<T1>::ptr() const {
return &r1[0];
}
template<typename T1>
void Matrix4<T1>::copy(T1* loc) const {
r1.copy(loc);
r2.copy(loc + 4);
r3.copy(loc + 8);
r4.copy(loc + 12);
}
template<typename T1>
template<typename T2> Matrix4<T1>::Matrix4(const Matrix3<T2> &m) {
r1 = Vector4<T1>(m[0], 0);
r2 = Vector4<T1>(m[1], 0);
r3 = Vector4<T1>(m[2], 0);
r4 = Vector4<T1>(T1(0), T1(0), T1(0), T1(1));
}
template<typename T1>
Matrix3<T1> Matrix4<T1>::mat3() const {
return Matrix3<T1>(r1, r2, r3);
}
template<typename T1>
const Vector4<T1>& Matrix4<T1>::row(size_t i) const {
return operator[](i);
}
template<typename T1>
Vector4<T1> Matrix4<T1>::col(size_t c) const {
return Vector4<T1>(r1[c], r2[c], r3[c], r4[c]);
}
template<typename T1> template<typename T2>
void Matrix4<T1>::setRow(unsigned int c, const Vector4<T2> &r) {
operator[](c) = r;
}
template<typename T1> template<typename T2>
void Matrix4<T1>::setCol(unsigned int c, const Vector4<T2> &v) {
r1[c] = v[0];
r2[c] = v[1];
r3[c] = v[2];
r4[c] = v[3];
}
template<typename T1> template<typename T2>
Matrix4<T1>::Matrix4(const Matrix4<T2> &m) {
r1 = m[0];
r2 = m[1];
r3 = m[2];
r4 = m[3];
}
template<typename T1> template<typename T2>
Matrix4<T1>& Matrix4<T1>::operator+=(const Matrix4<T2> &m) {
r1 += m[0];
r2 += m[1];
r3 += m[2];
r4 += m[3];
}
template<typename T1> template<typename T2>
Matrix4<T1>& Matrix4<T1>::operator-=(const Matrix4<T2> &m) {
r1 -= m[0];
r2 -= m[1];
r3 -= m[2];
r4 -= m[3];
}
template<typename T>
Matrix4<T>& Matrix4<T>::transpose() {
return operator=(Matrix4( r1[0], r2[0], r3[0], r4[0],
r1[1], r2[1], r3[1], r4[1],
r1[2], r2[2], r3[2], r4[2],
r1[3], r2[3], r3[3], r4[3]));
}
template<typename T>
Matrix4<T>& Matrix4<T>::opengl() {
return transpose();
}
template<typename T>
Matrix4<T>& Matrix4<T>::operator*=(const T &s) {
r1 *= s;
r2 *= s;
r3 *= s;
r4 *= s;
return *this;
}
template<typename T>
Matrix4<T>& Matrix4<T>::operator/=(const T &s) {
r1 /= s;
r2 /= s;
r3 /= s;
r4 /= s;
return *this;
}
template <typename T1> template<typename T2>
Matrix4<T1> Matrix4<T1>::operator+(const Matrix4<T2> &m2) const {
return Matrix4<T1>( r1 + m2[0],
r2 + m2[1],
r3 + m2[2],
r4 + m2[3]);
}
template <typename T1> template<typename T2>
Matrix4<T1> Matrix4<T1>::operator-(const Matrix4<T2> &m2) const {
return Matrix4<T1>( r1 - m2[0],
r2 - m2[1],
r3 - m2[2],
r4 - m2[3]);
}
template <typename T1> template<typename T2>
Matrix4<T1> Matrix4<T1>::operator*(const Matrix4<T2> &m2) const {
return Matrix4<T1>(
row(0)*m2.col(0), row(0)*m2.col(1),
row(0)*m2.col(2), row(0)*m2.col(3),
row(1)*m2.col(0), row(1)*m2.col(1),
row(1)*m2.col(2), row(1)*m2.col(3),
row(2)*m2.col(0), row(2)*m2.col(1),
row(2)*m2.col(2), row(2)*m2.col(3),
row(3)*m2.col(0), row(3)*m2.col(1),
row(3)*m2.col(2), row(3)*m2.col(3));
}
template<typename T1>
Matrix4<T1> Matrix4<T1>::operator*(const T1 &s) {
return Matrix4(r1*s, r2*s, r3*s, r4*s);
}
template<typename T1>
Matrix4<T1> Matrix4<T1>::operator/(const T1 &s) {
return Matrix4<T1>(r1/s, r2/s, r3/s, r4/s);
}
template <typename T1> template<typename T2>
Vector4<T1> Matrix4<T1>::operator*(const Vector4<T2>& v) const {
return Vector4<T1>(r1*v, r2*v, r3*v, r4*v);
}
template <typename T1>
Matrix4<T1> Matrix4<T1>::rotAroundPoint(Matrix3<T1> rot,
Vector3<T1> point) {
Matrix4<T1> final(rot);
final[0][3] =
rot[0][0]*point[0] + rot[0][1]*point[1] + rot[0][0]*point[2]-point[0];
final[1][3] =
rot[1][0]*point[0] + rot[1][1]*point[1] + rot[1][2]*point[2]-point[1];
final[2][3] =
rot[2][0]*point[0] + rot[2][1]*point[1] + rot[2][2]*point[2]-point[2];
return final;
}
template<typename T>
Matrix4<T> Matrix4<T>::frustrumInv(T l, T r, T b, T t, T n, T f) {
return Matrix4<T>(
(r-l)/(2*n), 0, 0, (r+l)/(2*n),
0, (t-b)/(2*n), 0, (t+b)/(2*n),
0, 0, 0, -1,
0, 0, -(f-n)/(2*f*n), (f+n)/(2*f*n));
}
template<typename T>
Matrix4<T> Matrix4<T>::frustrum(T l, T r, T b, T t, T n, T f) {
return Matrix4<T>(
2*n/(r-l), 0, 0, (r+l)/(r-l),
0, 2/(t-b), 0, (t+b)/(t-b),
0, 0, -2/(f-n), (f+n)/(f-n),
0, 0, 0, 1);
}
template<typename T>
Matrix4<T> transpose(Matrix4<T> m) {
return m.transpose();
}
template<typename T>
Matrix4<T> opengl(Matrix4<T> m) {
return m.opengl();
}
template<typename T>
Matrix4<T> operator*(const T &s, const Matrix4<T> &m) {
return m * s;
}
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,216 @@
#ifndef STAR_NOISE_FIELD_HPP
#define STAR_NOISE_FIELD_HPP
#include "StarMultiArrayInterpolator.hpp"
namespace Star {
// Noise field with configurable number of layers of randomness. Each layer of
// randomness is <alpha> times as dense as the last layer, as well as 1.0 /
// <beta> the magnitude of the last layer. If <alpha> and <beta> are 2.0
// (typical), and there are 4 layers, the perlin field would be constructed as
// follows:
// - The bottom layer would be 1x1, with magnitude 1 / 8
// - Next layer is 2x2 with magnitude 1 / 4
// - Next layer 4x4 with magnitude 1 / 2
// - Top layer 8x8 with magnitude 1.
//
// Note: Cubic interpolation mode *can* produce values just outside the range [0.0, 1.0] when normalized.
template<typename DataT, typename IndexT, typename ScaleT, size_t RankT, typename NoiseSource>
class NoiseField {
public:
typedef DataT Data;
static size_t const Rank = RankT;
typedef IndexT IndexType;
typedef Array<IndexType, Rank> Index;
typedef ScaleT ScaleType;
typedef Array<ScaleType, Rank> Scale;
typedef Star::MultiArray<Data, Rank> MultiArray;
typedef typename MultiArray::SizeList Size;
enum InterpolationMode {
Linear,
Smooth,
Cubic
};
NoiseField(InterpolationMode interpolation, size_t minLayer, size_t layers,
ScaleType alpha = 2.0, ScaleType beta = 2.0, NoiseSource const& noise = NoiseSource())
: m_minLayer(minLayer), m_noiseSource(noise), m_interpolation(interpolation),
m_normalize(true), m_max(ScaleType()) {
m_layers.resize(layers);
for (size_t i = 0; i < layers; ++i) {
m_layers[i].indexScale = 1 / pow(alpha, i);
m_layers[i].valueScale = 1 / pow(beta, layers - i - 1);
m_max += m_layers[i].valueScale;
}
}
// The maximum scale for any point in the field is sum(1 / s^x, x, 0, n),
// where s is the layer scale and n is the number of layers.
// This scale is the maximum amount that a generated value will be relative
// to the values generated by the noise source.
//
// If normalized is set to true, then all values genrated will be divided by
// this scale, so the range of values generated by this field will always be
// the same as the range generated by the noise source.
ScaleType maxScale() const {
return m_max;
}
InterpolationMode interpolationMode() const {
return m_interpolation;
}
void setInterpolationMode(InterpolationMode interpolation) const {
m_interpolation = interpolation;
}
bool isNormalized() const {
return m_normalize;
}
void setNormalize(bool normalize) {
m_normalize = normalize;
}
MultiArray generate(Index const& min, Size const& size) {
MultiArray array(size);
generate(min, array);
return array;
}
void generate(Index const& min, MultiArray& array) {
Size size = array.size();
for (size_t i = m_minLayer; i < m_layers.size(); ++i) {
ScaleType indexScale = m_layers[i].indexScale;
ScaleType valueScale = m_layers[i].valueScale;
if (m_normalize)
valueScale = valueScale / m_max;
// Bottom layer is always 1x1, so we can skip the resampling step.
if (i == 0) {
array.eval(NoiseGenerator(m_noiseSource, i, valueScale, min));
} else {
// This layer's min and max index in field space, and the total size of
// this layer.
Index layerMinIndex;
Index layerMaxIndex;
Size layerSize;
// Sampling offset of layerMin / layerMax
Scale sampleMin;
Scale sampleMax;
for (size_t j = 0; j < Rank; ++j) {
ScaleType lmin = min[j] * indexScale;
ScaleType lmax = (min[j] + size[j] - 1) * indexScale;
if (m_interpolation == Cubic) {
layerMinIndex[j] = IndexType(floor(lmin) - 1);
layerMaxIndex[j] = IndexType(ceil(lmax) + 1);
} else {
layerMinIndex[j] = IndexType(floor(lmin));
layerMaxIndex[j] = IndexType(ceil(lmax));
}
layerSize[j] = size_t(layerMaxIndex[j] - layerMinIndex[j]) + 1;
sampleMin[j] = lmin - layerMinIndex[j];
sampleMax[j] = lmax - layerMinIndex[j];
}
MultiArray layer(layerSize);
layer.eval(NoiseGenerator(m_noiseSource, i, valueScale, layerMinIndex));
//TODO: this code isn't even compiled, is it.
starAssert(m_interpolation != Smooth);
if (m_interpolation == Cubic) {
auto interpolator = MultiArrayInterpolator4<MultiArray, ScaleType>(CubicWeightOperator<ScaleType>());
interpolator.sample(layer, array, sampleMin, sampleMax);
// } else if (m_interpolation == Smooth) {
// auto interpolator = MultiArrayInterpolator2<MultiArray, ScaleType>(SmoothWeightOperator<ScaleType>());
// interpolator.sample(layer, array, sampleMin, sampleMax);
} else {
auto interpolator = MultiArrayInterpolator2<MultiArray, ScaleType>(LinearWeightOperator<ScaleType>());
interpolator.sample(layer, array, sampleMin, sampleMax);
}
}
}
}
private:
struct NoiseGenerator {
NoiseGenerator(NoiseSource& ns, size_t l, ScaleType sc, Index const& mn)
: noiseSource(ns), layer(l), scale(sc), min(mn) {}
inline Data operator()(typename MultiArray::IndexList& index) {
Index loc = min;
std::transform(min.begin(), min.end(), index.begin(), loc.begin(), std::plus<IndexType>());
return scale * noiseSource(layer, loc);
}
NoiseSource noiseSource;
size_t layer;
ScaleType scale;
Index min;
};
struct Layer {
ScaleType indexScale;
ScaleType valueScale;
};
std::vector<Layer> m_layers;
size_t m_minLayer;
NoiseSource m_noiseSource;
InterpolationMode m_interpolation;
bool m_normalize;
Data m_max;
};
// Generates values in range [0.0, 1.0]
struct DoubleNoiseFieldSource {
DoubleNoiseFieldSource(int64_t s) : seed(s) {}
template<typename Index>
inline double operator()(size_t layer, Index const& index) const {
int64_t x = layer;
for (size_t i = 0; i < Index::Size; ++i)
x = x * 38577907 + index[i];
x = (x<<27) ^ x;
x *= seed;
return ((x * (x * x * 5736215731 + 23453458789221) + 1997293021376312589) & 0x7fffffffffffffff) / 9223372036854775808.0;
}
int64_t seed;
};
// Generates values in range [0.0, 1.0]
struct FloatNoiseFieldSource {
FloatNoiseFieldSource(int64_t s) : seed(s) {}
template<typename Index>
inline float operator()(size_t layer, Index const& index) const {
int64_t x = layer;
for (size_t i = 0; i < Index::Size; ++i)
x = x * 38577907 + index[i];
x = (x<<13) ^ x;
x *= seed;
return ((x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 2147483648.0;
}
int64_t seed;
};
}
#endif

View file

@ -0,0 +1,120 @@
#ifndef STAR_PROMISE_HPP
#define STAR_PROMISE_HPP
#include "StarAtomicSharedPtr.hpp"
namespace Star {
STAR_EXCEPTION(PromiseException, StarException);
// Wraps a ThreadInvoker, and can hold the value for consumption by more than
// one thread, and is sharable. The return value is not taken on get(), and
// access to the value via get() by multiple threads is safe.
template<typename Result>
class Promise {
public:
Promise();
Promise(ThreadInvoker<Result> invoker);
Promise(Result result);
bool valid() const;
operator bool() const;
bool fulfilled() const;
Maybe<Result const&> maybe() const;
Maybe<Result&> maybe();
Result const& get() const;
Result& get();
private:
void checkValid();
struct Data {
mutable Mutex mutex;
Maybe<ThreadInvoker<Result>> invoker;
Maybe<Result> result;
};
AtomicSharedPtr<Data> m_data;
};
template<typename Result>
Promise<Result>::Promise() {}
template<typename Result>
Promise<Result>::Promise(ThreadInvoker<Result> invoker) {
m_data = make_shared<Data>(Data{{}, move(invoker), {}});
}
template<typename Result>
Promise<Result>::Promise(Result result) {
m_data = make_shared<Data>(Data{{}, {}, move(result)});
}
template<typename Result>
bool Promise<Result>::valid() const {
return m_data.valid();
}
template<typename Result>
Promise<Result>::operator bool() const {
return (bool)m_data;
}
template<typename Result>
bool Promise<Result>::fulfilled() const {
if (!m_data)
return false;
MutexLocker locker(m_data->mutex);
return m_data->result.isValid();
}
template<typename Result>
Maybe<Result const&> Promise<Result>::maybe() const {
if (!m_data)
return {};
MutexLocker locker(m_data->mutex);
return *m_data->result;
}
template<typename Result>
Maybe<Result&> Promise<Result>::maybe() {
if (!m_data)
return {};
MutexLocker locker(m_data->mutex);
return *m_data->result;
}
template<typename Result>
Result const& Promise<Result>::get() const {
checkValid();
MutexLocker locker(m_data->mutex);
if (m_data->invoker) {
m_data->result = m_data->invoker->finish();
m_data->invoker.reset();
}
return *m_data->result;
}
template<typename Result>
Result& Promise<Result>::get() {
checkValid();
MutexLocker locker(m_data->mutex);
if (m_data->invoker) {
m_data->result = m_data->invoker->finish();
m_data->invoker.reset();
}
return *m_data->result;
}
template<typename Result>
void Promise<Result>::checkValid() {
if (!m_data)
throw PromiseException("Unset Promise was accessed");
}
}
#endif

View file

@ -0,0 +1,448 @@
#ifndef STAR_QUATERNION_HPP
#define STAR_QUATERNION_HPP
#include "StarVector3.hpp"
#include "StarMatrix3.hpp"
namespace Star {
template <typename T1>
class Quaternion {
public:
// Default tolerance is 100 times type epsilon
static T1 const Tolerance;
static Quaternion slerp(Quaternion q, Quaternion r, T1 a);
static T1 dot(Quaternion q1, Quaternion q2);
static Quaternion fromRotMat(Matrix3<T1> mat);
static Quaternion fromAxisAngle(Vector3<T1> axis, T1 angle);
static Quaternion fromAngVel(Vector3<T1> omega);
T1 n;
Vector3<T1> v;
Quaternion();
Quaternion(T1 w, T1 x, T1 y, T1 z);
Quaternion(Vector3<T1> axis, T1 angle);
T1* ptr();
T1 const* ptr() const;
template<typename T2> Quaternion(Quaternion<T2> const& q);
template<typename T2> Quaternion& operator=(Quaternion<T2> const& q);
template<typename T2>
Quaternion& operator+=(Quaternion<T2> q);
template<typename T2>
Quaternion& operator-=(Quaternion<T2> q);
Quaternion& operator*=(T1 s);
Quaternion& operator/=(T1 s);
template<typename T2>
Quaternion operator+(Quaternion<T2> const& q) const;
template<typename T2>
Quaternion operator-(Quaternion<T2> const& q) const;
template<typename T2>
Quaternion operator*(Quaternion<T2> const& q) const;
template<typename T2>
Quaternion operator*(Vector3<T2> const& q) const;
Quaternion operator*(T1 const s) const;
Quaternion operator/(T1 const s) const;
Quaternion operator~() const;
Quaternion operator-() const;
T1 magnitude() const;
Quaternion& normalize();
Vector3<T1> axis() const;
T1 angle() const;
Matrix3<T1> toRotMat() const;
Quaternion rotate(Quaternion q2) const;
Vector3<T1> rotate(Vector3<T1> v) const;
};
typedef Quaternion<double> QuatD;
typedef Quaternion<float> QuatF;
template<typename T>
Quaternion<T>::Quaternion() {
n = 1;
v[0] = 0;
v[1] = 0;
v[2] = 0;
}
template<typename T>
Quaternion<T>::Quaternion(T w, T x, T y, T z) {
n = w;
v[0] = x;
v[1] = y;
v[2] = z;
}
template<typename T1>
Quaternion<T1>::Quaternion(Vector3<T1> axis, T1 angle) {
T1 d = axis.magnitude();
if (d != 0) {
T1 s = sin(0.5*angle) / d;
v[0] = (T1) axis[0] * s;
v[1] = (T1) axis[1] * s;
v[2] = (T1) axis[2] * s;
n = cos(0.5*angle);
}
else {
v[0] = v[1] = v[2] = 0;
n = 1;
}
}
template<typename T1> template<typename T2>
Quaternion<T1>::Quaternion(Quaternion<T2> const& q) {
n = q.n;
v = q.v;
}
template<typename T1> template<typename T2>
Quaternion<T1>& Quaternion<T1>::operator=(Quaternion<T2> const& q) {
n = q.n;
v = q.v;
return *this;
}
template<typename T>
T Quaternion<T>::magnitude() const {
return (T) sqrt(n*n + v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
}
template<typename T1> template<typename T2>
Quaternion<T1>& Quaternion<T1>::operator+=(Quaternion<T2> q) {
n += q.n;
v[0] += q.v[0];
v[1] += q.v[1];
v[2] += q.v[2];
return *this;
}
template<typename T1> template<typename T2>
Quaternion<T1>& Quaternion<T1>::operator-=(Quaternion<T2> q) {
n -= q.n;
v[0] -= q.v[0];
v[1] -= q.v[1];
v[2] -= q.v[2];
return *this;
}
template<typename T1>
Quaternion<T1>& Quaternion<T1>::operator*=(T1 s) {
n *= s;
v[0] *= s;
v[1] *= s;
v[2] *= s;
return *this;
}
template<typename T1>
Quaternion<T1> Quaternion<T1>::operator/(T1 s) const {
return Quaternion<T1>(n/s, v[0]/s, v[1]/s, v[2]/s);
}
template<typename T1>
Quaternion<T1>& Quaternion<T1>::operator/=(T1 s) {
n /= s;
v /= s;
return *this;
}
template<typename T1>
Quaternion<T1> Quaternion<T1>::operator~() const {
return Quaternion(n, -v[0], -v[1], -v[2]);
}
template<typename T1>
Quaternion<T1> Quaternion<T1>::operator-() const {
return Quaternion(-n, -v[0], -v[1], -v[2]);
}
template<typename T1> template<typename T2>
Quaternion<T1> Quaternion<T1>::operator+(
Quaternion<T2> const& q2) const {
return Quaternion<T1>( n + q2.n,
v[0] + q2.v[0],
v[1] + q2.v[1],
v[2] + q2.v[2]);
}
template<typename T1> template<typename T2>
Quaternion<T1> Quaternion<T1>::operator-(
Quaternion<T2> const& q2) const {
return Quaternion<T1>( n - q2.n,
v[0] - q2.v[0],
v[1] - q2.v[1],
v[2] - q2.v[2]);
}
template<typename T1> template<typename T2>
Quaternion<T1> Quaternion<T1>::operator*(
Quaternion<T2> const& q2) const {
return Quaternion<T1>(
n*q2.n - v[0]*q2.v[0] - v[1]*q2.v[1] - v[2]*q2.v[2],
n*q2.v[0] + v[0]*q2.n + v[1]*q2.v[2] - v[2]*q2.v[1],
n*q2.v[1] + v[1]*q2.n + v[2]*q2.v[0] - v[0]*q2.v[2],
n*q2.v[2] + v[2]*q2.n + v[0]*q2.v[1] - v[1]*q2.v[0]);
}
template<typename T1>
Quaternion<T1> Quaternion<T1>::operator*(T1 s) const {
return Quaternion<T1>( n * s, v[0] * s,
v[1] * s, v[2] * s );
}
template<typename T1> template<typename T2>
Quaternion<T1> Quaternion<T1>::operator*(Vector3<T2> const& v2) const {
return Quaternion<T1>( -(v[0]*v2[0] + v[1]*v2[1] + v[2]*v2[2]),
n*v2[0] + v[1]*v2[2] - v[2]*v2[1],
n*v2[1] + v[2]*v2[0] - v[0]*v2[2],
n*v2[2] + v[0]*v2[1] - v[1]*v2[0]);
}
template<typename T1>
T1 Quaternion<T1>::angle() const {
return T1(2*acos(n));
}
template<typename T1>
Vector3<T1> Quaternion<T1>::axis() const {
T1 m = v.magnitude();
if (m <= Tolerance)
return Vector3<T1();
else
return v/m;
}
template<typename T1>
Quaternion<T1> Quaternion<T1>::rotate(Quaternion q2) const {
return (*this)*q2*(~(*this));
}
template<typename T1>
Vector3<T1> Quaternion<T1>::rotate(Vector3<T1> v) const {
Quaternion<T1> t;
t = (*this)*v*(~(*this));
return t.v;
}
template<typename T>
Quaternion<T> Quaternion<T>::slerp(Quaternion<T> q, Quaternion<T> r, T a) {
q.normalize();
r.normalize();
T u, v, angle, s;
T dot = qdot(q, r);
if(dot < 0.0f) {
q = -q;
dot = qdot(q, r);
}
// acos gives NaN for dot slightly out of range
if(dot > -(1.0 - Tolerance)) {
if(dot < (1.0 - Tolerance)) {
angle = acos(dot);
s = sin(angle);
} else {
angle = 0;
s = 0;
}
} else {
angle = T(Constants::pi);
s = 0;
}
if(s == 0)
return angle<Constants::pi/2 ? q : r;
u = sin((1-a)*angle)/s;
v = sin(a*angle)/s;
return norm(u*q + v*r);
}
template<typename T>
Quaternion<T> Quaternion<T>::fromRotMat(Matrix3<T> mat) {
// quicker, but still wrong in one case
/*
Quaternion<T> q(1.0f, 0.0f, 0.0f, 0.0f);
T t;
if((t=0.25f*(1.0f + mat.r1[0] + mat.r2[1] + mat.r3[2])) > Tolerance) {
q.n = sqrt(t);
t = 4.0f*q.n;
q.v[0] = (mat.r2[2]-mat.r3[1]) / t;
q.v[1] = (mat.r3[0]-mat.r1[2]) / t;
q.v[2] = (mat.r1[1]-mat.r2[0]) / t;
} else if((t=-0.5f*(mat.r2[1]+mat.r3[2])) > Tolerance) {
q.v[0] = sqrt(t);
t=2.0f*q.v[0];
q.v[1] = mat.r1[1]/t;
q.v[2] = mat.r1[2]/t;
} else if((t=0.5f*(1-mat.r3[2])) > Tolerance) {
q.v[1]=sqrt(t);
q.v[2]=mat.r2[2]/(2.0f*q.v[1]);
}
return norm(q);
*/
Quaternion<T> quat;
T s;
T tr = mat[0][0] + mat[1][1] + mat[2][2];
if(tr >= 0.0f) {
s = sqrt(tr + 1.0f);
quat.n = s * 0.5f;
s = T(0.5) / s;
quat.v[0] = (mat.r3[1] - mat.r2[2]) * s;
quat.v[1] = (mat.r1[2] - mat.r3[0]) * s;
quat.v[2] = (mat.r2[0] - mat.r1[1]) * s;
}
else {
int i = 0;
if(mat.r2[1] > mat.r1[0]) i = 1;
if(mat.r3[2] > mat[i][i]) i = 2;
switch(i) {
case 0:
s = sqrt(1.0f + mat.r1[0] - mat.r2[1] - mat.r3[2]);
quat.v[0] = s*0.5f;
s = 0.5f / s;
quat.v[1] = (mat.r2[0] + mat.r1[1]) * s;
quat.v[2] = (mat.r1[2] + mat.r3[0]) * s;
quat.n = (mat.r3[1] - mat.r2[2]) * s;
break;
case 1:
s = sqrt(1.0f + mat.r2[1] - mat.r1[0] - mat.r3[2]);
quat.v[1] = s*0.5f;
s = 0.5f / s;
quat.v[2] = (mat.r3[1] + mat.r2[2]) * s;
quat.v[0] = (mat.r2[0] + mat.r1[1]) * s;
quat.n = (mat.r1[2] - mat.r3[0]) * s;
break;
case 2:
s = sqrt(1.0f + mat.r3[2] - mat.r1[0] - mat.r2[1]);
quat.v[2] = s*0.5f;
s = 0.5f / s;
quat.v[0] = (mat.r1[2] + mat.r3[0]) * s;
quat.v[1] = (mat.r3[1] + mat.r2[2]) * s;
quat.n = (mat.r2[0] - mat.r1[1]) * s;
break;
}
}
return norm(quat);
}
template<typename T>
Matrix3<T> Quaternion<T>::toRotMat() const {
Matrix3<T> mat;
T twx = 2*n*v[0];
T twy = 2*n*v[1];
T twz = 2*n*v[2];
T txy = 2*v[0]*v[1];
T txz = 2*v[0]*v[2];
T tyz = 2*v[1]*v[2];
T tx2 = 2*v[0]*v[0];
T ty2 = 2*v[1]*v[1];
T tz2 = 2*v[2]*v[2];
mat.r1[0] = 1 - ty2 - tz2;
mat.r2[0] = txy + twz;
mat.r3[0] = txz - twy;
mat.r1[1] = txy - twz;
mat.r2[1] = 1 - tx2 - tz2;
mat.r3[1] = tyz + twx;
mat.r1[2] = txz + twy;
mat.r2[2] = tyz - twx;
mat.r3[2] = 1 - tx2 - ty2;
return mat;
}
template<typename T>
Quaternion<T> Quaternion<T>::fromAxisAngle(Vector3<T> axis, T angle) {
Quaternion<T> ret;
ret.n = cos(angle / 2);
ret.v = norm(axis) * sin(angle / 2);
return ret;
}
template<typename T>
Quaternion<T> Quaternion<T>::fromAngVel(Vector3<T> axis) {
T mag = mag(axis);
return fromAxisAngle(axis/mag, mag);
}
template<typename T>
T Quaternion<T>::dot(Quaternion<T> q, Quaternion<T> r) {
return q.n*r.n + (q.v * r.v);
}
template<typename T>
Quaternion<T>& Quaternion<T>::normalize() {
operator/=(magnitude());
return *this;
}
template<typename T>
T qdot(Quaternion<T> const& q, Quaternion<T> const& r) {
return Quaternion<T>::dot(q, r);
}
template<typename T>
T mag(Quaternion<T> const& q) {
return q.magnitude();
}
template<typename T>
Quaternion<T> norm(Quaternion<T> q) {
return q.normalize();
}
template<typename T>
Vector3<T> qv_rotate(Quaternion<T> q, Vector3<T> v) {
return q.rotate(v);
}
template<typename T>
Quaternion<T> q_rotate(Quaternion<T> q1, Quaternion<T> q2) {
return q1.rotate(q2);
}
template<typename T1, typename T2>
Quaternion<T1> operator*(Vector3<T1> const& v, Quaternion<T2> const& q) {
return q * v;
}
template<typename T1>
Quaternion<T1> operator*(T1 s, Quaternion<T1> const& q) {
return q * s;
}
template<typename T1>
T1 const* Quaternion<T1>::ptr() const {
return &n;
}
template<typename T1>
T1* Quaternion<T1>::ptr() {
return &n;
}
}
#endif

View file

@ -0,0 +1,105 @@
#ifndef STAR_RUTABLE_HPP
#define STAR_RUTABLE_HPP
#include "StarInterpolation.hpp"
#include "StarArray.hpp"
#include <vector>
namespace Star {
template<typename Element, typename Position, size_t Rank, size_t StorageRank>
struct Storage;
template<typename Element, typename Position, size_t Rank>
struct Storage<Element, Position, Rank, 1> {
std::vector<Position> positions;
std::vector<Element> elements;
template<typename IndexList>
void pushDim(Position const& position, size_t dim, IndexList const& index) {
starAssert(false);
}
template<typename IndexList>
void pushElement(Position const& position, Element const& element, IndexList const& index) {
positions.push_back(position);
elements.push_back(element);
}
template<typename IndexList>
Element const& get(IndexList const& index) const {
return elements[index[Rank - 1]];
}
};
template<typename Element, typename Position, size_t Rank, size_t StorageRank>
struct Storage {
typedef Storage<Element, Position, Rank, StorageRank - 1> Stored;
template<typename IndexList>
void pushDim(Position const& position, size_t dim, IndexList const& index) {
if (dim == Rank - StorageRank) {
positions.push_back(position);
elements.push_back(Stored());
} else {
elements[index[Rank - StorageRank]].pushDim(position, dim, index);
}
}
template<typename IndexList>
void pushElement(Position const& position, Element const& element, IndexList const& index) {
elements[index[Rank - StorageRank]].pushElement(position, element, index);
}
template<typename IndexList>
Element const& get(IndexList const& index) const {
return elements[index[Rank - StorageRank]].get(index);
}
std::vector<Position> positions;
std::vector<Stored> elements;
};
template<typename ElementT, typename PositionT, size_t RankN>
class RUTable {
public:
typedef ElementT Element;
typedef PositionT Position;
static size_t const Rank = RankN;
typedef Array<Position, Rank> PositionList;
typedef Array<size_t, Rank> IndexList;
typedef Array<size_t, Rank> SizeList;
RUTable() : m_curDim(0), m_curIndex(IndexList::filled(0)) {}
void pushDim(Position const& position) {
m_storage.pushDim(position, m_curDim, m_curIndex);
++m_curDim;
}
void pushElement(Position const& position, Element const& element) {
m_storage.pushElement(position, element, m_curIndex);
++m_curIndex[Rank - 1];
}
void popDim() {
m_curIndex[m_curDim] = 0;
--m_curDim;
++m_curIndex[m_curDim];
}
Element const& get(IndexList const& index) const {
return m_storage.get(index);
}
private:
size_t m_curDim;
IndexList m_curIndex;
Storage<Element, Position, Rank, Rank> m_storage;
};
}
#endif

View file

@ -0,0 +1,420 @@
#ifndef STAR_SPARSE_ARRAY_HPP
#define STAR_SPARSE_ARRAY_HPP
#include <vector>
#include <limits>
#include <algorithm>
namespace Star {
// Sparse "infinite" (mapped to every possible value of IndexT) 1d array.
// Run-length compressed. Every element is always "set" -- starts out with
// default constructed value of DataT. Stays run-length compressed as elements
// are added or removed. CompareT should compare equality of two DataT,
// defaults to operator==. IndexT must be an integer-like type.
template<typename DataT, typename IndexT, typename CompareT = std::equal_to<DataT>,
typename IndexLimitsT = std::numeric_limits<IndexT> >
class SparseArray {
public:
typedef DataT Data;
typedef IndexT Index;
typedef CompareT Compare;
typedef IndexLimitsT IndexLimits;
static Index minIndex() {
return IndexLimits::min();
}
static Index maxIndex() {
return IndexLimits::max();
}
struct Element {
Element(Index const& i, Data const& v) : index(i), value(v) {}
Index index;
Data value;
bool operator<(Element const& e) const {
return index < e.index;
}
bool operator<(Index const& i) const {
return index < i;
}
};
struct ElementCompare {
ElementCompare(Compare const& r) : compare(r) {}
bool operator()(Element const& e1, Element const& e2) const {
return e1.index == e2.index && compare(e1.value, e2.value);
}
Compare const& compare;
};
typedef std::vector<Element> ElementList;
typedef typename ElementList::const_iterator ConstElementIterator;
typedef typename ElementList::iterator ElementIterator;
ElementIterator rangeForIndex(Index const& i) {
ElementIterator it = std::lower_bound(m_elements.begin(), m_elements.end(), i);
if (it != m_elements.end()) {
if (it->index != i)
--it;
} else {
--it;
}
return it;
}
ConstElementIterator rangeForIndex(Index const& i) const {
return const_cast<SparseArray*>(this)->rangeForIndex(i);
}
ElementList const& elements() const {
return m_elements;
}
SparseArray(Compare const& c = Compare()) : m_compare(c) {
m_elements.insert(m_elements.begin(), Element(minIndex(), Data()));
}
SparseArray(Data const& d, Compare const& c = Compare()) : m_compare(c) {
m_elements.insert(m_elements.begin(), Element(minIndex(), d));
}
Data const& get(Index const& i) const {
return rangeForIndex(i)->value;
}
void set(Index const& i, Data const& d) {
fill(i, i, d);
}
void fill(Data const& d) {
fill(minIndex(), maxIndex(), d);
}
// Find the ranges in the array that include begin and end, brange, and erange, as well as the length of erange.
// Delete every range in between, but not including brange and erange.
// Find the distance between begin index and brange start, and end index and erange end (bdiff, ediff)
// Store value of erange.
// If erange is not the same range as brange, delete erange.
// If brange value is equal to inserted value, then don't need to change brange.
// Else, if bdiff is 0, then check the range previous to brange. If values are equal, erase brange, else replace brange,
// Else, insert (begin, value) after brange.
// Now, if ediff is 0, then check if next range value is equal to this value, if so, erase.
// Else, insert (end+1, erange.value) after previously inserted range.
// Sets *inclusive* range.
void fill(Index const& begin, Index const& end, Data const& d) {
ElementIterator brangeIt = rangeForIndex(begin);
ElementIterator erangeIt = rangeForIndex(end);
if (brangeIt != erangeIt) {
++brangeIt;
if (brangeIt != erangeIt) {
erangeIt = m_elements.erase(brangeIt, erangeIt);
brangeIt = erangeIt;
}
--brangeIt;
}
Index erangeEnd;
ElementIterator pastIt(erangeIt);
++pastIt;
if (pastIt == m_elements.end())
erangeEnd = maxIndex();
else
erangeEnd = pastIt->index - 1;
bool bdiff = begin != brangeIt->index;
bool ediff = erangeEnd != end;
Data erangeVal = erangeIt->value;
if (brangeIt != erangeIt) {
erangeIt = m_elements.erase(erangeIt);
brangeIt = erangeIt;
--brangeIt;
}
// insertIt should point to range after erange after this.
ElementIterator insertIt = brangeIt;
if (!m_compare(insertIt->value, d)) {
if (!bdiff) {
if (insertIt != m_elements.begin()) {
ElementIterator prevIt = insertIt;
--prevIt;
if (m_compare(prevIt->value, d)) {
insertIt = m_elements.erase(insertIt);
} else {
insertIt->value = d;
++insertIt;
}
} else {
insertIt->value = d;
++insertIt;
}
} else {
++insertIt;
insertIt = m_elements.insert(insertIt, Element(begin, d));
++insertIt;
}
} else {
++insertIt;
}
if (!ediff) {
if (insertIt != m_elements.end()) {
if (m_compare(insertIt->value, d))
m_elements.erase(insertIt);
}
} else {
ElementIterator prevIt = insertIt;
--prevIt;
if (!m_compare(prevIt->value, erangeVal))
m_elements.insert(insertIt, Element(end+1, erangeVal));
}
}
template<typename Func>
void transform(Index const& begin, Index const& end, Func f) {
ElementIterator brangeIt = rangeForIndex(begin);
ElementIterator erangeIt = rangeForIndex(end);
Data erangeVal = erangeIt->value;
Index erangeEnd;
ElementIterator pastIt(erangeIt);
++pastIt;
if (pastIt == m_elements.end())
erangeEnd = maxIndex();
else
erangeEnd = pastIt->index - 1;
bool bdiff = begin != brangeIt->index;
bool ediff = erangeEnd != end;
if (bdiff) {
Data d = f(brangeIt->value);
if (!m_compare(d, brangeIt->value)) {
brangeIt = m_elements.insert(++brangeIt, Element(begin, d));
}
} else {
brangeIt->value = f(brangeIt->value);
}
ElementIterator last = brangeIt;
ElementIterator cur = brangeIt;
++cur;
while(true) {
if (cur == m_elements.end())
break;
if (cur->index > erangeEnd)
break;
cur->value = f(cur->value);
if (m_compare(cur->value, last->value)) {
cur = m_elements.erase(cur);
last = cur;
--last;
} else {
last = cur++;
}
}
if (!ediff) {
return;
} else {
if (!m_compare(last->value, erangeVal))
m_elements.insert(cur, Element(end+1, erangeVal));
}
}
class Proxy {
public:
Proxy(SparseArray& s, Index const& i) : ref(s), index(i) {}
operator Data const& () { return ref.get(index); }
Proxy& operator=(Data const& d) {
ref.set(index, d);
return *this;
}
private:
SparseArray& ref;
Index const& index;
};
Data const& operator()(Index const& i) const {
return get(i);
}
Proxy operator()(Index const& i) {
return Proxy(*this, i);
}
bool operator==(SparseArray const& sa) const {
if (m_elements.size() != sa.m_elements.size())
return false;
return std::equal(m_elements.begin(), m_elements.end(), sa.m_elements.begin(), ElementCompare(m_compare));
}
// Force compression (useful if m_compare is changed.)
void compress() {
for (ElementIterator i = m_elements.begin(); i != m_elements.end(); ++i) {
if (i != m_elements.begin()) {
ElementIterator p = i;
--p;
if (m_compare(p->value, i->value)) {
i = m_elements.erase(i);
--i;
}
}
}
}
void setCompare(Compare c) {
m_compare = c;
}
Compare const& compare() const {
return m_compare;
}
private:
ElementList m_elements;
Compare m_compare;
};
// 2 dimensional SparseArray
template<typename DataT, typename IndexT, typename CompareT = std::equal_to<DataT>,
typename IndexLimitsT = std::numeric_limits<IndexT> >
class SparseGrid {
public:
typedef DataT Data;
typedef IndexT Index;
typedef CompareT Compare;
typedef IndexLimitsT IndexLimits;
typedef SparseArray<Data, Index, Compare, IndexLimitsT> ArrayElement;
// Compare rows, ignoring rows with complexity greater than 'limit'
struct RowCompare {
RowCompare(size_t cl = std::numeric_limits<size_t>::max()) : limit(cl) {}
bool operator()(ArrayElement const& a, ArrayElement const& b) const {
if (a.elements().size() > limit || b.elements().size() > limit)
return false;
else
return a == b;
}
size_t limit;
};
typedef SparseArray<ArrayElement, Index, RowCompare, IndexLimits> RowArray;
static Index minIndex() {
return IndexLimits::min();
}
static Index maxIndex() {
return IndexLimits::max();
}
SparseGrid(size_t compareLimit = 1) : m_rows(Data(), RowCompare(compareLimit)) {}
size_t compareLimit() const {
return m_rows.compare().limit;
}
void setCompareLimit(size_t cl) {
m_rows.setCompare(RowCompare(cl));
}
Data const& get(Index const& i, Index const& j) const {
return m_rows.get(i).get(j);
}
void set(Index const& i, Index const& j, Data const& d) {
ArrayElement e = m_rows.get(i);
e(j) = d;
m_rows.set(i, e);
}
Data const& operator()(Index const& i, Index const& j) const {
return m_rows.get(i)(j);
}
friend class Proxy;
class Proxy {
public:
Proxy(SparseGrid& s, Index const& i, Index const& j) : ref(s), index_i(i), index_j(j) {}
operator Data const&() const { return ref.get(index_i, index_j); }
Proxy& operator=(Data const& d) {
ref.set(index_i, index_j, d);
return *this;
}
private:
SparseGrid& ref;
Index const& index_i;
Index const& index_j;
};
Proxy operator()(Index const& i, Index const& j) {
return Proxy(*this, i, j);
}
class FillTransform {
public:
FillTransform(Index const& min, Index const& max, Data const& d) : m_min(min), m_max(max), m_val(d) {}
ArrayElement operator()(ArrayElement const& e) const {
ArrayElement ne(e);
ne.fill(m_min, m_max, m_val);
return ne;
}
private:
Index const& m_min;
Index const& m_max;
Data const& m_val;
};
void fill(Data const& d) {
fill(minIndex(), maxIndex(), ArrayElement(d));
}
void fill(Index const& rmin, Index const& rmax, ArrayElement const& row) {
m_rows.fill(rmin, rmax, row);
}
void fill(Index const& rmin, Index const& cmin, Index const& rmax, Index const& cmax, Data const& d) {
m_rows.transform(rmin, rmax, FillTransform(cmin, cmax, d));
}
// Full compression, turns off RowCompare limit temporarily.
void compress() {
size_t oldCompareLimit = compareLimit();
setCompareLimit(std::numeric_limits<size_t>::max());
m_rows.compress();
setCompareLimit(oldCompareLimit);
}
RowArray const& rows() const {
return m_rows;
}
size_t elementCount() const {
size_t count = 0;
for (typename RowArray::ConstElementIterator i = m_rows.begin(); i != m_rows.end(); ++i)
count += m_rows.elements.count();
return count;
}
private:
RowArray m_rows;
};
}
#endif

View file

@ -0,0 +1,185 @@
#ifndef STAR_TABLE_HPP
#define STAR_TABLE_HPP
#include "StarMultiArrayInterpolator.hpp"
namespace Star {
// Provides a method for storing, retrieving, and interpolating even n-variate
// data. Access time is O(n), where n is the GridRank of the Table
// Interpolation time for piecewise is O(n), O(2^n) for linear,
// O(4^n) for cubic.
template<typename ElementT, typename PositionT, size_t RankN>
class Table {
public:
typedef ElementT Element;
typedef PositionT Position;
static size_t const Rank = RankN;
typedef Star::MultiArray<ElementT, RankN> MultiArray;
typedef Star::MultiArrayInterpolator2<MultiArray, Position> Interpolator2;
typedef Star::MultiArrayInterpolator4<MultiArray, Position> Interpolator4;
typedef Star::MultiArrayPiecewiseInterpolator<MultiArray, Position> PiecewiseInterpolator;
typedef Array<Position, Rank> PositionList;
typedef Array<Position, 2> WeightList2;
typedef Array<Position, 4> WeightList4;
typedef typename MultiArray::SizeList SizeList;
typedef typename MultiArray::IndexList IndexList;
typedef std::function<WeightList2(Position)> WeightFunction2;
typedef std::function<WeightList4(Position)> WeightFunction4;
typedef std::function<Element(PositionList const&)> InterpolateFunction;
struct TableRange {
Position min;
Position max;
};
Table() {}
Table(SizeList const& size, PositionList const& min, PositionList const& max) {
setDimensions(size, min, max);
}
MultiArray const& array() const {
return m_array;
}
MultiArray& array() {
return m_array;
}
void setRanges(PositionList const& min, PositionList const& max) {
for (size_t i = 0; i < Rank; ++i) {
m_ranges[i].min = min[i];
m_ranges[i].max = max[i];
}
}
void setRange(size_t dim, Position min, Position max) {
m_ranges[dim].min = min;
m_ranges[dim].max = max;
}
TableRange const& range(size_t dim) const {
return m_ranges[dim];
}
TableRange& range(size_t dim) {
return m_ranges[dim];
}
void setDimension(size_t dim, size_t size, Position min, Position max) {
SizeList sizes = m_array.sizes();
sizes[dim] = size;
m_array.resize(sizes);
setRange(dim, min, max);
}
void setDimensions(SizeList const& sizes, PositionList const& min, PositionList const& max) {
m_array.resize(sizes);
setRanges(min, max);
}
void set(IndexList const& index, Element const& element) {
m_array.set(index, element);
}
Element const& get(IndexList const& index) const {
return m_array(index);
}
void set2TermInterpolation(WeightFunction2 const& weightFunction, BoundMode boundMode) {
Interpolator2 interpolator2(weightFunction, boundMode);
m_interpolateFunction = [=](PositionList const& position) {
return interpolator2.interpolate(this->m_array, this->toIndexSpace(position));
};
}
void set4TermInterpolation(WeightFunction4 const& weightFunction, BoundMode boundMode) {
Interpolator4 interpolator4(weightFunction, boundMode);
m_interpolateFunction = [=](PositionList const& position) {
return interpolator4.interpolate(this->m_array, this->toIndexSpace(position));
};
}
void setPiecewiseInterpolation(WeightFunction2 const& weightFunction, BoundMode boundMode) {
PiecewiseInterpolator piecewiseInterpolator(weightFunction, boundMode);
m_interpolateFunction = [=](PositionList const& position) {
return piecewiseInterpolator.interpolate(this->m_array, this->toIndexSpace(position));
};
}
Element interpolate(PositionList const& coord) const {
return m_interpolateFunction(coord);
}
Element operator()(PositionList const& coord) const {
return interpolate(coord);
}
// op should take a PositionList parameter and return an element.
template<typename OpType>
void eval(OpType op) {
m_array.eval(EvalWrapper<OpType>(op, *this));
}
protected:
PositionList toIndexSpace(PositionList const& coord) const {
PositionList indexCoord;
for (size_t i = 0; i < Rank; ++i)
indexCoord[i] = (coord[i] - m_ranges[i].min) / delta(i);
return indexCoord;
}
Position toTableSpace(Position pos, size_t dim) const {
return m_ranges[dim].min + pos * delta(dim);
}
Position delta(size_t dim) const {
return (m_ranges[dim].max - m_ranges[dim].min) / (m_array.size(dim) - 1);
}
private:
enum InterpolateMode {
Interpolate2Mode,
Interpolate4Mode,
PiecewiseInterpolateMode
};
template<typename OpType>
struct EvalWrapper {
EvalWrapper(OpType &o, Table const& t) : op(o), table(t) {}
template<typename IndexList>
Element operator()(IndexList const& indexList) {
PositionList rangeList;
for (size_t i = 0; i < Rank; ++i)
rangeList[i] = table.toTableSpace(indexList[i], i);
return op(rangeList);
}
OpType& op;
Table const& table;
};
InterpolateFunction m_interpolateFunction;
Array<TableRange, Rank> m_ranges;
MultiArray m_array;
};
typedef Table<float, float, 2> Table2F;
typedef Table<double, double, 2> Table2D;
typedef Table<float, float, 3> Table3F;
typedef Table<double, double, 3> Table3D;
typedef Table<float, float, 4> Table4F;
typedef Table<double, double, 4> Table4D;
}
#endif

View file

@ -0,0 +1,202 @@
#ifndef STAR_UTABLE_HPP
#define STAR_UTABLE_HPP
#include "StarTable.hpp"
namespace Star {
// Provides a method for storing, retrieving, and interpolating uneven
// n-variate data. Access times involve a binary search over the domain of
// each dimension, so is O(log(n)*m) where n is the size of the largest
// dimension, and m is the table_rank.
//
// BoundMode Wrap makes little mathematical sense for UTable.
template<typename ElementT, typename PositionT, size_t RankN>
class UTable {
public:
typedef ElementT Element;
typedef PositionT Position;
static size_t const Rank = RankN;
typedef Star::MultiArray<ElementT, RankN> MultiArray;
typedef Star::MultiArrayInterpolator2<MultiArray, Position> Interpolator2;
typedef Star::MultiArrayPiecewiseInterpolator<MultiArray, Position> PiecewiseInterpolator;
typedef Array<Position, Rank> PositionList;
typedef Array<Position, 2> WeightList2;
typedef Array<Position, 4> WeightList4;
typedef typename MultiArray::SizeList SizeList;
typedef typename MultiArray::IndexList IndexList;
typedef std::vector<Position> Range;
typedef Vector<Range, Rank> RangeList;
typedef std::function<WeightList2(Position)> WeightFunction2;
// Evenly spaced table that UTable can be resampled into.
typedef Table<Element, Position, Rank> ResampledTable;
// Set input ranges on a particular dimension. Will resize underlying storage to fit range.
void setRange(std::size_t dim, Range const& range) {
SizeList sizes = m_array.sizes();
sizes[dim] = range.size();
m_array.resize(sizes);
m_ranges[dim] = range;
}
template<typename... Rest>
void setRanges(RangeList const& ranges) {
SizeList arraySize;
for (size_t dim = 0; dim < Rank; ++dim) {
arraySize[dim] = ranges[dim].size();
m_ranges[dim] = ranges[dim];
}
m_array.resize(arraySize);
}
template<typename... T>
void setRanges(Range const& range, T const&... rest) {
setRanges(RangeList{range, rest...});
}
// Set array element based on index.
void set(IndexList const& index, Element const& element) {
m_array.set(index, element);
}
// Get array element based on index.
Element const& get(IndexList const& index) const {
return m_array(index);
}
MultiArray const& array() const {
return m_array;
}
MultiArray& array() {
return m_array;
}
void set2TermInterpolation(WeightFunction2 const& weightFunction, BoundMode boundMode) {
Interpolator2 interpolator2(weightFunction, boundMode);
m_interpolateFunction = [=](PositionList const& position) {
return interpolator2.interpolate(this->m_array, this->toIndexSpace(position));
};
m_resampleFunction = [=](SizeList const& size) {
ResampledTable resampledTable;
resampledTable.reshape(size);
for (size_t i = 0; i < Rank; ++i)
resampledTable.setRange(i, *this->m_ranges[i].begin(), *--this->m_ranges[i].end());
interpolator2.sample(this->m_array, resampledTable.array(), ResampleCoordinateOp(*this, resampledTable));
return resampledTable;
};
}
void setPiecewiseInterpolation(WeightFunction2 const& weightFunction, BoundMode boundMode) {
PiecewiseInterpolator piecewiseInterpolator(weightFunction, boundMode);
m_interpolateFunction = [=](PositionList const& position) {
return piecewiseInterpolator.interpolate(this->m_array, this->toIndexSpace(position));
};
m_resampleFunction = [=](SizeList const& size) {
ResampledTable resampledTable;
resampledTable.reshape(size);
resampledTable.eval([=](typename ResampledTable::PositionList const& position) {
piecewiseInterpolator.interpolate(this->m_array, this->toIndexSpace(position));
});
return resampledTable;
};
}
Element interpolate(PositionList const& coord) const {
return m_interpolateFunction(coord);
}
Element operator()(PositionList const& coord) const {
return interpolate(coord);
}
ResampledTable resample(SizeList const& size) const {
return m_resampleFunction(size);
}
// op should take a PositionList parameter and return an element.
template<typename OpType>
void eval(OpType op) {
m_array.eval(EvalWrapper<OpType>(op, *this));
}
private:
struct ResampleCoordinateOp {
typedef Position Scalar;
ResampleCoordinateOp(UTable const& u, ResampledTable const& t) :
utable(u), table(t) {}
Scalar operator()(size_t dim, size_t pos) {
return inverseLinearInterpolate(utable.m_ranges[dim], table.toTableSpace(pos, dim));
}
PositionList operator()(size_t dim, int i) {
return inverseLinearInterpolate(utable.getRange(dim),
table.getTransformedCoord(i, dim));
}
UTable const& utable;
ResampledTable const& table;
};
template<typename Coordinate>
inline PositionList toIndexSpace(Coordinate const& coord) const {
PositionList indexCoord;
for (size_t i = 0; i < Rank; ++i)
indexCoord[i] = inverseLinearInterpolate(m_ranges[i], coord[i]);
return indexCoord;
}
template<typename OpType>
struct EvalWrapper {
EvalWrapper(OpType &o, UTable const& t) : op(o), table(t) {}
template<typename IndexList>
Element operator()(IndexList const& indexList) {
PositionList rangeList;
for (size_t i = 0; i < Rank; ++i)
rangeList[i] = table.m_ranges[i][indexList[i]];
return op(rangeList);
}
OpType& op;
UTable const& table;
};
typedef std::function<Element(PositionList const&)> InterpolateFunction;
typedef std::function<ResampledTable(SizeList const&)> ResampleFunction;
RangeList m_ranges;
InterpolateFunction m_interpolateFunction;
ResampleFunction m_resampleFunction;
MultiArray m_array;
};
typedef UTable<float, float, 2> UTable2F;
typedef UTable<double, double, 2> UTable2D;
typedef UTable<float, float, 3> UTable3F;
typedef UTable<double, double, 3> UTable3D;
typedef UTable<float, float, 4> UTable4F;
typedef UTable<double, double, 4> UTable4D;
}
#endif

View file

@ -0,0 +1,24 @@
#include "StarGuardedPtr.hpp"
#include "gtest/gtest.h"
using namespace Star;
TEST(GuardedPtr, All) {
GuardedPtr<int> ptr1;
{
GuardedPtr<int> ptr2;
GuardedHolder<int> holder(new int (5), true);
ptr1 = ptr2 = holder.guardedPtr();
EXPECT_TRUE((bool)ptr1);
EXPECT_TRUE((bool)ptr2);
EXPECT_EQ(*ptr1, 5);
EXPECT_EQ(*ptr2, 5);
}
EXPECT_FALSE((bool)ptr1);
}

View file

@ -0,0 +1,229 @@
#include "StarCellularLiquid.hpp"
#include "StarRandom.hpp"
#include "StarRenderer.hpp"
#include "StarRootLoader.hpp"
#include "StarLiquidsDatabase.hpp"
#include "StarAssets.hpp"
#include "StarTextPainter.hpp"
#include "StarMainApplication.hpp"
using namespace Star;
constexpr int LiquidArrayWidth = 60;
constexpr int LiquidArrayHeight = 60;
constexpr int LiquidArraySlowBorder = 0;
constexpr int LiquidArraySlowTickLimit = 200;
constexpr float LiquidBottomDrainPercentage = 0.5f;
class LiquidTest : public Application {
protected:
struct LiquidStore {
bool collision;
bool endless;
Maybe<size_t> liquid;
float level;
float pressure;
};
void startup(StringList const& cmdLineArgs) override {
RootLoader rootLoader({{}, {}, {}, LogLevel::Info, false, {}});
m_root = rootLoader.initOrDie(cmdLineArgs).first;
m_liquidArray.resize(Array2S(LiquidArrayWidth, LiquidArrayHeight), LiquidStore());
m_paused = false;
m_step = false;
m_update = 0;
struct CellWorld : CellularLiquidWorld<size_t> {
LiquidTest& parent;
CellWorld(LiquidTest& parent) : parent(parent) {}
float drainLevel(Vec2I const& location) const override {
return location[1] == 0 ? LiquidBottomDrainPercentage : 0.0f;
}
CellularLiquidCell<size_t> cell(Vec2I const& location) const override {
if (location[0] < 0 || location[1] < 0 || location[0] >= (int)parent.m_liquidArray.size(0) || location[1] >= (int)parent.m_liquidArray.size(1))
return {};
auto const& store = parent.m_liquidArray(location[0], location[1]);
if (store.collision)
return CellularLiquidCollisionCell();
if (store.endless)
return CellularLiquidSourceCell<size_t>{*store.liquid, store.pressure};
else
return CellularLiquidFlowCell<size_t>{store.liquid, store.level, store.pressure};
}
void setFlow(Vec2I const& location, CellularLiquidFlowCell<size_t> const& flow) override {
if (location[0] < 0 || location[1] < 0 || location[0] >= (int)parent.m_liquidArray.size(0) || location[1] >= (int)parent.m_liquidArray.size(1))
return;
auto& store = parent.m_liquidArray(location[0], location[1]);
if (!store.endless && !store.collision) {
store.liquid = flow.liquid;
store.level = flow.level;
store.pressure = flow.pressure;
}
}
void liquidInteraction(Vec2I const& a, size_t aLiquid, Vec2I const& b, size_t bLiquid) override {
if (a[0] < 0 || a[1] < 0 || a[0] >= (int)parent.m_liquidArray.size(0) || a[1] >= (int)parent.m_liquidArray.size(1))
return;
if (b[0] < 0 || b[1] < 0 || b[0] >= (int)parent.m_liquidArray.size(0) || b[1] >= (int)parent.m_liquidArray.size(1))
return;
auto& aCell = parent.m_liquidArray(a[0], a[1]);
auto& bCell = parent.m_liquidArray(b[0], b[1]);
if (aLiquid == 0 && bLiquid == 2 && !aCell.endless)
bCell = LiquidStore{true, false, {}, 0.0f, 0.0f};
if (aLiquid == 2 && bLiquid == 0 && !bCell.endless)
aCell = LiquidStore{true, false, {}, 0.0f, 0.0f};
if (aLiquid == 0 && bLiquid == 1 && !aCell.endless)
aCell.liquid = 1;
if (aLiquid == 1 && bLiquid == 0 && !bCell.endless)
bCell.liquid = 1;
}
};
m_liquidEngine = make_shared<LiquidCellEngine<size_t>>(
Root::singleton().liquidsDatabase()->liquidEngineParameters(), make_shared<CellWorld>(*this));
auto assets = Root::singleton().assets();
m_liquids = {{0, 0, 255, 255}, {0, 255, 0, 255}, {255, 0, 0, 255}};
m_currentLiquid = 0;
m_liquidEngine->setLiquidTickDelta(0, 2);
m_liquidEngine->setLiquidTickDelta(1, 3);
m_liquidEngine->setLiquidTickDelta(2, 5);
m_liquidEngine->setProcessingLimit(LiquidArraySlowTickLimit);
m_liquidEngine->setNoProcessingLimitRegions({RectI(0, 0, LiquidArrayWidth, LiquidArrayHeight).trimmed(LiquidArraySlowBorder)});
}
void renderInit(RendererPtr renderer) override {
Application::renderInit(renderer);
m_textPainter = make_shared<TextPainter>(m_root->assets()->font("/hobo.ttf")->clone(), renderer);
m_textPainter->setFontSize(16);
}
void update() override {
if (m_mouseButtonHeld) {
Vec2I cell = screenToCell(m_mousePos);
auto& mouseCell = m_liquidArray(cell[0], cell[1]);
if (*m_mouseButtonHeld == MouseButton::Left) {
mouseCell = LiquidStore{true, false, {}, 0.0f, 0.0f};
} else if (*m_mouseButtonHeld == MouseButton::Middle) {
mouseCell = LiquidStore{false, true, m_currentLiquid, 1.0f, 5.0f};
} else if (*m_mouseButtonHeld == MouseButton::Right) {
mouseCell = LiquidStore{false, false, {}, 0.0f, 0.0f};
}
m_liquidEngine->visitLocation(cell);
}
if (!m_paused || m_step) {
m_liquidEngine->update();
++m_update;
}
m_step = false;
}
void render() override {
for (int x = 0; x < LiquidArrayWidth; ++x) {
for (int y = 0; y < LiquidArrayHeight; ++y) {
auto const& cell = m_liquidArray(x, y);
RectF screenRect = cellScreenRect({x, y});
Vec4B color;
if (cell.collision)
color = {255, 255, 255, 255};
if (cell.endless)
color = {255, 0, 255, 255};
else if (cell.liquid)
color = liquidColor(*cell.liquid, cell.level);
renderer()->render(renderFlatRect(screenRect, color));
}
}
Vec2I mouseCellPos = screenToCell(m_mousePos);
auto const& mouseCell = m_liquidArray(mouseCellPos[0], mouseCellPos[1]);
String hoverText = strf(
"fps: %s\nactive water cells: %s\nactive acid cells: %s\nactive lava cells: %s\ncell: %s\npaused: %s\ncurrent "
"liquid: %d\ncell collision: %s\ncell liquid: %d\ncell level: %s\ncell pressure: %s\n",
appController()->renderFps(), m_liquidEngine->activeCells(0), m_liquidEngine->activeCells(1), m_liquidEngine->activeCells(2),
mouseCellPos, m_paused, m_currentLiquid, mouseCell.collision, mouseCell.liquid, mouseCell.level, mouseCell.pressure);
m_textPainter->renderText(hoverText, TextPositioning(Vec2F(0, renderer()->screenSize()[1]), HorizontalAnchor::LeftAnchor, VerticalAnchor::TopAnchor));
}
void processInput(InputEvent const& event) override {
if (auto keyDown = event.ptr<KeyDownEvent>()) {
if (keyDown->key == Key::P)
m_paused = !m_paused;
if (keyDown->key == Key::S)
m_step = true;
} else if (auto mouseDown = event.ptr<MouseButtonDownEvent>()) {
m_mouseButtonHeld = mouseDown->mouseButton;
} else if (auto mouseWheel = event.ptr<MouseWheelEvent>()) {
if (mouseWheel->mouseWheel == MouseWheel::Up)
m_currentLiquid = pmod<int>((int)m_currentLiquid + 1, m_liquids.size());
else
m_currentLiquid = pmod<int>((int)m_currentLiquid - 1, m_liquids.size());
} else if (event.is<MouseButtonUpEvent>()) {
m_mouseButtonHeld.reset();
} else if (auto mme = event.ptr<MouseMoveEvent>()) {
m_mousePos = mme->mousePosition;
}
}
private:
typedef MultiArray<LiquidStore, 2> LiquidArray;
Vec2F cellScreenDimensions() const {
Vec2F windowSize(renderer()->screenSize());
return {windowSize[0] / LiquidArrayWidth, windowSize[1] / LiquidArrayHeight};
}
RectF cellScreenRect(Vec2I const& c) const {
Vec2F cdim = cellScreenDimensions();
Vec2F ll = Vec2F(cdim[0] * c[0], cdim[1] * c[1]);
return RectF::withSize(ll, cdim);
}
Vec2I screenToCell(Vec2I const& screen) const {
Vec2F cdim = cellScreenDimensions();
Vec2I cpos = Vec2I::floor(Vec2F(screen[0] / cdim[0], screen[1] / cdim[1]));
return Vec2I(clamp(cpos[0], 0, LiquidArrayWidth - 1), clamp(cpos[1], 0, LiquidArrayHeight - 1));
}
Vec4B liquidColor(size_t liquid, float level) {
Vec4B color = m_liquids.at(liquid);
return Color::v4fToByte(Color::v4bToFloat(color) * level);
}
RootUPtr m_root;
LiquidArray m_liquidArray;
shared_ptr<LiquidCellEngine<size_t>> m_liquidEngine;
List<Vec4B> m_liquids;
bool m_paused;
bool m_step;
uint64_t m_update;
Vec2I m_mousePos;
RendererPtr m_renderer;
TextPainterPtr m_textPainter;
Maybe<MouseButton> m_mouseButtonHeld;
size_t m_currentLiquid;
};
STAR_MAIN_APPLICATION(LiquidTest);

View file

@ -0,0 +1,147 @@
#include "StarMainApplication.hpp"
using namespace Star;
class MainApplication : public Application {
protected:
void renderInit(RendererPtr renderer) override {
Application::renderInit(renderer);
if (renderer->rendererId() == "OpenGL20") {
renderer->setEffectConfig(Json::parseJson(R"JSON(
{
"effectParameters" : {
"lightMapEnabled" : {
"type" : "bool",
"default" : false,
"uniform" : "lightMapEnabled"
},
"lightMapScale" : {
"type" : "vec2",
"default" : [1, 1],
"uniform" : "lightMapScale"
},
"lightMapMultiplier" : {
"type" : "float",
"default" : 1.0,
"uniform" : "lightMapMultiplier"
}
},
"effectTextures" : {
"lightMap" : {
"textureUniform" : "lightMap",
"textureSizeUniform" : "lightMapSize",
"textureAddressing" : "clamp",
"textureFiltering" : "linear"
}
},
"vertexShader" : "
#version 110
uniform vec2 textureSize;
uniform vec2 screenSize;
uniform mat3 vertexTransform;
uniform vec2 lightMapSize;
uniform vec2 lightMapScale;
attribute vec2 vertexPosition;
attribute vec2 vertexTextureCoordinate;
attribute vec4 vertexColor;
varying vec2 fragmentTextureCoordinate;
varying vec4 fragmentColor;
varying vec2 fragmentLightMapCoordinate;
void main() {
vec2 screenPosition = (vertexTransform * vec3(vertexPosition, 1.0)).xy;
gl_Position = vec4(screenPosition / screenSize * 2.0 - 1.0, 0.0, 1.0);
fragmentLightMapCoordinate = (screenPosition / lightMapScale) / lightMapSize;
fragmentTextureCoordinate = vertexTextureCoordinate / textureSize;
fragmentColor = vertexColor;
}
",
"fragmentShader" : "
#version 110
uniform sampler2D texture;
uniform bool lightMapEnabled;
uniform sampler2D lightMap;
uniform float lightMapMultiplier;
varying vec2 fragmentTextureCoordinate;
varying vec4 fragmentColor;
varying vec2 fragmentLightMapCoordinate;
void main() {
vec4 finalColor = texture2D(texture, fragmentTextureCoordinate) * fragmentColor;
if (lightMapEnabled)
finalColor *= texture2D(lightMap, fragmentLightMapCoordinate) * lightMapMultiplier;
gl_FragColor = finalColor;
}
"
}
)JSON"));
}
Image texture1Image(10, 10, PixelFormat::RGBA32);
for (unsigned y = 0; y < 10; ++y) {
for (unsigned x = 0; x < 10; ++x) {
if (x < 3 || x > 7)
texture1Image.set(x, y, Vec3B(0, 0, 0));
else
texture1Image.set(x, y, Vec3B(255, 255, 255));
}
}
auto texture1 = renderer->createTexture(texture1Image, TextureAddressing::Clamp, TextureFiltering::Linear);
Image texture2Image(10, 10, PixelFormat::RGBA32);
for (unsigned y = 0; y < 10; ++y) {
for (unsigned x = 0; x < 10; ++x) {
if (y < 3 || y > 7)
texture2Image.set(x, y, Vec3B(0, 0, 0));
else
texture2Image.set(x, y, Vec3B(255, 255, 255));
}
}
auto texture2 = renderer->createTexture(texture2Image, TextureAddressing::Clamp, TextureFiltering::Linear);
List<RenderPrimitive> primitives;
for (size_t y = 0; y < 100; ++y) {
for (size_t x = 0; x < 100; ++x) {
primitives.append(RenderQuad{(y % 2 == 0) ? texture1 : texture2,
RenderVertex{Vec2F(x, y), Vec2F(0, 0), Vec4B(255, 255, 255, 255)},
RenderVertex{Vec2F(x + 1, y), Vec2F(10, 0), Vec4B(255, 255, 255, 255)},
RenderVertex{Vec2F(x + 1, y + 1), Vec2F(10, 10), Vec4B(255, 255, 255, 255)},
RenderVertex{Vec2F(x, y + 1), Vec2F(0, 10), Vec4B(255, 255, 255, 255)}});
}
}
m_renderBuffer = renderer->createRenderBuffer();
m_renderBuffer->set(primitives);
Image lightMapImage(50, 50, PixelFormat::RGBA32);
for (unsigned y = 0; y < 50; ++y) {
for (unsigned x = 0; x < 50; ++x)
lightMapImage.set(x, y, Vec3B((x + 1) * 5.2 - 5, (y + 1) * 5.2 - 5, 255));
}
renderer->setEffectParameter("lightMapEnabled", true);
renderer->setEffectTexture("lightMap", lightMapImage);
}
void render() override {
Vec2U screenSize = renderer()->screenSize();
renderer()->setEffectParameter("lightMapScale", Vec2F(screenSize[0] / 50.0f, screenSize[1] / 50.0f));
renderer()->setScissorRect(RectI(20, 20, screenSize[0] - 20, screenSize[1] - 20));
renderer()->renderBuffer(m_renderBuffer, Mat3F::scaling(Vec2F(screenSize[0] / 100.0f, screenSize[1] / 100.0f)));
}
private:
RenderBufferPtr m_renderBuffer;
};
STAR_MAIN_APPLICATION(MainApplication);