Sequencer - Progress on track display

This commit is contained in:
Emmanuel BENOîT 2017-11-25 22:02:17 +01:00
parent 2cfc80a6e4
commit c4d5bd3fd1

View file

@ -16,7 +16,7 @@ using namespace ebcl;
namespace {
/*= VARIOUS IMGUI HELPERS ====================================================*/
/*= VARIOUS HELPERS ==========================================================*/
bool FakeTab_(
char const* const name ,
@ -34,6 +34,18 @@ bool FakeTab_(
return rv;
}
void TimeToString_(
char* const buffer ,
const size_t bSize ,
const float time ) noexcept
{
const float msf{ fmod( time , 1.f ) };
const float sf{ fmod( time - msf , 60.f ) };
snprintf( buffer , bSize , "%02d:%02d.%03d" ,
uint32_t( ( time - msf - sf ) / 60.f ) ,
uint32_t( sf ) , uint32_t( msf * 1000.f ) );
}
/*= T_ChangeDurationDialog_ ==================================================*/
@ -139,10 +151,62 @@ struct T_SyncViewImpl_
static constexpr float BarWidth = 40.f;
static constexpr float TrackHeight = 15.f;
static constexpr float TrackPadding = 2.f;
static constexpr float PointRadius = ( TrackHeight - 2.f ) * .5f;
static constexpr float PointRadiusSqr = PointRadius * PointRadius;
bool display( ) noexcept;
private:
// Track display data
struct T_TrackDisplay
{
T_String id;
bool isOverride;
ImRect area;
uint32_t dispSegs;
uint32_t firstSeg;
};
struct T_TrackSegDisplay
{
uint32_t track;
uint32_t seg;
ImRect area;
uint32_t dispPoints;
uint32_t firstPoint;
};
struct T_TrackPointDisplay
{
uint32_t seg;
uint32_t index;
ImVec2 center;
enum {
START ,
MIDDLE ,
END
} mode;
};
// Description for mouse locations in the sequencer window
enum class E_MousePosType
{
NONE ,
TRACK ,
SEGMENT ,
POINT
};
struct T_MousePos
{
E_MousePosType type;
uint32_t index;
};
// Type of sub-windows
enum E_SubWindow {
SW_NONE ,
SW_CURVE_SELECTOR ,
SW_OVERRIDE_SELECTOR ,
};
// Make sure all displayed curves/inputs/overrides still exist
void checkSelectedCurves( ) noexcept;
void displayToolbar( ) noexcept;
@ -150,18 +214,23 @@ struct T_SyncViewImpl_
void computeMetrics( float innerWidth ) noexcept;
void sequencerWidget( ) noexcept;
void sequencerHeader( ImRect const& bb ) noexcept;
bool sequencerBody( ImRect const& bb ) noexcept;
bool sequencerTracks(
void sequencerBody( ImRect const& bb ) noexcept;
void sequencerTracks(
float& hue ,
ImRect& bb ,
ImRect const& container ) noexcept;
bool sequencerTrack(
void sequencerTrack(
float& hue ,
ImRect const& bb ,
ImRect const& container ,
T_String const& name ,
T_SyncCurve const* curve ) noexcept;
void displayTooltips(
const float time ) noexcept;
T_MousePos getMousePos( ) const noexcept;
void displayCurveSelectorWindow( ) noexcept;
void displayCurveSelector( ) noexcept;
void displayOverrideSelector( ) noexcept;
@ -191,19 +260,21 @@ struct T_SyncViewImpl_
float totalPixels;
float startPixel;
// Track display
T_Array< T_TrackDisplay > dspTracks;
T_Array< T_TrackSegDisplay > dspSegments;
T_Array< T_TrackPointDisplay > dspPoints;
// Zoom area selection
bool zoomInProgress{ false };
bool justZoomed{ false };
float firstZoomPixel;
float curZoomPixel;
// Curve display / edition
enum E_SubWindow {
SW_NONE ,
SW_CURVE_SELECTOR ,
SW_OVERRIDE_SELECTOR ,
};
// Sub-windows
E_SubWindow sub{ SW_NONE };
// Curve selection
T_KeyValueTable< T_String , bool > sCurves;
T_Set< T_String > sOverrides;
T_String curveFinder;
@ -387,17 +458,13 @@ void T_SyncViewImpl_::sequencerWidget( ) noexcept
sequencerHeader( bbHeader );
PopID( );
}
bool mouseConsumed{ false };
if ( bbDisplay.Min.y < bbDisplay.Max.y && ItemAdd( bbDisplay , dspId ) ) {
PushID( dspId );
mouseConsumed = sequencerBody( bbDisplay );
sequencerBody( bbDisplay );
PopID( );
}
PopID( );
if ( mouseConsumed ) {
EndGroup( );
return;
}
EndGroup( );
auto& io( GetIO( ) );
if ( hovered && ( io.MouseDown[ 0 ] || io.MouseDown[ 1 ] ) ) {
@ -406,46 +473,56 @@ void T_SyncViewImpl_::sequencerWidget( ) noexcept
}
const bool active( GetCurrentContext( )->ActiveId == seqId );
if ( hovered && !active && io.MouseWheel != 0 ) {
zoomLevel = ImSaturate( zoomLevel + .025 * io.MouseWheel );
} else if ( active ) {
if ( io.MouseDown[ 0 ] ) {
const float p{ io.MousePos.x - bbAll.Min.x + startPixel };
auto& sync( Common::Sync( ) );
sync.setTime( p * Common::Sync( ).duration( ) / totalPixels );
auto& sync( Common::Sync( ) );
const float mPixels{ io.MousePos.x - bbAll.Min.x + startPixel };
const float mTime{ mPixels * sync.duration( ) / totalPixels };
if ( !active ) {
if ( !hovered ) {
return;
}
if ( io.MouseDown[ 1 ] ) {
const float p{ io.MousePos.x - bbAll.Min.x + startPixel };
if ( !zoomInProgress ) {
firstZoomPixel = p;
zoomInProgress = true;
}
curZoomPixel = p;
} else if ( zoomInProgress ) {
zoomInProgress = false;
justZoomed = true;
const auto zMin{ std::min( firstZoomPixel , curZoomPixel ) } ,
zMax{ std::max( firstZoomPixel , curZoomPixel ) } ,
diff{ zMax - zMin };
if ( diff > 4 ) {
auto& sync( Common::Sync( ) );
const float u( sync.durationUnits( ) );
startPos = zMin * u / totalPixels;
if ( ( width - 2.f ) / u >= BarWidth ) {
zoomLevel = 0;
} else {
const auto length{ std::min( u , diff * u / totalPixels ) };
const auto ppu{ std::min( ( width - 2 ) / length , BarWidth ) };
zoomLevel = ( ppu - BarWidth ) / ( BarWidth - width / u ) + 1;
}
}
if ( io.MouseWheel != 0 ) {
zoomLevel = ImSaturate( zoomLevel + .025 * io.MouseWheel );
}
if ( !( io.MouseDown[ 0 ] || io.MouseDown[ 1 ] ) ) {
ClearActiveID( );
if ( bbDisplay.Contains( io.MousePos ) ) {
displayTooltips( mTime );
}
return;
}
if ( io.MouseDown[ 0 ] ) {
sync.setTime( mTime );
}
if ( io.MouseDown[ 1 ] ) {
if ( !zoomInProgress ) {
firstZoomPixel = mPixels;
zoomInProgress = true;
}
curZoomPixel = mPixels;
} else if ( zoomInProgress ) {
zoomInProgress = false;
justZoomed = true;
const auto zMin{ std::min( firstZoomPixel , curZoomPixel ) } ,
zMax{ std::max( firstZoomPixel , curZoomPixel ) } ,
diff{ zMax - zMin };
if ( diff > 4 ) {
const float u( sync.durationUnits( ) );
startPos = zMin * u / totalPixels;
if ( ( width - 2.f ) / u >= BarWidth ) {
zoomLevel = 0;
} else {
const auto length{ std::min( u , diff * u / totalPixels ) };
const auto ppu{ std::min( ( width - 2 ) / length , BarWidth ) };
zoomLevel = ( ppu - BarWidth ) / ( BarWidth - width / u ) + 1;
}
}
}
EndGroup( );
if ( !( io.MouseDown[ 0 ] || io.MouseDown[ 1 ] ) ) {
ClearActiveID( );
}
}
void T_SyncViewImpl_::computeMetrics(
@ -526,11 +603,7 @@ void T_SyncViewImpl_::sequencerHeader(
const ImVec2 taEnd{ taStart + ImVec2{ barWidth , inner.Max.y - inner.Min.y } };
const float time{ bar * timePerBar };
const float msf{ fmod( time , 1.f ) };
const float sf{ fmod( time - msf , 60.f ) };
snprintf( buffer , sizeof( buffer ) , "%02d:%02d.%03d" ,
uint32_t( ( time - msf - sf ) / 60.f ) ,
uint32_t( sf ) , uint32_t( msf * 1000.f ) );
TimeToString_( buffer , sizeof( buffer ) , time );
RenderTextClipped( taStart , taEnd , buffer , nullptr , nullptr ,
ImVec2{ .5f , .05f + ( ( bar % 2 ) ? .9f : 0.f ) } , &inner );
pos += barWidth;
@ -540,7 +613,7 @@ void T_SyncViewImpl_::sequencerHeader(
PopFont( );
}
bool T_SyncViewImpl_::sequencerBody(
void T_SyncViewImpl_::sequencerBody(
ImRect const& bb ) noexcept
{
using namespace ImGui;
@ -574,7 +647,6 @@ bool T_SyncViewImpl_::sequencerBody(
}
// Display the curve / override controls
bool mouseConsumed{ false };
if ( sCurves.size( ) != 0 ) {
ImRect subBb{ inner };
@ -585,7 +657,7 @@ bool T_SyncViewImpl_::sequencerBody(
float hue{ 0.12f };
// TODO: display overrides
mouseConsumed = sequencerTracks( hue , subBb , inner ) || mouseConsumed;
sequencerTracks( hue , subBb , inner );
}
if ( cursorPos >= 0 && cursorPos <= inner.GetWidth( ) ) {
@ -594,18 +666,14 @@ bool T_SyncViewImpl_::sequencerBody(
ImVec2{ inner.Min.x + cursorPos , inner.Max.y - 1 } ,
0xffffffff );
}
return mouseConsumed;
}
bool T_SyncViewImpl_::sequencerTracks(
void T_SyncViewImpl_::sequencerTracks(
float& hue ,
ImRect& subBb ,
ImRect const& container ) noexcept
{
auto& sync{ Common::Sync( ) };
const auto nc{ sCurves.size( ) };
bool mouseConsumed{ false };
for ( auto i = 0u ; i < nc ; i ++ ) {
if ( sCurves.values( )[ i ] ) {
continue;
@ -613,31 +681,38 @@ bool T_SyncViewImpl_::sequencerTracks(
auto const& name{ sCurves.keys( )[ i ] };
auto* const curve{ sync.getCurve( name ) };
mouseConsumed = sequencerTrack( hue , subBb , container , name , curve )
|| mouseConsumed;
sequencerTrack( hue , subBb , container , name , curve );
subBb.Min.y += TrackHeight + 2 * TrackPadding;
subBb.Max.y += TrackHeight + 2 * TrackPadding;
hue = fmodf( hue + .17f , 1.f );
}
return mouseConsumed;
}
bool T_SyncViewImpl_::sequencerTrack(
void T_SyncViewImpl_::sequencerTrack(
float& hue ,
ImRect const& bb ,
ImRect const& container ,
T_String const& name ,
T_String const& id ,
T_SyncCurve const* curve ) noexcept
{
using namespace ImGui;
// Don't display if the track is fully hidden
if ( !container.Overlaps( bb ) ) {
return false;
return;
}
auto* const dl{ GetWindowDrawList( ) };
// Add track display record
const auto dTrackIdx{ dspTracks.size( ) };
auto& dTrack{ dspTracks.addNew( ) };
dTrack.id = id;
dTrack.isOverride = false;
dTrack.dispSegs = 0;
dTrack.firstSeg = dspSegments.size( );
dTrack.area = bb;
// Compute colors
using namespace ImGui;
const auto bgColor{ ColorHSVAToU32( hue , .25f , 1.f , .25f ) } ,
borderColor{ ColorHSVAToU32( hue , .12f , 1.f , 1.f ) };
borderColor{ ColorHSVAToU32( hue , .88f , 1.f , 1.f ) };
const uint32_t segColors[ 2 ] = {
ColorHSVAToU32( hue - .03f , .4f , 1.f , 1.f ) ,
ColorHSVAToU32( hue + .03f , .4f , 1.f , 1.f ) ,
@ -650,6 +725,7 @@ bool T_SyncViewImpl_::sequencerTrack(
ImVec2{ ImFloor( bb.Max.x ) - 1 ,
ImFloor( ImMin( container.Max.y , bb.Max.y ) ) }
};
auto* const dl{ GetWindowDrawList( ) };
dl->AddRectFilled( bar.Min , bar.Max , bgColor );
if ( container.Contains( bb.GetTL( ) ) ) {
dl->AddLine( bar.GetTL( ) , bar.GetTR( ) , borderColor );
@ -667,13 +743,6 @@ bool T_SyncViewImpl_::sequencerTrack(
}
dl->PushClipRect( bar.Min , bar.Max );
#if 0
// We'll need to display the right pop-up
auto& io( GetIO( ) );
const auto mPos{ io.MousePos };
bool mousePosUsed{ false } , mouseConsumed{ false };
#endif
// If there's a curve, go through all segments
const auto units{ Common::Sync( ).durationUnits( ) };
const auto nSeg{ curve ? curve->segments.size( ) : 0u };
@ -699,6 +768,17 @@ bool T_SyncViewImpl_::sequencerTrack(
continue;
}
// Add segment to displayed list
auto dSegIdx{ dspSegments.size( ) };
auto& dSeg{ dspSegments.addNew( ) };
dSeg.area = segFull;
dSeg.track = dTrackIdx;
dSeg.seg = i;
dSeg.dispPoints = 0;
dSeg.firstPoint = dspPoints.size( );
dTrack.dispSegs ++;
// Draw segment
const ImRect segBar{
ImVec2{ ImMax( bar.Min.x , segFull.Min.x ) , segFull.Min.y } ,
ImVec2{ ImMin( bar.Max.x , segFull.Max.x ) , segFull.Max.y }
@ -706,73 +786,147 @@ bool T_SyncViewImpl_::sequencerTrack(
dl->AddRectFilled( segBar.Min , segBar.Max , color );
dl->PushClipRect( segBar.Min , segBar.Max );
// Handle points
const auto nd{ seg.durations.size( ) };
const auto radius{ ( TrackHeight - 2.f ) * .5f };
const auto ym{ bb.Min.y + radius + 1 };
const auto ym{ bb.Min.y + PointRadius + 1 };
auto cDur{ 0 };
for ( auto j = 0u ; j < nd + 1 ; j ++ ) {
const ImVec2 ctr{
std::roundf( xStart + cDur * totalPixels / units ) ,
ym
};
dl->AddCircleFilled( ctr , radius , 0x7f000000 ); // XXX color
#if 0
if ( ImLengthSqr( mPos - ctr ) <= radius * radius ) {
if ( ! mousePosUsed ) {
BeginTooltip( );
stringBuffer.clear( ) << name << " (input)\nIn "
<< seg.type << " segment\nAt " << segDur
<< " units\nValue: " << seg.values[ j ];
Text( "%s" , stringBuffer.data( ) );
EndTooltip( );
mousePosUsed = true;
}
if ( !mouseConsumed && io.MouseDown[ 0 ] ) {
printf( "fuck yeah!\n" );
mouseConsumed = true;
}
}
#endif
dl->AddCircleFilled( ctr , PointRadius , 0x7f000000 ); // XXX color
cDur += j < nd ? seg.durations[ j ] : 0;
// Add point record
auto& dPoint{ dspPoints.addNew( ) };
dPoint.center = ctr;
dPoint.index = j;
dPoint.mode =
j == 0 ? T_TrackPointDisplay::START : (
j == nd ? T_TrackPointDisplay::END :
T_TrackPointDisplay::MIDDLE );
dPoint.seg = dSegIdx;
dSeg.dispPoints ++;
}
dl->PopClipRect( );
#if 0
if ( segBar.Contains( mPos ) && !mousePosUsed ) {
BeginTooltip( );
stringBuffer.clear( ) << name << " (input)\nIn "
<< seg.type << " segment\nDuration: "
<< segDur << " units (from " << ( segStart - segDur )
<< " to " << ( segStart - 1 )
<< ")\n";
Text( "%s" , stringBuffer.data( ) );
EndTooltip( );
mousePosUsed = true;
}
#endif
}
#if 0
if ( bar.Contains( mPos ) && !mousePosUsed ) {
const float time{ ( io.MousePos.x - container.Min.x + startPixel )
* units / totalPixels };
const float msf{ fmod( time , 1.f ) };
const float sf{ fmod( time - msf , 60.f ) };
char buffer[ 12 ];
snprintf( buffer , sizeof( buffer ) , "%02d:%02d.%03d" ,
uint32_t( ( time - msf - sf ) / 60.f ) ,
uint32_t( sf ) , uint32_t( msf * 1000.f ) );
BeginTooltip( );
stringBuffer.clear( ) << name << " (input)\nNot defined at "
<< buffer << '\0';
Text( "%s" , stringBuffer.data( ) );
EndTooltip( );
mousePosUsed = true;
}
#endif
dl->PopClipRect( );
}
return false;
/*----------------------------------------------------------------------------*/
void T_SyncViewImpl_::displayTooltips(
const float time ) noexcept
{
auto mp{ getMousePos( ) };
if ( mp.type == E_MousePosType::NONE ) {
return;
}
// Track / segment / point from mouse info
auto const& track( [&](){
if ( mp.type == E_MousePosType::POINT ) {
return dspTracks[ dspSegments[
dspPoints[ mp.index ].seg ].track ];
}
if ( mp.type == E_MousePosType::SEGMENT ) {
return dspTracks[ dspSegments[ mp.index ].track ];
}
return dspTracks[ mp.index ];
}() );
auto const* const seg( [&]() -> T_TrackSegDisplay const* {
if ( mp.type == E_MousePosType::TRACK ) {
return nullptr;
}
if ( mp.type == E_MousePosType::SEGMENT ) {
return &dspSegments[ mp.index ];
}
return &dspSegments[ dspPoints[ mp.index ].seg ];
}() );
auto const* const point( [&]() -> T_TrackPointDisplay const* {
if ( mp.type != E_MousePosType::POINT ) {
return nullptr;
}
return &dspPoints[ mp.index ];
}() );
// Curve from track
T_SyncCurve const* const curve{ Common::Sync( ).getCurve( track.id ) };
assert( mp.type == E_MousePosType::TRACK || curve != nullptr );
// Time offset
const float dTime( [&](){
if ( point ) {
auto tDur{ .0f };
printf( "segseg %d\n" , seg->seg );
for ( auto i = 0u ; i <= seg->seg ; i ++ ) {
auto const& s{ curve->segments[ i ] };
auto const nd{ i == seg->seg
? point->index
: s.durations.size( ) };
printf( "s %d -> nd %d\n" , i , nd );
for ( auto j = 0u ; j < nd ; j ++ ) {
tDur += s.durations[ j ];
}
}
return tDur * Common::Sync( ).durationUnitSize( );
}
return time;
}() );
char buffer[ 12 ];
TimeToString_( buffer , sizeof( buffer ) , dTime );
const float value{ point ? curve->segments[ seg->seg ].values[ point->index ] : -.666f }; // FIXME
stringBuffer.clear( ) << track.id << " (input)\n";
if ( mp.type == E_MousePosType::TRACK ) {
stringBuffer << "No segment";
} else {
auto const& s{ curve->segments[ seg->seg ] };
if ( mp.type == E_MousePosType::SEGMENT ) {
stringBuffer << "Segment type: " << s.type;
} else {
stringBuffer << "On " << s.type << " segment, index " << point->index;
}
}
stringBuffer << "\nTime: " << buffer << "\nValue: " << value;
using namespace ImGui;
stringBuffer << '\0';
BeginTooltip( );
Text( "%s" , stringBuffer.data( ) );
EndTooltip( );
}
/*----------------------------------------------------------------------------*/
T_SyncViewImpl_::T_MousePos T_SyncViewImpl_::getMousePos( ) const noexcept
{
auto const& mp{ ImGui::GetIO( ).MousePos };
for ( auto i = 0u ; i < dspTracks.size( ) ; i ++ ) {
auto const& track{ dspTracks[ i ] };
if ( !track.area.Contains( mp ) ) {
continue;
}
for ( auto j = 0u ; j < track.dispSegs ; j ++ ) {
auto const& seg{ dspSegments[ track.firstSeg + j ] };
if ( !seg.area.Contains( mp ) ) {
continue;
}
for ( auto k = 0u ; k < seg.dispPoints ; k ++ ) {
auto const& p{ dspPoints[ seg.firstPoint + k ] };
if ( ImLengthSqr( mp - p.center ) <= PointRadiusSqr ) {
return T_MousePos{ E_MousePosType::POINT ,
seg.firstPoint + k };
}
}
return T_MousePos{ E_MousePosType::SEGMENT , i };
}
return T_MousePos{ E_MousePosType::TRACK , i };
}
return T_MousePos{ E_MousePosType::NONE , 0 };
}
/*----------------------------------------------------------------------------*/