demotool/c-sync.hh

495 lines
13 KiB
C++
Raw Normal View History

2017-10-07 16:56:20 +02:00
#pragma once
#include "c-filewatcher.hh"
#include "c-project.hh"
2017-10-07 16:56:20 +02:00
2017-11-05 21:06:38 +01:00
#include <ebcl/SRDParserConfig.hh>
2017-11-16 09:55:56 +01:00
#include <ebcl/Sets.hh>
#include <ebcl/Algorithms.hh>
2017-11-05 21:06:38 +01:00
// Duration and current playing time
struct T_SyncTime
2017-10-07 16:56:20 +02:00
{
2017-10-30 18:29:52 +01:00
float uDuration = 1.f / 60.f; // Duration - unit size
uint32_t iDuration = 3600; // Duration - total units
float time = 0;
2017-10-17 11:30:44 +02:00
void setDuration(
2017-11-03 09:08:19 +01:00
const float uDuration ,
const uint32_t iDuration );
2017-10-17 11:30:44 +02:00
float duration( ) const noexcept
{ return uDuration * iDuration; }
2017-10-17 11:30:44 +02:00
2017-11-03 09:08:19 +01:00
void setTime( const float t ) noexcept
{ time = std::max( 0.f , std::min( t , duration( ) ) ); }
2017-10-07 16:56:20 +02:00
};
2017-10-17 11:30:44 +02:00
/*============================================================================*/
// Segment of an input's curve
2017-10-07 16:56:20 +02:00
struct T_SyncSegment
{
2017-10-17 11:30:44 +02:00
enum E_SegmentType
{
LINEAR ,
RAMP ,
SMOOTH ,
//HERMITE
};
E_SegmentType type;
T_Array< float > values{ T_Array< float >( 16 ) };
T_Array< uint32_t > durations{ T_Array< uint32_t >( 16 ) };
// Find the amount of time units before point
uint32_t findTimeOfPoint( uint32_t index ) const noexcept;
// Compute the value for the specified position, which is relative
// to the start of the segment.
float computeValue( float relTimeUnits ) const noexcept;
// Is the segment constant? If it is, return the value.
T_Optional< float > isConstant( ) const noexcept;
2017-10-07 16:56:20 +02:00
};
2017-11-23 09:31:23 +01:00
M_LSHIFT_OP( T_StringBuilder , T_SyncSegment::E_SegmentType );
2017-10-07 16:56:20 +02:00
/*----------------------------------------------------------------------------*/
// Possible results when trying to check whether two curves have a similar
// structure.
enum class E_SyncCurveMatch
{
// Both curves have the same structure.
IDENTICAL ,
// One of the curves has less information than the other, but the
// information in the shortest curve matches the corresponding
// information in the longest curve (i.e. it is only missing
// segments). _SHORT/_LONG indicate that the curve for which the
// comparison method was called is the shortest/longest (resp.)
MATCHING_SHORT ,
MATCHING_LONG ,
// One of the curves has less information than the other, and its
// last segment is shorter than the corresponding segment in the
// longest record (but its end matches a point in the longest curve's
// segment).
LASTSEG_SHORT ,
LASTSEG_LONG ,
// The curves do not match.
MISMATCH
};
2017-10-17 11:30:44 +02:00
// An input curve
struct T_SyncCurve
{
2017-11-03 09:08:19 +01:00
T_String name;
T_Array< T_SyncSegment > segments{ 16 };
2017-11-03 09:08:19 +01:00
T_SyncCurve( ) noexcept { }
T_SyncCurve( T_String const& name ) noexcept
: name( name )
{ }
T_SyncCurve( char const* name ) noexcept
: name( T_String( name ).usePool( ) )
{ }
// Compute the value at the specified time.
float computeValue( float timeUnits ) const noexcept;
// Compare this curve to another.
E_SyncCurveMatch matches(
T_SyncCurve const& other ) const noexcept;
// Count the points in the curve. A "point" corresponds to a value
// inside a segment, or the first and last value in the curve, or
// a junction between segments.
uint32_t points( ) const noexcept;
// Does the curve represent a constant? It does if all values in
// all segments are identical; in that case the value is returned.
T_Optional< float > isConstant( ) const noexcept;
2017-10-17 11:30:44 +02:00
};
/*----------------------------------------------------------------------------*/
// All configured curves. Some may not actually correspond to an input and may
// have been defined for inputs that have been removed temporarily (e.g.
// because some include was commented out), in which case we don't want to
// waste them.
struct T_SyncCurves
{
2017-11-03 09:08:19 +01:00
T_ObjectTable< T_String , T_SyncCurve > curves;
T_SyncCurves( )
: curves( []( T_SyncCurve const& c ) -> T_String { return c.name; } )
{ }
void clear( ) noexcept
{ curves.clear( ); }
void setCurve( T_SyncCurve curve ) noexcept
{ curves.set( std::move( curve ) ); }
bool removeCurve( T_String const& curve ) noexcept
{ return curves.remove( curve ); }
// Returns -1 on lookup failure
2017-11-03 09:08:19 +01:00
int32_t indexOf( T_String const& name ) noexcept;
int32_t indexOf( char const* name ) noexcept
{
return indexOf( T_String( name ) );
}
};
// Curves data file loader/writer
struct T_SyncCurvesIO : public ebcl::A_PrivateImplementation
{
T_SyncCurvesIO( ) noexcept;
struct T_Data
{
T_SyncCurves curves;
T_Optional< T_SyncTime > time;
ebcl::T_SRDLocation tLocation;
};
T_Data load( T_FSPath const& path ) const;
void save( T_FSPath const& path ,
T_SyncCurves const& curves ,
T_SyncTime const& time ) const;
};
/*----------------------------------------------------------------------------*/
2017-10-17 11:30:44 +02:00
// Pre-computed data for a curve
struct T_SyncCurveCache
{
using T_SegRef = std::pair< uint32_t , uint32_t >;
2017-10-07 16:56:20 +02:00
uint32_t curve;
2017-11-03 09:08:19 +01:00
T_Array< T_SegRef > segRefs;
T_Array< float > segStarts;
T_Array< float > segEnds;
2017-10-23 11:03:38 +02:00
uint32_t curPos;
2017-10-07 16:56:20 +02:00
2017-10-17 11:30:44 +02:00
T_SyncCurveCache(
2017-11-03 09:08:19 +01:00
T_SyncTime const& time ,
T_SyncCurves const& curves ,
const uint32_t curve ) noexcept;
// Find the index of the segment for the specified time
uint32_t findSegment(
2017-11-03 09:08:19 +01:00
const float time ) const noexcept;
2017-10-17 11:30:44 +02:00
// Compute the value of the curve at the specified location, ignoring
// curPos.
2017-10-17 11:30:44 +02:00
float valueAt(
2017-11-03 09:08:19 +01:00
T_SyncTime const& time ,
T_SyncCurves const& curves ,
const float position ) const noexcept;
// Compute the value of the curve at the current time, using and
// updating curPos as necessary.
float value(
2017-11-03 09:08:19 +01:00
T_SyncTime const& time ,
T_SyncCurves const& curves ) noexcept;
float segmentValue(
2017-11-03 09:08:19 +01:00
float time ,
uint32_t segIndex ,
T_Array< T_SyncSegment > const& segments ) const noexcept;
2017-10-17 11:30:44 +02:00
};
2017-11-03 09:08:19 +01:00
using P_SyncCurveCache = T_OwnPtr< T_SyncCurveCache >;
2017-10-17 11:30:44 +02:00
/*============================================================================*/
// Synchronization values. The values vector always contains an extra entry
// used for missing inputs.
struct T_SyncValues
{
2017-11-03 09:08:19 +01:00
T_HashIndex index;
T_Array< T_String > identifiers;
T_Array< float > values;
T_Array< bool > overriden;
T_SyncValues( );
void clear( );
// Returns true on success, false on duplicate
bool addValue(
2017-11-03 09:08:19 +01:00
T_String const& name ,
const float initial = 0.f ) noexcept;
bool addValue(
char const* name ,
const float initial = 0.f ) noexcept
{
return addValue( T_String( name ) , initial );
}
// If the name isn't found, the last entry of values[] is returned
uint32_t indexOf(
2017-11-03 09:08:19 +01:00
T_String const& name ) const noexcept;
};
/*============================================================================*/
2017-11-16 09:55:56 +01:00
class A_SyncOverride;
using P_SyncOverride = T_OwnPtr< A_SyncOverride >;
struct T_SyncOverrideSection;
using P_SyncOverrideSection = T_OwnPtr< T_SyncOverrideSection >;
// Base class for overrides
class A_SyncOverride
{
private:
const T_String type_;
bool enabled_{ false };
2017-11-17 20:33:56 +01:00
ebcl::T_SRDLocation location_;
2017-11-22 07:38:27 +01:00
T_String id_;
2017-11-16 09:55:56 +01:00
protected:
ebcl::T_Buffer< char > title_;
2017-11-16 09:55:56 +01:00
ebcl::T_Set< T_String > inputs_{
ebcl::UseTag< ebcl::ArrayBacked< 8 > >( ) };
T_AutoArray< uint32_t , 8 > inputPos_;
2017-11-30 11:25:20 +01:00
T_String fullTitle_;
2017-11-16 09:55:56 +01:00
A_SyncOverride( char const* type ,
T_String const& title ) noexcept;
2017-11-16 09:55:56 +01:00
public:
A_SyncOverride( ) = delete;
2017-11-16 09:55:56 +01:00
virtual ~A_SyncOverride( ) = 0;
T_String const& type( ) const noexcept
{ return type_; }
auto const& inputNames( ) const noexcept
2017-11-16 09:55:56 +01:00
{ return inputs_; }
auto const& inputPositions( ) const noexcept
{ return inputPos_; }
2017-11-16 09:55:56 +01:00
bool& enabled( ) noexcept
{ return enabled_; }
2017-11-16 09:55:56 +01:00
bool enabled( ) const noexcept
{ return enabled_; }
2017-11-22 07:38:27 +01:00
T_String const& id( ) const noexcept
{ return id_; }
char const* title( ) const noexcept
{ return &title_[ 0 ]; }
2017-11-17 20:33:56 +01:00
ebcl::T_SRDLocation& location( ) noexcept
{ return location_; }
ebcl::T_SRDLocation const& location( ) const noexcept
{ return location_; }
2017-11-30 11:25:20 +01:00
T_String const& fullTitle( ) const noexcept
{ return fullTitle_; }
void fullTitle( T_String ft ) noexcept
{ assert( !fullTitle_ ); fullTitle_ = std::move( ft ); }
2017-11-16 09:55:56 +01:00
// Connect the required inputs to the sync manager. Called once
// the inputs have been added.
virtual void setup( ) noexcept;
// Create a clone of the current override.
virtual T_OwnPtr< A_SyncOverride > clone( ) const noexcept = 0;
2017-11-16 09:55:56 +01:00
};
// Overrides section
struct T_SyncOverrideSection
{
const T_String title;
const ebcl::T_Buffer< char > cTitle;
2017-11-16 09:55:56 +01:00
bool open{ false };
T_Array< P_SyncOverrideSection > subsections;
T_Array< P_SyncOverride > overrides;
T_SyncOverrideSection( ) = delete;
NO_COPY( T_SyncOverrideSection );
NO_MOVE( T_SyncOverrideSection );
2017-11-16 09:55:56 +01:00
explicit T_SyncOverrideSection( T_String title ) noexcept;
void merge( T_SyncOverrideSection& other ) noexcept;
// Generate the UI. A counter and temporary string builder are passed
// to help generating "fake" labels for use with ImGui.
void makeUI( uint32_t& counter ,
T_StringBuilder& sb ,
bool topLevel = false ) noexcept;
2017-11-16 09:55:56 +01:00
T_SyncOverrideSection& section(
T_String const& name ) noexcept;
T_SyncOverrideSection const* section(
T_String const& name ) const noexcept;
};
/*----------------------------------------------------------------------------*/
struct T_SyncOverrideVisitor
{
public:
using T_Element = ebcl::T_Union<
T_SyncOverrideSection* ,
A_SyncOverride* >;
using T_OpElement = T_Optional< T_Element >;
2017-11-22 07:38:27 +01:00
using T_Visitor = ebcl::T_Visitor< T_Element , T_Element >;
using F_NodeAction = T_Visitor::F_NodeAction;
static T_OpElement nodeBrowser( T_Element element , uint32_t child );
2017-11-22 07:38:27 +01:00
T_Visitor visitor{ nodeBrowser };
};
2017-11-16 09:55:56 +01:00
/*============================================================================*/
// Synchronisation manager; handles all the synchronization data and makes it
// work together.
struct T_SyncManager : public virtual A_ProjectPathListener
{
T_SyncManager( ) noexcept;
~T_SyncManager( );
void enable( ) noexcept;
void disable( ) noexcept
{ enabled_ = false; }
bool enabled( ) const noexcept
{ return enabled_; }
// ---------------------------------------------------------------------
// Duration & time controls
void setDuration(
2017-11-03 09:08:19 +01:00
const float uDuration ,
const uint32_t iDuration );
float duration( ) const noexcept
{ return time_.duration( ); }
uint32_t durationUnits( ) const noexcept
{ return time_.iDuration; }
float durationUnitSize( ) const noexcept
{ return time_.uDuration; }
2017-11-03 09:08:19 +01:00
void setTime( const float time );
void timeDelta( const float delta )
{ setTime( time_.time + delta ); }
float time( ) const noexcept
{ return time_.time; }
bool playing( ) const noexcept
{ return playing_; }
bool& playing( ) noexcept
{ return playing_; }
bool finished( ) const noexcept
{ return time_.time >= time_.duration( ); }
void updateTime( ) noexcept;
2017-10-30 18:29:52 +01:00
// ---------------------------------------------------------------------
// Value access
void clearInputs( ) noexcept;
bool addInput( T_String const& name ,
2017-11-03 09:08:19 +01:00
const float initial = 0.f ) noexcept
{ return values_.addValue( name , initial ); }
bool hasInput( T_String const& name ) const noexcept
{ return values_.identifiers.contains( name ); }
2017-10-30 19:04:10 +01:00
2017-11-21 22:23:34 +01:00
T_Array< T_String > const& inputNames( ) const noexcept
{ return values_.identifiers; }
2017-11-03 09:08:19 +01:00
uint32_t inputPos( T_String const& name ) const noexcept
2017-10-30 18:29:52 +01:00
{ return values_.indexOf( name ); }
2017-11-03 09:08:19 +01:00
T_Array< float > const& inputs( ) const noexcept
2017-10-30 18:29:52 +01:00
{ return values_.values; }
T_Array< float >& inputs( ) noexcept
{ return values_.values; }
2017-10-30 18:29:52 +01:00
// ---------------------------------------------------------------------
// Curves
void clearCurves( );
2017-11-03 09:08:19 +01:00
void setCurve( T_SyncCurve curve );
void removeCurve( T_String const& curve ) noexcept;
T_SyncCurve const* getCurve(
T_String const& name ) const noexcept;
2017-11-22 14:14:49 +01:00
T_Array< T_SyncCurve > const& curves( ) const noexcept
{ return curves_.curves.values( ); }
bool curvesModified( ) const noexcept
{ return modified_; }
bool curvesFileChanged( ) const noexcept
{ return fileChanged_; }
2017-11-22 15:43:06 +01:00
bool loadCurves( bool undoable = true );
2017-11-23 09:31:23 +01:00
bool saveCurves( );
2017-10-31 14:21:42 +01:00
private:
void curvesChanged_( );
void addReloadUndoData_(
T_SyncCurvesIO::T_Data const& data ) const noexcept;
2017-10-31 14:21:42 +01:00
// ---------------------------------------------------------------------
2017-11-16 09:55:56 +01:00
// Overrides
2017-10-31 14:21:42 +01:00
public:
void clearOverrides( ) noexcept;
void mergeOverrides( T_SyncOverrideSection& overrides );
2017-11-16 09:55:56 +01:00
bool overrideExists( T_String const& id ) const noexcept;
A_SyncOverride* getOverride( T_String const& id ) const noexcept;
// Mark a bunch of inputs as overridden / not overridden
void setOverridesActive( bool active ,
uint32_t n ,
uint32_t const* pos );
2017-11-22 07:38:27 +01:00
// Visit the overrides tree
void visitOverrides( T_SyncOverrideVisitor::F_NodeAction visitor );
2017-11-16 09:55:56 +01:00
// ---------------------------------------------------------------------
// Update
void updateCurveCaches( );
void updateValues( );
// ---------------------------------------------------------------------
// Private data
private:
bool enabled_{ false }; // Interactive mode enabled?
T_SyncCurvesIO io_; // Curves loader/writer
T_FSPath curvesFile_; // Path to the curves file
T_WatchedFiles watcher_; // Curves file watcher
2017-11-23 09:31:23 +01:00
bool saving_{ false }; // True if file is being saved
bool modified_; // Locally modified
bool fileChanged_; // File modified
2017-11-22 07:38:27 +01:00
2017-11-16 09:55:56 +01:00
T_SyncTime time_; // Duration/time information
bool playing_{ false }; // Is it playing?
2017-11-22 07:38:27 +01:00
2017-11-16 09:55:56 +01:00
T_SyncValues values_; // Value storage
T_SyncCurves curves_; // Curves storage
T_Array< P_SyncCurveCache > curveCaches_; // Cache for curve segments
2017-11-22 07:38:27 +01:00
T_SyncOverrideSection soRoot_; // Root for overrides
2017-11-22 07:38:27 +01:00
T_SyncOverrideVisitor soVisitor_;
T_KeyValueTable< T_String , A_SyncOverride* > soTable_;
// Table of sync overrides, by ID
protected:
void projectPathChanged( ) noexcept;
};