ImFusion SDK 4.3
ExampleStreams.cpp
#include <ImFusion/Stream/Stream.h>
#include <ImFusion/Stream/StreamData.h>
#include <ImFusion/Stream/StreamUtils.h>
#include <ImFusion/Stream/TrackingStream.h>
#include <iostream>
#include <memory>
#include <vector>
using namespace ImFusion;
class MockData : public StreamData
{
public:
explicit MockData(Stream* stream)
: StreamData(stream)
{
}
MockData& operator=(const MockData& other)
{
m_stream = other.m_stream;
return *this;
}
std::unique_ptr<StreamData> clone() const override { return std::make_unique<MockData>(*this); }
};
// Stream which performs some work to generate every data sample in the Stream working thread
class PeriodicStream : public Stream
{
public:
std::string uuid() override { return std::to_string((size_t)this); }
protected:
bool openImpl() override { return true; }
bool closeImpl() override { return true; }
bool startImpl() override { return true; }
bool stopImpl() override { return true; }
std::optional<WorkContinuation> doWork() override
{
// determine the time at which doWork() should be called next by the working thread
// this pattern should lead to a constant frame rate, unless the computation exceeds the available runtime
auto startTime = std::chrono::system_clock::now();
auto nextIterationTime = startTime + std::chrono::milliseconds(100);
// mock for an operation that can take a varying amount of time, e.g. querying data from a device or reading from the filesystem
// however, we want to make sure to not exceed 80 milliseconds and use the preciseSleep method instead of the build in std::chrono::sleep_for
StreamUtils::preciseSleep(std::chrono::milliseconds(80 * (std::rand() / RAND_MAX)));
std::shared_ptr<MockData> md = std::make_shared<MockData>(this);
signalStreamData.emitSignal(md);
return WorkContinuation{nextIterationTime};
}
};
// Stream which performs some work to generate every data sample in the Stream working thread, with extra care for timings
class ExactlyPeriodicStream : public Stream
{
public:
std::string uuid() override { return std::to_string((size_t)this); }
protected:
bool openImpl() override { return true; }
bool closeImpl() override { return true; }
bool startImpl() override
{
return true;
}
bool stopImpl() override { return true; }
std::optional<WorkContinuation> doWork() override
{
if (m_nextData)
signalStreamData.emitSignal(std::move(m_nextData));
auto nextIterationTime = m_startTime + std::chrono::milliseconds(100 * m_iteration++);
StreamUtils::preciseSleep(std::chrono::milliseconds(80 * (std::rand() / RAND_MAX)));
// do not emit the generated data immediately, but wait for the next iteration
// this pattern introduces extra latency, but leads to more precise timings
m_nextData = std::make_shared<MockData>(this);
return WorkContinuation{nextIterationTime};
}
int m_iteration = 1;
std::chrono::system_clock::time_point m_startTime;
std::shared_ptr<MockData> m_nextData;
};
namespace MockFramework
{
void registerCallback(std::function<void()> callback) {}
bool start() { return true; }
bool stop() { return true; }
bool tearDown() { return true; }
};
// Stream which does not use the working thread, but publishes the data within a callback registered elsewhere
class ExternalWorkStream : public Stream
{
public:
std::string uuid() override { return std::to_string((size_t)this); }
protected:
bool openImpl() override
{
MockFramework::registerCallback([this]() {
std::shared_ptr<MockData> md = std::make_shared<MockData>(this);
signalStreamData.emitSignal(md);
});
return true;
}
bool closeImpl() override { return MockFramework::tearDown(); }
bool startImpl() override { return MockFramework::start(); }
bool stopImpl() override { return MockFramework::stop(); }
std::optional<WorkContinuation> doWork() override
{
// request not to periodically call this function; the external framework will emit signalStreamData instead
// doWork() will still be called at least once when starting the Stream, so this can be done conditionally depending on context
return std::nullopt;
}
};
// Tracking stream example using the worker thread of the base class
class MyTrackingStream : public TrackingStream
{
public:
~MyTrackingStream() { MyTrackingStream::close(); }
virtual bool openImpl() override
{
// creates a a TrackerID with ID "systemUID-123" and name "Tracker 1" (model number is empty)
m_instruments->create(TrackerID{"systemUID-123", "", "Tracker 1"});
m_instruments->create(TrackerID{"systemUID-456", "", "Tracker 2"});
// alternatively, autogenerate TrackerID with ID "systemUID-0", "systemUID-1", and so on
// m_instruments->createWithPrefix("systemUID", "Tracker 1");
// m_instruments->createWithPrefix("systemUID", "Tracker 2");
return true;
}
virtual std::optional<WorkContinuation> doWork() override
{
// query tracking information from device such as tracking poses (mat4)
// and qualities [0.0-1.0] in every iteration of the stream loop while running
mat4 matrixTracker1 = mat4::Identity();
double qualityTracker1 = 1.0;
mat4 matrixTracker2 = mat4::Identity();
double qualityTracker2 = 1.0;
m_instruments->update("systemUID-123", matrixTracker1, qualityTracker1);
m_instruments->update("systemUID-456", matrixTracker2, qualityTracker2);
// send out data (with current time as arrival and device timestamp)
signalStreamData.emitSignal(createTrackingStreamData());
return {};
}
// override other abstract methods
virtual std::string uuid() override { return "MyTrackingStream"; }
virtual bool closeImpl() override { return true; }
virtual bool startImpl() override { return true; }
virtual bool stopImpl() override { return true; }
};
Base class for all data streamed from a streaming device.
Definition StreamData.h:23
Base class representing a stream of changing data in the DataModel.
Definition Stream.h:60
Specialization of Stream for streams producing tracking data for spatial localization.
Definition TrackingStream.h:28
T make_shared(T... args)
T make_unique(T... args)
Namespace of the ImFusion SDK.
Definition Assert.h:7
T rand(T... args)
T to_string(T... args)
Search Tab / S to search, Esc to close