mirror of
https://github.com/Ed94/Cog.git
synced 2026-06-13 00:01:37 -07:00
CogEngine: Improve Plot window
- Save plotted entries between sessions - Add more options (autofit padding, num recorded values, record values when paused - Fix dragging the mouse on a different window over the graph pausing the graph
This commit is contained in:
@@ -1,326 +1,24 @@
|
||||
#include "CogDebugPlot.h"
|
||||
|
||||
#include "CogDebug.h"
|
||||
#include "CogDebugHelper.h"
|
||||
#include "CogImguiHelper.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Engine/World.h"
|
||||
|
||||
FCogDebugPlotEvent FCogDebugPlot::DefaultEvent;
|
||||
TArray<FCogDebugPlotEntry> FCogDebugPlot::Plots;
|
||||
TArray<FCogDebugPlotEntry> FCogDebugPlot::Events;
|
||||
TMap<FName, FCogDebugValueHistory> FCogDebugPlot::Values;
|
||||
TMap<FName, FCogDebugEventHistory> FCogDebugPlot::Events;
|
||||
int32 FCogDebugPlot::NumRecordedValues = 2000;
|
||||
bool FCogDebugPlot::IsVisible = false;
|
||||
bool FCogDebugPlot::Pause = false;
|
||||
bool FCogDebugPlot::RecordValuesWhenPause = true;
|
||||
FName FCogDebugPlot::LastAddedEventPlotName = NAME_None;
|
||||
int32 FCogDebugPlot::LastAddedEventIndex = INDEX_NONE;
|
||||
TMap<int32, TMap<int32, int32>> FCogDebugPlot::OccupationMap;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
// FCogPlotEvent
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
float FCogDebugPlotEvent::GetActualEndTime(const FCogDebugPlotEntry& Plot) const
|
||||
{
|
||||
const UWorld* World = Plot.World.Get();
|
||||
const float WorldTime = World != nullptr ? World->GetTimeSeconds() : 0.0f;
|
||||
const float ActualEndTime = EndTime != 0.0f ? EndTime : WorldTime;
|
||||
return ActualEndTime;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
uint64 FCogDebugPlotEvent::GetActualEndFrame(const FCogDebugPlotEntry& Plot) const
|
||||
{
|
||||
const float ActualEndFame = EndFrame != 0.0f ? EndFrame : GFrameCounter;
|
||||
return ActualEndFame;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlotEvent::AddParam(const FName Name, bool Value)
|
||||
{
|
||||
if (FCogDebugPlot::IsVisible)
|
||||
{
|
||||
AddParam(Name, FString::Printf(TEXT("%s"), Value ? TEXT("True") : TEXT("False")));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlotEvent::AddParam(const FName Name, int Value)
|
||||
{
|
||||
if (FCogDebugPlot::IsVisible)
|
||||
{
|
||||
AddParam(Name, FString::Printf(TEXT("%d"), Value));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlotEvent::AddParam(const FName Name, float Value)
|
||||
{
|
||||
if (FCogDebugPlot::IsVisible)
|
||||
{
|
||||
AddParam(Name, FString::Printf(TEXT("%0.2f"), Value));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlotEvent::AddParam(const FName Name, FName Value)
|
||||
{
|
||||
if (FCogDebugPlot::IsVisible)
|
||||
{
|
||||
AddParam(Name, Value.ToString());
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlotEvent::AddParam(const FName Name, const FString& Value)
|
||||
{
|
||||
if (FCogDebugPlot::IsVisible)
|
||||
{
|
||||
|
||||
if (Name == "Name")
|
||||
{
|
||||
DisplayName = Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
FCogDebugPlotEventParams& Param = Params.AddDefaulted_GetRef();
|
||||
Param.Name = Name;
|
||||
Param.Value = Value;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
// FCogPlotEntry
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogDebugPlotEntry::AddPoint(float X, float Y)
|
||||
{
|
||||
if (Values.Capacity == 0)
|
||||
{
|
||||
Values.reserve(2000);
|
||||
}
|
||||
|
||||
if (Values.size() < Values.Capacity)
|
||||
{
|
||||
Values.push_back(ImVec2(X, Y));
|
||||
}
|
||||
else
|
||||
{
|
||||
Values[ValueOffset] = ImVec2(X, Y);
|
||||
ValueOffset = (ValueOffset + 1) % Values.size();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlotEntry::AddEvent(
|
||||
const FString& OwnerName,
|
||||
const bool IsInstant,
|
||||
const FName EventId,
|
||||
const int32 Row,
|
||||
const FColor& Color)
|
||||
{
|
||||
if (Events.Max() < 200)
|
||||
{
|
||||
Events.Reserve(200);
|
||||
}
|
||||
|
||||
//----------------------------
|
||||
// Stop if it already exist.
|
||||
//----------------------------
|
||||
StopEvent(EventId);
|
||||
|
||||
FCogDebugPlotEvent* Event = nullptr;
|
||||
|
||||
int32 AddedIndex = 0;
|
||||
if (Events.Num() < Events.Max())
|
||||
{
|
||||
Event = &Events.AddDefaulted_GetRef();
|
||||
AddedIndex = Events.Num() - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Event = &Events[EventOffset];
|
||||
AddedIndex = EventOffset;
|
||||
EventOffset = (EventOffset + 1) % Events.Num();
|
||||
}
|
||||
|
||||
Event->Id = EventId;
|
||||
Event->OwnerName = OwnerName;
|
||||
Event->DisplayName = EventId.ToString();
|
||||
Event->StartTime = Time;
|
||||
Event->EndTime = IsInstant ? Time : 0.0f;
|
||||
Event->StartFrame = Frame;
|
||||
Event->EndFrame = IsInstant ? Frame : 0.0f;
|
||||
Event->Row = (Row == FCogDebugPlot::AutoRow) ? FCogDebugPlot::FindFreeGraphRow(GraphIndex) : Row;
|
||||
|
||||
if (IsInstant == false)
|
||||
{
|
||||
FCogDebugPlot::OccupyGraphRow(GraphIndex, Event->Row);
|
||||
}
|
||||
|
||||
MaxRow = FMath::Max(Event->Row, MaxRow);
|
||||
|
||||
const FColor BorderColor = FCogDebugHelper::GetAutoColor(EventId, Color).WithAlpha(200);
|
||||
const FColor FillColor = BorderColor.WithAlpha(100);
|
||||
Event->BorderColor = FCogImguiHelper::ToImColor(BorderColor);
|
||||
Event->FillColor = FCogImguiHelper::ToImColor(FillColor);
|
||||
|
||||
FCogDebugPlot::LastAddedEventPlotName = Name;
|
||||
FCogDebugPlot::LastAddedEventIndex = AddedIndex;
|
||||
|
||||
return *Event;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlotEntry::StopEvent(const FName EventId)
|
||||
{
|
||||
FCogDebugPlotEvent* Event = FindLastEventByName(EventId);
|
||||
if (Event == nullptr)
|
||||
{
|
||||
return FCogDebugPlot::DefaultEvent;
|
||||
}
|
||||
|
||||
if (Event->EndTime == 0.0f)
|
||||
{
|
||||
Event->EndTime = Time;
|
||||
Event->EndFrame = Frame;
|
||||
|
||||
FCogDebugPlot::FreeGraphRow(GraphIndex, Event->Row);
|
||||
}
|
||||
|
||||
return *Event;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent* FCogDebugPlotEntry::GetLastEvent()
|
||||
{
|
||||
if (Events.Num() == 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int32 Index = Events.Num() - 1;
|
||||
if (EventOffset != 0)
|
||||
{
|
||||
Index = (Index + EventOffset) % Events.Num();
|
||||
}
|
||||
|
||||
return &Events[Index];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent* FCogDebugPlotEntry::FindLastEventByName(FName EventId)
|
||||
{
|
||||
for (int32 i = Events.Num() - 1; i >= 0; --i)
|
||||
{
|
||||
//--------------------------------------------------
|
||||
// The array cycle so we must offset the index
|
||||
//--------------------------------------------------
|
||||
int32 Index = i;
|
||||
if (EventOffset != 0)
|
||||
{
|
||||
Index = (i + EventOffset) % Events.Num();
|
||||
}
|
||||
|
||||
if (Events[Index].Id == EventId)
|
||||
{
|
||||
return &Events[Index];
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogDebugPlotEntry::AssignGraphAndAxis(int32 InGraph, ImAxis InYAxis)
|
||||
{
|
||||
GraphIndex = InGraph;
|
||||
YAxis = InYAxis;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogDebugPlotEntry::ResetGraphAndAxis()
|
||||
{
|
||||
GraphIndex = INDEX_NONE;
|
||||
YAxis = ImAxis_COUNT;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogDebugPlotEntry::Clear()
|
||||
{
|
||||
FCogDebugPlot::ResetLastAddedEvent();
|
||||
|
||||
MaxRow = 0;
|
||||
|
||||
if (Values.size() > 0)
|
||||
{
|
||||
Values.shrink(0);
|
||||
ValueOffset = 0;
|
||||
}
|
||||
|
||||
if (Events.Num() > 0)
|
||||
{
|
||||
Events.Empty();
|
||||
Events.Shrink();
|
||||
EventOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
bool FCogDebugPlotEntry::FindValue(float x, float& y) const
|
||||
{
|
||||
y = 0.0f;
|
||||
|
||||
bool FoundAfter = false;
|
||||
bool FoundBefore = false;
|
||||
|
||||
for (int32 i = Values.size() - 1; i >= 0; --i)
|
||||
{
|
||||
//--------------------------------------------------
|
||||
// The array cycle so we must offset the index
|
||||
//--------------------------------------------------
|
||||
int32 Index = i;
|
||||
if (ValueOffset != 0)
|
||||
{
|
||||
Index = (i + ValueOffset) % Values.size();
|
||||
}
|
||||
|
||||
const ImVec2 Point = Values[Index];
|
||||
if (Point.x > x)
|
||||
{
|
||||
FoundAfter = true;
|
||||
}
|
||||
|
||||
if (Point.x < x)
|
||||
{
|
||||
FoundBefore = true;
|
||||
}
|
||||
|
||||
if (FoundAfter && FoundBefore)
|
||||
{
|
||||
y = Point.y;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
// FCogPlot
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogDebugPlot::Reset()
|
||||
{
|
||||
Plots.Empty();
|
||||
Values.Empty();
|
||||
Events.Empty();
|
||||
OccupationMap.Empty();
|
||||
Pause = false;
|
||||
@@ -330,14 +28,14 @@ void FCogDebugPlot::Reset()
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogDebugPlot::Clear()
|
||||
{
|
||||
for (FCogDebugPlotEntry& Entry : Plots)
|
||||
for (auto& kv : Values)
|
||||
{
|
||||
Entry.Clear();
|
||||
kv.Value.Clear();
|
||||
}
|
||||
|
||||
for (FCogDebugPlotEntry& Entry : Events)
|
||||
for (auto& kv : Events)
|
||||
{
|
||||
Entry.Clear();
|
||||
kv.Value.Clear();
|
||||
}
|
||||
|
||||
OccupationMap.Empty();
|
||||
@@ -345,165 +43,45 @@ void FCogDebugPlot::Clear()
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogDebugPlot::ResetLastAddedEvent()
|
||||
{
|
||||
LastAddedEventPlotName = NAME_None;
|
||||
LastAddedEventIndex = INDEX_NONE;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent* FCogDebugPlot::GetLastAddedEvent()
|
||||
{
|
||||
FCogDebugPlotEntry* Plot = FindEntry(true, LastAddedEventPlotName);
|
||||
if (Plot == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return Plot->GetLastEvent();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEntry* FCogDebugPlot::FindEntry(const FName Name)
|
||||
{
|
||||
if (FCogDebugPlotEntry* Event = Events.FindByPredicate([Name](const FCogDebugPlotEntry& P) { return P.Name == Name; }))
|
||||
{
|
||||
return Event;
|
||||
}
|
||||
|
||||
if (FCogDebugPlotEntry* Plot = Plots.FindByPredicate([Name](const FCogDebugPlotEntry& P) { return P.Name == Name; }))
|
||||
{
|
||||
return Plot;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEntry* FCogDebugPlot::FindEntry(bool IsEvent, const FName Name)
|
||||
{
|
||||
TArray<FCogDebugPlotEntry>* Entries = IsEvent ? &Events : &Plots;
|
||||
FCogDebugPlotEntry* Entry = Entries->FindByPredicate([Name](const FCogDebugPlotEntry& P) { return P.Name == Name; });
|
||||
return Entry;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEntry* FCogDebugPlot::RegisterPlot(const UObject* WorldContextObject, const FName PlotName, bool IsEventPlot)
|
||||
bool FCogDebugPlot::ShouldRegisterEntry(const UObject* WorldContextObject, const UWorld*& World)
|
||||
{
|
||||
//----------------------------------------------------------
|
||||
// When not visible, we don't go further for performances.
|
||||
//----------------------------------------------------------
|
||||
if (IsVisible == false)
|
||||
{
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
const UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull);
|
||||
World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull);
|
||||
if (World == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FCogDebug::IsDebugActiveForObject(WorldContextObject) == false)
|
||||
{
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
FCogDebugPlotEntry* EntryPtr = FindEntry(IsEventPlot, PlotName);
|
||||
if (EntryPtr == nullptr)
|
||||
{
|
||||
TArray<FCogDebugPlotEntry>* Entries = IsEventPlot ? &Events : &Plots;
|
||||
EntryPtr = &Entries->AddDefaulted_GetRef();
|
||||
EntryPtr->Name = PlotName;
|
||||
EntryPtr->IsEventPlot = IsEventPlot;
|
||||
Entries->Sort([](const FCogDebugPlotEntry& A, const FCogDebugPlotEntry& B) { return A.Name.ToString().Compare(B.Name.ToString()) < 0; });
|
||||
}
|
||||
|
||||
//if (EntryPtr->YAxis == ImAxis_COUNT)
|
||||
//{
|
||||
// return nullptr;
|
||||
//}
|
||||
|
||||
const float Time = World->GetTimeSeconds();
|
||||
if (Time < EntryPtr->Time)
|
||||
{
|
||||
EntryPtr->Clear();
|
||||
}
|
||||
|
||||
EntryPtr->World = World;
|
||||
EntryPtr->Time = World->GetTimeSeconds();
|
||||
EntryPtr->Frame = GFrameCounter;
|
||||
|
||||
return EntryPtr;
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogDebugPlot::PlotValue(const UObject* WorldContextObject, const FName PlotName, const float Value)
|
||||
void FCogDebugPlot::InitializeEntry(FCogDebugHistory& OutValue, const UWorld* InWorld, const FName InName)
|
||||
{
|
||||
FCogDebugPlotEntry* Plot = RegisterPlot(WorldContextObject, PlotName, false);
|
||||
if (Plot == nullptr)
|
||||
const float Time = InWorld->GetTimeSeconds();
|
||||
if (Time < OutValue.Time)
|
||||
{
|
||||
return;
|
||||
OutValue.Clear();
|
||||
}
|
||||
|
||||
Plot->AddPoint(Plot->Time, Value);
|
||||
OutValue.Name = InName;
|
||||
OutValue.World = InWorld;
|
||||
OutValue.Time = InWorld->GetTimeSeconds();
|
||||
OutValue.Frame = GFrameCounter;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlot::PlotEvent(const UObject* WorldContextObject, const FName PlotName, const FName EventId, bool IsInstant, const int32 Row, const FColor& Color)
|
||||
{
|
||||
FCogDebugPlotEntry* Plot = RegisterPlot(WorldContextObject, PlotName, true);
|
||||
if (Plot == nullptr)
|
||||
{
|
||||
ResetLastAddedEvent();
|
||||
return DefaultEvent;
|
||||
}
|
||||
|
||||
FCogDebugPlotEvent& Event = Plot->AddEvent(GetNameSafe(WorldContextObject), IsInstant, EventId, Row, Color);
|
||||
return Event;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlot::PlotEventInstant(const UObject* WorldContextObject, const FName PlotName, const FName EventId, const int32 Row, const FColor& Color)
|
||||
{
|
||||
FCogDebugPlotEvent& Event = PlotEvent(WorldContextObject, PlotName, EventId, true, Row, Color);
|
||||
return Event;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlot::PlotEventStart(const UObject* WorldContextObject, const FName PlotName, const FName EventId, const int32 Row, const FColor& Color)
|
||||
{
|
||||
FCogDebugPlotEvent& Event = PlotEvent(WorldContextObject, PlotName, EventId, false, Row, Color);
|
||||
return Event;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlot::PlotEventStop(const UObject* WorldContextObject, const FName PlotName, const FName EventId)
|
||||
{
|
||||
FCogDebugPlotEntry* Plot = RegisterPlot(WorldContextObject, PlotName, true);
|
||||
if (Plot == nullptr)
|
||||
{
|
||||
return DefaultEvent;
|
||||
}
|
||||
|
||||
FCogDebugPlotEvent& Event = Plot->StopEvent(EventId);
|
||||
return Event;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlot::PlotEventToggle(const UObject* WorldContextObject, const FName PlotName, const FName EventId, const bool ToggleValue, const int32 Row, const FColor& Color)
|
||||
{
|
||||
if (ToggleValue)
|
||||
{
|
||||
return PlotEventStart(WorldContextObject, PlotName, EventId, Row, Color);
|
||||
}
|
||||
else
|
||||
{
|
||||
return PlotEventStop(WorldContextObject, PlotName, EventId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogDebugPlot::OccupyGraphRow(const int32 InGraphIndex, const int32 InRow)
|
||||
{
|
||||
@@ -558,3 +136,31 @@ int32 FCogDebugPlot::FindFreeGraphRow(const int32 InGraphIndex)
|
||||
return FreeRow;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugHistory* FCogDebugPlot::FindEntry(const FName InName)
|
||||
{
|
||||
FCogDebugHistory* Entry = Events.Find(InName);
|
||||
if (Entry != nullptr)
|
||||
{
|
||||
return Entry;
|
||||
}
|
||||
|
||||
Entry = Values.Find(InName);
|
||||
if (Entry != nullptr)
|
||||
{
|
||||
return Entry;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogDebugPlot::SetNumRecordedValues(int32 InValue)
|
||||
{
|
||||
NumRecordedValues = InValue;
|
||||
|
||||
for (auto& kv : Values)
|
||||
{
|
||||
kv.Value.SetNumRecordedValues(InValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
#include "CogDebugPlot.h"
|
||||
|
||||
#include "CogDebugHelper.h"
|
||||
#include "CogImguiHelper.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Engine/World.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
float FCogDebugPlotEvent::GetActualEndTime(const FCogDebugHistory& Plot) const
|
||||
{
|
||||
const UWorld* World = Plot.World.Get();
|
||||
const float WorldTime = World != nullptr ? World->GetTimeSeconds() : 0.0f;
|
||||
const float ActualEndTime = EndTime != 0.0f ? EndTime : WorldTime;
|
||||
return ActualEndTime;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
uint64 FCogDebugPlotEvent::GetActualEndFrame(const FCogDebugHistory& Plot) const
|
||||
{
|
||||
const float ActualEndFame = EndFrame != 0.0f ? EndFrame : GFrameCounter;
|
||||
return ActualEndFame;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlotEvent::AddParam(const FName Name, bool Value)
|
||||
{
|
||||
if (FCogDebugPlot::IsVisible)
|
||||
{
|
||||
AddParam(Name, FString::Printf(TEXT("%s"), Value ? TEXT("True") : TEXT("False")));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlotEvent::AddParam(const FName Name, int Value)
|
||||
{
|
||||
if (FCogDebugPlot::IsVisible)
|
||||
{
|
||||
AddParam(Name, FString::Printf(TEXT("%d"), Value));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlotEvent::AddParam(const FName Name, float Value)
|
||||
{
|
||||
if (FCogDebugPlot::IsVisible)
|
||||
{
|
||||
AddParam(Name, FString::Printf(TEXT("%0.2f"), Value));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlotEvent::AddParam(const FName Name, FName Value)
|
||||
{
|
||||
if (FCogDebugPlot::IsVisible)
|
||||
{
|
||||
AddParam(Name, Value.ToString());
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlotEvent::AddParam(const FName Name, const FString& Value)
|
||||
{
|
||||
if (FCogDebugPlot::IsVisible)
|
||||
{
|
||||
|
||||
if (Name == "Name")
|
||||
{
|
||||
DisplayName = Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
FCogDebugPlotEventParams& Param = Params.AddDefaulted_GetRef();
|
||||
Param.Name = Name;
|
||||
Param.Value = Value;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugEventHistory::AddEvent(
|
||||
const FString& OwnerName,
|
||||
const bool IsInstant,
|
||||
const FName EventId,
|
||||
const int32 Row,
|
||||
const FColor& Color)
|
||||
{
|
||||
if (Events.Max() < 200)
|
||||
{
|
||||
Events.Reserve(200);
|
||||
}
|
||||
|
||||
//----------------------------
|
||||
// Stop if it already exist.
|
||||
//----------------------------
|
||||
StopEvent(EventId);
|
||||
|
||||
FCogDebugPlotEvent* Event = nullptr;
|
||||
|
||||
int32 AddedIndex = 0;
|
||||
if (Events.Num() < Events.Max())
|
||||
{
|
||||
Event = &Events.AddDefaulted_GetRef();
|
||||
AddedIndex = Events.Num() - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Event = &Events[EventOffset];
|
||||
AddedIndex = EventOffset;
|
||||
EventOffset = (EventOffset + 1) % Events.Num();
|
||||
}
|
||||
|
||||
Event->Id = EventId;
|
||||
Event->OwnerName = OwnerName;
|
||||
Event->DisplayName = EventId.ToString();
|
||||
Event->StartTime = Time;
|
||||
Event->EndTime = IsInstant ? Time : 0.0f;
|
||||
Event->StartFrame = Frame;
|
||||
Event->EndFrame = IsInstant ? Frame : 0.0f;
|
||||
Event->Row = (Row == FCogDebugPlot::AutoRow) ? FCogDebugPlot::FindFreeGraphRow(GraphIndex) : Row;
|
||||
|
||||
if (IsInstant == false)
|
||||
{
|
||||
FCogDebugPlot::OccupyGraphRow(GraphIndex, Event->Row);
|
||||
}
|
||||
|
||||
MaxRow = FMath::Max(Event->Row, MaxRow);
|
||||
|
||||
const FColor BorderColor = FCogDebugHelper::GetAutoColor(EventId, Color).WithAlpha(200);
|
||||
const FColor FillColor = BorderColor.WithAlpha(100);
|
||||
Event->BorderColor = FCogImguiHelper::ToImColor(BorderColor);
|
||||
Event->FillColor = FCogImguiHelper::ToImColor(FillColor);
|
||||
|
||||
FCogDebugPlot::LastAddedEventPlotName = Name;
|
||||
FCogDebugPlot::LastAddedEventIndex = AddedIndex;
|
||||
|
||||
return *Event;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugEventHistory::StopEvent(const FName EventId)
|
||||
{
|
||||
FCogDebugPlotEvent* Event = FindLastEventByName(EventId);
|
||||
if (Event == nullptr)
|
||||
{
|
||||
return FCogDebugPlot::DefaultEvent;
|
||||
}
|
||||
|
||||
if (Event->EndTime == 0.0f)
|
||||
{
|
||||
Event->EndTime = Time;
|
||||
Event->EndFrame = Frame;
|
||||
|
||||
FCogDebugPlot::FreeGraphRow(GraphIndex, Event->Row);
|
||||
}
|
||||
|
||||
return *Event;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent* FCogDebugEventHistory::GetLastEvent()
|
||||
{
|
||||
if (Events.Num() == 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int32 Index = Events.Num() - 1;
|
||||
if (EventOffset != 0)
|
||||
{
|
||||
Index = (Index + EventOffset) % Events.Num();
|
||||
}
|
||||
|
||||
return &Events[Index];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent* FCogDebugEventHistory::FindLastEventByName(FName EventId)
|
||||
{
|
||||
for (int32 i = Events.Num() - 1; i >= 0; --i)
|
||||
{
|
||||
//--------------------------------------------------
|
||||
// The array cycle so we must offset the index
|
||||
//--------------------------------------------------
|
||||
int32 Index = i;
|
||||
if (EventOffset != 0)
|
||||
{
|
||||
Index = (i + EventOffset) % Events.Num();
|
||||
}
|
||||
|
||||
if (Events[Index].Id == EventId)
|
||||
{
|
||||
return &Events[Index];
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogDebugEventHistory::Clear()
|
||||
{
|
||||
FCogDebugHistory::Clear();
|
||||
|
||||
FCogDebugPlot::ResetLastAddedEvent();
|
||||
|
||||
MaxRow = 0;
|
||||
|
||||
if (Events.Num() > 0)
|
||||
{
|
||||
Events.Empty();
|
||||
Events.Shrink();
|
||||
EventOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlot::PlotEvent(const UObject* WorldContextObject, const FName PlotName, const FName EventId, bool IsInstant, const int32 Row, const FColor& Color)
|
||||
{
|
||||
FCogDebugEventHistory* EventHistory = RegisterEvent(WorldContextObject, PlotName);
|
||||
if (EventHistory == nullptr)
|
||||
{
|
||||
ResetLastAddedEvent();
|
||||
return DefaultEvent;
|
||||
}
|
||||
|
||||
FCogDebugPlotEvent& Event = EventHistory->AddEvent(GetNameSafe(WorldContextObject), IsInstant, EventId, Row, Color);
|
||||
return Event;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlot::PlotEventInstant(const UObject* WorldContextObject, const FName PlotName, const FName EventId, const int32 Row, const FColor& Color)
|
||||
{
|
||||
FCogDebugPlotEvent& Event = PlotEvent(WorldContextObject, PlotName, EventId, true, Row, Color);
|
||||
return Event;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlot::PlotEventStart(const UObject* WorldContextObject, const FName PlotName, const FName EventId, const int32 Row, const FColor& Color)
|
||||
{
|
||||
FCogDebugPlotEvent& Event = PlotEvent(WorldContextObject, PlotName, EventId, false, Row, Color);
|
||||
return Event;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlot::PlotEventStop(const UObject* WorldContextObject, const FName PlotName, const FName EventId)
|
||||
{
|
||||
FCogDebugEventHistory* EventHistory = RegisterEvent(WorldContextObject, PlotName);
|
||||
if (EventHistory == nullptr)
|
||||
{
|
||||
return DefaultEvent;
|
||||
}
|
||||
|
||||
FCogDebugPlotEvent& Event = EventHistory->StopEvent(EventId);
|
||||
return Event;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent& FCogDebugPlot::PlotEventToggle(const UObject* WorldContextObject, const FName PlotName, const FName EventId, const bool ToggleValue, const int32 Row, const FColor& Color)
|
||||
{
|
||||
if (ToggleValue)
|
||||
{
|
||||
return PlotEventStart(WorldContextObject, PlotName, EventId, Row, Color);
|
||||
}
|
||||
else
|
||||
{
|
||||
return PlotEventStop(WorldContextObject, PlotName, EventId);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugEventHistory* FCogDebugPlot::RegisterEvent(const UObject* InWorldContextObject, const FName InName)
|
||||
{
|
||||
|
||||
const UWorld* World;
|
||||
if (ShouldRegisterEntry(InWorldContextObject, World) == false)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FCogDebugEventHistory& Event = Events.FindOrAdd(InName);
|
||||
Event.Type = FCogDebugHistoryType::Event;
|
||||
|
||||
InitializeEntry(Event, World, InName);
|
||||
|
||||
return &Event;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogDebugPlot::ResetLastAddedEvent()
|
||||
{
|
||||
LastAddedEventPlotName = NAME_None;
|
||||
LastAddedEventIndex = INDEX_NONE;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugPlotEvent* FCogDebugPlot::GetLastAddedEvent()
|
||||
{
|
||||
FCogDebugEventHistory* EventHistory = Events.Find(LastAddedEventPlotName);
|
||||
if (EventHistory == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return EventHistory->GetLastEvent();
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
#include "CogDebugPlot.h"
|
||||
|
||||
#include "CogImguiHelper.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Engine/World.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogDebugValueHistory::SetNumRecordedValues(const int32 Value)
|
||||
{
|
||||
Values.reserve(Value);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogDebugValueHistory::AddPoint(float X, float Y)
|
||||
{
|
||||
if (Values.Capacity == 0)
|
||||
{
|
||||
Values.reserve(FCogDebugPlot::NumRecordedValues);
|
||||
}
|
||||
|
||||
if (Values.size() < Values.Capacity)
|
||||
{
|
||||
Values.push_back(ImVec2(X, Y));
|
||||
}
|
||||
else
|
||||
{
|
||||
Values[ValueOffset] = ImVec2(X, Y);
|
||||
ValueOffset = (ValueOffset + 1) % Values.size();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogDebugValueHistory::Clear()
|
||||
{
|
||||
FCogDebugHistory::Clear();
|
||||
|
||||
if (Values.size() > 0)
|
||||
{
|
||||
Values.shrink(0);
|
||||
ValueOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
bool FCogDebugValueHistory::FindValue(float x, float& y) const
|
||||
{
|
||||
y = 0.0f;
|
||||
|
||||
bool FoundAfter = false;
|
||||
bool FoundBefore = false;
|
||||
|
||||
for (int32 i = Values.size() - 1; i >= 0; --i)
|
||||
{
|
||||
//--------------------------------------------------
|
||||
// The array cycle so we must offset the index
|
||||
//--------------------------------------------------
|
||||
int32 Index = i;
|
||||
if (ValueOffset != 0)
|
||||
{
|
||||
Index = (i + ValueOffset) % Values.size();
|
||||
}
|
||||
|
||||
const ImVec2 Point = Values[Index];
|
||||
if (Point.x > x)
|
||||
{
|
||||
FoundAfter = true;
|
||||
}
|
||||
|
||||
if (Point.x < x)
|
||||
{
|
||||
FoundBefore = true;
|
||||
}
|
||||
|
||||
if (FoundAfter && FoundBefore)
|
||||
{
|
||||
y = Point.y;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogDebugValueHistory* FCogDebugPlot::RegisterValue(const UObject* InWorldContextObject, const FName InName)
|
||||
{
|
||||
const UWorld* World;
|
||||
if (ShouldRegisterEntry(InWorldContextObject, World) == false)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FCogDebugValueHistory& Value = Values.FindOrAdd(InName);
|
||||
Value.Type = FCogDebugHistoryType::Value;
|
||||
|
||||
InitializeEntry(Value, World, InName);
|
||||
|
||||
return &Value;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogDebugPlot::PlotValue(const UObject* WorldContextObject, const FName PlotName, const float Value)
|
||||
{
|
||||
if (Pause && RecordValuesWhenPause == false)
|
||||
{ return; }
|
||||
|
||||
FCogDebugValueHistory* ValueHistory = RegisterValue(WorldContextObject, PlotName);
|
||||
if (ValueHistory == nullptr)
|
||||
{ return; }
|
||||
|
||||
ValueHistory->AddPoint(ValueHistory->Time, Value);
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#ifdef ENABLE_COG
|
||||
|
||||
struct FCogDebugPlotEntry;
|
||||
struct FCogDebugHistory;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
struct COGDEBUG_API FCogDebugPlotEventParams
|
||||
@@ -19,9 +19,9 @@ struct COGDEBUG_API FCogDebugPlotEventParams
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
struct COGDEBUG_API FCogDebugPlotEvent
|
||||
{
|
||||
float GetActualEndTime(const FCogDebugPlotEntry& Plot) const;
|
||||
float GetActualEndTime(const FCogDebugHistory& Plot) const;
|
||||
|
||||
uint64 GetActualEndFrame(const FCogDebugPlotEntry& Plot) const;
|
||||
uint64 GetActualEndFrame(const FCogDebugHistory& Plot) const;
|
||||
|
||||
FCogDebugPlotEvent& AddParam(const FName Name, bool Value);
|
||||
|
||||
@@ -47,58 +47,68 @@ struct COGDEBUG_API FCogDebugPlotEvent
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
struct COGDEBUG_API FCogDebugPlotEntry
|
||||
enum class FCogDebugHistoryType
|
||||
{
|
||||
void AssignGraphAndAxis(int32 AssignedRow, ImAxis CurrentYAxis);
|
||||
Value,
|
||||
Event,
|
||||
};
|
||||
|
||||
void AddPoint(float X, float Y);
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
struct COGDEBUG_API FCogDebugHistory
|
||||
{
|
||||
virtual ~FCogDebugHistory() {}
|
||||
|
||||
bool FindValue(float Time, float& Value) const;
|
||||
|
||||
void ResetGraphAndAxis();
|
||||
|
||||
void Clear();
|
||||
|
||||
FCogDebugPlotEvent& AddEvent(const FString& OwnerName, bool IsInstant, const FName EventId, const int32 Row, const FColor& Color);
|
||||
|
||||
FCogDebugPlotEvent& StopEvent(const FName EventId);
|
||||
|
||||
FCogDebugPlotEvent* GetLastEvent();
|
||||
|
||||
FCogDebugPlotEvent* FindLastEventByName(FName EventId);
|
||||
virtual void Clear() {}
|
||||
|
||||
FName Name;
|
||||
|
||||
bool IsEventPlot = false;
|
||||
|
||||
int32 GraphIndex = INDEX_NONE;
|
||||
|
||||
ImAxis YAxis = ImAxis_COUNT;
|
||||
|
||||
float Time = 0;
|
||||
|
||||
uint64 Frame = 0;
|
||||
|
||||
TWeakObjectPtr<const UWorld> World;
|
||||
int32 GraphIndex = 0;
|
||||
|
||||
FCogDebugHistoryType Type = FCogDebugHistoryType::Value;
|
||||
|
||||
TWeakObjectPtr<const UWorld> World;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
struct COGDEBUG_API FCogDebugValueHistory : FCogDebugHistory
|
||||
{
|
||||
void AddPoint(float X, float Y);
|
||||
|
||||
bool FindValue(float Time, float& Value) const;
|
||||
|
||||
virtual void Clear() override;
|
||||
|
||||
void SetNumRecordedValues(const int32 Value);
|
||||
|
||||
//--------------------------
|
||||
// Values
|
||||
//--------------------------
|
||||
int32 ValueOffset = 0;
|
||||
|
||||
ImVector<ImVec2> Values;
|
||||
|
||||
bool ShowValuesMarkers = false;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
struct COGDEBUG_API FCogDebugEventHistory : FCogDebugHistory
|
||||
{
|
||||
FCogDebugPlotEvent& AddEvent(const FString& OwnerName, bool IsInstant, const FName EventId, const int32 Row, const FColor& Color);
|
||||
|
||||
FCogDebugPlotEvent& StopEvent(const FName EventId);
|
||||
|
||||
FCogDebugPlotEvent* GetLastEvent();
|
||||
|
||||
FCogDebugPlotEvent* FindLastEventByName(FName EventId);
|
||||
|
||||
virtual void Clear() override;
|
||||
|
||||
//--------------------------
|
||||
// Events
|
||||
//--------------------------
|
||||
int32 EventOffset = 0;
|
||||
|
||||
TArray<FCogDebugPlotEvent> Events;
|
||||
|
||||
int32 MaxRow = 1;
|
||||
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -108,6 +118,11 @@ class COGDEBUG_API FCogDebugPlot
|
||||
public:
|
||||
static constexpr int32 AutoRow = -1;
|
||||
|
||||
static constexpr int32 MaxNumGraphs = 5;
|
||||
|
||||
static constexpr int32 MaxNumEntriesPerGraph = 10;
|
||||
|
||||
|
||||
static void PlotValue(const UObject* WorldContextObject, const FName PlotName, const float Value);
|
||||
|
||||
static FCogDebugPlotEvent& PlotEvent(const UObject* WorldContextObject, const FName PlotName, const FName EventId, bool IsInstant, const int32 Row = AutoRow, const FColor& Color = FColor::Transparent);
|
||||
@@ -120,29 +135,40 @@ public:
|
||||
|
||||
static FCogDebugPlotEvent& PlotEventToggle(const UObject* WorldContextObject, const FName PlotName, const FName EventId, const bool ToggleValue, const int32 Row = AutoRow, const FColor& Color = FColor::Transparent);
|
||||
|
||||
static FCogDebugHistory* FindEntry(const FName InName);
|
||||
|
||||
static void SetNumRecordedValues(int32 InValue);
|
||||
|
||||
static void Reset();
|
||||
|
||||
static void Clear();
|
||||
|
||||
static FCogDebugPlotEntry* FindEntry(const FName Name);
|
||||
static TMap<FName, FCogDebugValueHistory> Values;
|
||||
|
||||
static FCogDebugPlotEntry* FindEntry(bool IsEvent, const FName Name);
|
||||
|
||||
static TArray<FCogDebugPlotEntry> Plots;
|
||||
|
||||
static TArray<FCogDebugPlotEntry> Events;
|
||||
static TMap<FName, FCogDebugEventHistory> Events;
|
||||
|
||||
static bool IsVisible;
|
||||
|
||||
static bool Pause;
|
||||
|
||||
static bool RecordValuesWhenPause;
|
||||
|
||||
|
||||
private:
|
||||
friend struct FCogDebugPlotEntry;
|
||||
friend struct FCogDebugHistory;
|
||||
friend struct FCogDebugEventHistory;
|
||||
friend struct FCogDebugValueHistory;
|
||||
|
||||
static void ResetLastAddedEvent();
|
||||
|
||||
static FCogDebugPlotEntry* RegisterPlot(const UObject* Owner, const FName PlotName, bool IsEventPlot);
|
||||
|
||||
static FCogDebugValueHistory* RegisterValue(const UObject* InWorldContextObject, const FName InName);
|
||||
|
||||
static FCogDebugEventHistory* RegisterEvent(const UObject* InWorldContextObject, const FName InName);
|
||||
|
||||
static bool ShouldRegisterEntry(const UObject* WorldContextObject, const UWorld*& World);
|
||||
|
||||
static void InitializeEntry(FCogDebugHistory& OutValue, const UWorld* InWorld, const FName InName);
|
||||
|
||||
static FCogDebugPlotEvent* GetLastAddedEvent();
|
||||
|
||||
static void OccupyGraphRow(const int32 InGraphIndex, const int32 InRow);
|
||||
@@ -151,6 +177,8 @@ private:
|
||||
|
||||
static int32 FindFreeGraphRow(const int32 InGraphIndex);
|
||||
|
||||
static int32 NumRecordedValues;
|
||||
|
||||
static FName LastAddedEventPlotName;
|
||||
|
||||
static int32 LastAddedEventIndex;
|
||||
|
||||
@@ -17,6 +17,11 @@ void FCogEngineWindow_Plots::Initialize()
|
||||
|
||||
Config = GetConfig<UCogEngineConfig_Plots>();
|
||||
|
||||
if (Config != nullptr)
|
||||
{
|
||||
RefreshPlotSettings();
|
||||
}
|
||||
|
||||
FCogDebugPlot::Clear();
|
||||
}
|
||||
|
||||
@@ -34,8 +39,9 @@ void FCogEngineWindow_Plots::ResetConfig()
|
||||
Super::ResetConfig();
|
||||
|
||||
Config->Reset();
|
||||
}
|
||||
|
||||
RefreshPlotSettings();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogEngineWindow_Plots::RenderTick(float DeltaTime)
|
||||
@@ -49,23 +55,6 @@ void FCogEngineWindow_Plots::RenderContent()
|
||||
{
|
||||
Super::RenderContent();
|
||||
|
||||
TArray<FCogDebugPlotEntry*> VisiblePlots;
|
||||
for (FCogDebugPlotEntry& Plot : FCogDebugPlot::Plots)
|
||||
{
|
||||
if (Plot.YAxis != ImAxis_COUNT && Plot.GraphIndex != INDEX_NONE)
|
||||
{
|
||||
VisiblePlots.Add(&Plot);
|
||||
}
|
||||
}
|
||||
|
||||
for (FCogDebugPlotEntry& Event : FCogDebugPlot::Events)
|
||||
{
|
||||
if (Event.YAxis != ImAxis_COUNT && Event.GraphIndex != INDEX_NONE)
|
||||
{
|
||||
VisiblePlots.Add(&Event);
|
||||
}
|
||||
}
|
||||
|
||||
RenderMenu();
|
||||
|
||||
if (Config->DockEntries)
|
||||
@@ -86,7 +75,7 @@ void FCogEngineWindow_Plots::RenderContent()
|
||||
RenderAllEntriesNames(ImVec2(0, -1));
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
RenderPlots(VisiblePlots);
|
||||
RenderPlots();
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
@@ -94,12 +83,19 @@ void FCogEngineWindow_Plots::RenderContent()
|
||||
}
|
||||
else
|
||||
{
|
||||
RenderPlots(VisiblePlots);
|
||||
RenderPlots();
|
||||
}
|
||||
|
||||
bApplyTimeScale = false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogEngineWindow_Plots::RefreshPlotSettings()
|
||||
{
|
||||
FCogDebugPlot::SetNumRecordedValues(Config->NumRecordedValues);
|
||||
FCogDebugPlot::RecordValuesWhenPause = Config->RecordValuesWhenPaused;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogEngineWindow_Plots::RenderMenu()
|
||||
{
|
||||
@@ -116,33 +112,41 @@ void FCogEngineWindow_Plots::RenderMenu()
|
||||
|
||||
if (ImGui::BeginMenu("Options"))
|
||||
{
|
||||
if (ImGui::MenuItem("Reset"))
|
||||
{
|
||||
FCogDebugPlot::Pause = false;
|
||||
FCogDebugPlot::Reset();
|
||||
ResetConfig();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
FCogWindowWidgets::SetNextItemToShortWidth();
|
||||
ImGui::SliderInt("Num graphs", &Config->NumGraphs, 1, UCogEngineConfig_Plots::MaxNumGraphs);
|
||||
|
||||
FCogWindowWidgets::SetNextItemToShortWidth();
|
||||
if (ImGui::SliderInt("Num Graphs", &Config->NumGraphs, 1, 5))
|
||||
ImGui::SliderInt("Num Y axis", &Config->NumYAxis, 1, 3);
|
||||
|
||||
FCogWindowWidgets::SetNextItemToShortWidth();
|
||||
if (ImGui::SliderFloat("Time range", &Config->TimeRange, 1.0f, 100.0f, "%0.0f"))
|
||||
{
|
||||
bApplyTimeScale = true;
|
||||
}
|
||||
|
||||
FCogWindowWidgets::SetNextItemToShortWidth();
|
||||
ImGui::SliderInt("Num YAxis", &Config->NumYAxis, 0, 3);
|
||||
|
||||
FCogWindowWidgets::SetNextItemToShortWidth();
|
||||
if (ImGui::SliderFloat("Time range", &Config->TimeRange, 1.0f, 100.0f, "%0.1f"))
|
||||
if (ImGui::SliderInt("Num recorded values", &Config->NumRecordedValues, 100, 10000))
|
||||
{
|
||||
bApplyTimeScale = true;
|
||||
Config->NumRecordedValues = (Config->NumRecordedValues / 100) * 100;
|
||||
}
|
||||
|
||||
if (ImGui::IsItemDeactivatedAfterEdit())
|
||||
{
|
||||
RefreshPlotSettings();
|
||||
}
|
||||
|
||||
FCogWindowWidgets::SetNextItemToShortWidth();
|
||||
ImGui::SliderFloat("Auto-fit padding", &Config->AutoFitPadding, 0.0f, 0.2f, "%0.2f");
|
||||
|
||||
FCogWindowWidgets::SetNextItemToShortWidth();
|
||||
ImGui::SliderFloat("Drag pause sensitivity", &Config->DragPauseSensitivity, 1.0f, 50.0f, "%0.0f");
|
||||
|
||||
ImGui::Checkbox("Record values when paused", &Config->RecordValuesWhenPaused);
|
||||
if (ImGui::IsItemDeactivatedAfterEdit())
|
||||
{
|
||||
RefreshPlotSettings();
|
||||
}
|
||||
|
||||
FCogWindowWidgets::SetNextItemToShortWidth();
|
||||
ImGui::Checkbox("Show time bar at game time", &Config->ShowTimeBarAtGameTime);
|
||||
|
||||
@@ -159,6 +163,16 @@ void FCogEngineWindow_Plots::RenderMenu()
|
||||
FCogImguiHelper::ColorEdit4("Pause background color", Config->PauseBackgroundColor, ColorEditFlags);
|
||||
ImGui::SetItemTooltip("Background color of the plot when paused.");
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("Reset Settings"))
|
||||
{
|
||||
FCogDebugPlot::Pause = false;
|
||||
FCogDebugPlot::Reset();
|
||||
ResetConfig();
|
||||
bApplyTimeScale = true;
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
@@ -174,20 +188,31 @@ void FCogEngineWindow_Plots::RenderMenu()
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogEngineWindow_Plots::RenderEntryName(const int Index, FCogDebugPlotEntry& Entry)
|
||||
void FCogEngineWindow_Plots::RenderEntryName(const int Index, FCogDebugHistory& Entry)
|
||||
{
|
||||
ImGui::PushID(Index);
|
||||
|
||||
const bool IsAssignedToRow = Entry.GraphIndex != INDEX_NONE;
|
||||
if (ImGui::Selectable(TCHAR_TO_ANSI(*Entry.Name.ToString()), IsAssignedToRow, ImGuiSelectableFlags_AllowDoubleClick))
|
||||
bool IsAssignedToGraph = false;
|
||||
|
||||
for (int32 i = 0; i < UCogEngineConfig_Plots::MaxNumGraphs; ++i)
|
||||
{
|
||||
if (IsAssignedToRow)
|
||||
FCogEngineConfig_Plots_GraphInfo& GraphInfo = Config->Graphs[i];
|
||||
if (GraphInfo.Entries.ContainsByPredicate([Entry](const auto& InEntry) { return InEntry.Name == Entry.Name; }))
|
||||
{
|
||||
Entry.ResetGraphAndAxis();
|
||||
IsAssignedToGraph = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::Selectable(TCHAR_TO_ANSI(*Entry.Name.ToString()), IsAssignedToGraph, ImGuiSelectableFlags_AllowDoubleClick))
|
||||
{
|
||||
if (IsAssignedToGraph)
|
||||
{
|
||||
UnassignToGraphAndAxis(Entry.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
Entry.AssignGraphAndAxis(0, ImAxis_Y1);
|
||||
AssignToGraphAndAxis(Entry.Name, 0, ImAxis_Y1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,50 +230,46 @@ void FCogEngineWindow_Plots::RenderEntryName(const int Index, FCogDebugPlotEntry
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogEngineWindow_Plots::RenderAllEntriesNames(const ImVec2& InSize)
|
||||
{
|
||||
const int32 Indent = ImGui::GetFontSize() * 0.5f;
|
||||
|
||||
if (ImGui::BeginChild("Entries", InSize))
|
||||
{
|
||||
if (Config->DockEntries)
|
||||
{
|
||||
ImGui::Indent(6);
|
||||
}
|
||||
|
||||
int Index = 0;
|
||||
|
||||
if (FCogWindowWidgets::DarkCollapsingHeader("Events", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
ImGui::Indent(Indent);
|
||||
if (FCogDebugPlot::Events.IsEmpty())
|
||||
{
|
||||
ImGui::TextDisabled("No event added yet");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (FCogDebugPlotEntry& Event : FCogDebugPlot::Events)
|
||||
for (auto& kv : FCogDebugPlot::Events)
|
||||
{
|
||||
RenderEntryName(Index, Event);
|
||||
RenderEntryName(Index, kv.Value);
|
||||
Index++;
|
||||
}
|
||||
}
|
||||
ImGui::Unindent(Indent);
|
||||
}
|
||||
|
||||
if (FCogWindowWidgets::DarkCollapsingHeader("Plots", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
if (FCogDebugPlot::Plots.IsEmpty())
|
||||
ImGui::Indent(Indent);
|
||||
if (FCogDebugPlot::Values.IsEmpty())
|
||||
{
|
||||
ImGui::TextDisabled("No plot added yet");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (FCogDebugPlotEntry& Plot : FCogDebugPlot::Plots)
|
||||
for (auto& kv : FCogDebugPlot::Values)
|
||||
{
|
||||
RenderEntryName(Index, Plot);
|
||||
RenderEntryName(Index, kv.Value);
|
||||
Index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config->DockEntries)
|
||||
{
|
||||
ImGui::Unindent();
|
||||
ImGui::Unindent(Indent);
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
@@ -257,17 +278,14 @@ void FCogEngineWindow_Plots::RenderAllEntriesNames(const ImVec2& InSize)
|
||||
{
|
||||
if (const ImGuiPayload* Payload = ImGui::AcceptDragDropPayload("DragAndDrop"))
|
||||
{
|
||||
if (FCogDebugPlotEntry* Plot = FCogDebugPlot::FindEntry(FName((const char*)Payload->Data)))
|
||||
{
|
||||
Plot->ResetGraphAndAxis();
|
||||
}
|
||||
UnassignToGraphAndAxis(GetDroppedEntryName(Payload));
|
||||
}
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogEngineWindow_Plots::RenderPlots(const TArray<FCogDebugPlotEntry*>& VisiblePlots) const
|
||||
void FCogEngineWindow_Plots::RenderPlots()
|
||||
{
|
||||
if (ImGui::BeginChild("Graph", ImVec2(0, -1)))
|
||||
{
|
||||
@@ -281,45 +299,66 @@ void FCogEngineWindow_Plots::RenderPlots(const TArray<FCogDebugPlotEntry*>& Visi
|
||||
ImPlot::PushStyleColor(ImPlotCol_PlotBg, FCogImguiHelper::ToImVec4(Config->PauseBackgroundColor));
|
||||
}
|
||||
|
||||
ImPlot::PushStyleVar(ImPlotStyleVar_FitPadding, ImVec2(0.0f, Config->AutoFitPadding));
|
||||
|
||||
if (ImPlot::BeginSubplots("", Config->NumGraphs, 1, ImVec2(-1, -1), SubplotsFlags, RowRatios, ColRatios))
|
||||
{
|
||||
for (int PlotIndex = 0; PlotIndex < Config->NumGraphs; ++PlotIndex)
|
||||
for (int32 GraphIndex = 0; GraphIndex < Config->NumGraphs && GraphIndex < UCogEngineConfig_Plots::MaxNumGraphs; ++GraphIndex)
|
||||
{
|
||||
ImGui::PushID(GraphIndex);
|
||||
|
||||
FCogEngineConfig_Plots_GraphInfo& GraphInfo = Config->Graphs[GraphIndex];
|
||||
|
||||
if (ImPlot::BeginPlot("##Plot", ImVec2(-1, 250)))
|
||||
{
|
||||
ImPlotAxisFlags HasPlotOnAxisY1 = false;
|
||||
ImPlotAxisFlags HasPlotOnAxisY2 = false;
|
||||
ImPlotAxisFlags HasPlotOnAxisY3 = false;
|
||||
|
||||
for (const FCogDebugPlotEntry* PlotPtr : VisiblePlots)
|
||||
{
|
||||
HasPlotOnAxisY1 |= PlotPtr->YAxis == ImAxis_Y1 && PlotPtr->GraphIndex == PlotIndex;
|
||||
HasPlotOnAxisY2 |= PlotPtr->YAxis == ImAxis_Y2 && PlotPtr->GraphIndex == PlotIndex;
|
||||
HasPlotOnAxisY3 |= PlotPtr->YAxis == ImAxis_Y3 && PlotPtr->GraphIndex == PlotIndex;
|
||||
}
|
||||
|
||||
ImPlot::SetupAxis(ImAxis_X1, nullptr, ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoGridLines);
|
||||
|
||||
if (Config->NumYAxis > 0)
|
||||
{
|
||||
ImPlot::SetupAxis(ImAxis_Y1, HasPlotOnAxisY1 ? "" : "[drop here]", (HasPlotOnAxisY1 ? ImPlotAxisFlags_None : (ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoGridLines)) | ImPlotAxisFlags_AutoFit);
|
||||
}
|
||||
|
||||
if (Config->NumYAxis > 1)
|
||||
{
|
||||
ImPlot::SetupAxis(ImAxis_Y2, HasPlotOnAxisY2 ? "" : "[drop here]", (HasPlotOnAxisY2 ? ImPlotAxisFlags_None : (ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoGridLines)) | ImPlotAxisFlags_AutoFit | ImPlotAxisFlags_Opposite);
|
||||
}
|
||||
|
||||
if (Config->NumYAxis > 2)
|
||||
{
|
||||
ImPlot::SetupAxis(ImAxis_Y3, HasPlotOnAxisY3 ? "" : "[drop here]", (HasPlotOnAxisY3 ? ImPlotAxisFlags_None : (ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoGridLines)) | ImPlotAxisFlags_AutoFit | ImPlotAxisFlags_Opposite);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Set the initial X axis range. After, it is automatically updated to move with the current time.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
ImPlot::SetupAxisLimits(ImAxis_X1, 0, Config->TimeRange, ImGuiCond_Appearing);
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Setup the Y axis
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
for (int32 YAxisIndex = 0; YAxisIndex <= (ImAxis_Y3 - ImAxis_Y1); ++YAxisIndex)
|
||||
{
|
||||
const ImAxis YAxis = ImAxis_Y1 + YAxisIndex;
|
||||
|
||||
bool IsAssigned = false;
|
||||
int32 YMax = 0;
|
||||
for (const FCogEngineConfig_Plots_GraphEntryInfo& GraphEntry : GraphInfo.Entries)
|
||||
{
|
||||
IsAssigned |= GraphEntry.YAxis == YAxis;
|
||||
|
||||
if (FCogDebugEventHistory* EventHistory = FCogDebugPlot::Events.Find(GraphEntry.Name))
|
||||
{
|
||||
YMax = FMath::Max(FMath::Max(5, YMax), EventHistory->MaxRow);
|
||||
}
|
||||
}
|
||||
|
||||
const bool IsAxisVisible = IsAssigned || (YAxisIndex < Config->NumYAxis);
|
||||
if (IsAxisVisible)
|
||||
{
|
||||
ImPlotAxisFlags Flags = IsAssigned ? ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_AutoFit
|
||||
: ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_AutoFit;
|
||||
if (YAxisIndex > 0)
|
||||
{
|
||||
Flags |= ImPlotAxisFlags_Opposite;
|
||||
}
|
||||
|
||||
ImPlot::SetupAxis(YAxis, IsAssigned || (Config->NumYAxis == 1) ? "" : "[drop here]", Flags);
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Set the Y axis limit for Events.
|
||||
//--------------------------------------------------------------------------------
|
||||
if (YMax > 0)
|
||||
{
|
||||
ImPlot::SetupAxisLimits(YAxis, 0, YMax, ImGuiCond_Always);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ImPlotRange& PlotRange = ImPlot::GetCurrentPlot()->Axes[ImAxis_X1].Range;
|
||||
const float TimeRange = PlotRange.Max - PlotRange.Min;
|
||||
|
||||
@@ -331,7 +370,7 @@ void FCogEngineWindow_Plots::RenderPlots(const TArray<FCogDebugPlotEntry*>& Visi
|
||||
const float Time = GetWorld() ? GetWorld()->GetTimeSeconds() : 0.0;
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Setup all the Z and Y axis limits. Must be done before calling
|
||||
// Setup all the X axis limits. Must be done before calling
|
||||
// ImPlot::GetPlotPos or ImPlot::GetPlotSize as it calls SetupLock()
|
||||
//------------------------------------------------------------------
|
||||
{
|
||||
@@ -347,23 +386,6 @@ void FCogEngineWindow_Plots::RenderPlots(const TArray<FCogDebugPlotEntry*>& Visi
|
||||
{
|
||||
ImPlot::SetupAxisLimits(ImAxis_X1, Time - Config->TimeRange, Time, ImGuiCond_Always);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Set the Y axis limit for Events.
|
||||
//--------------------------------------------------------------------------------
|
||||
for (const FCogDebugPlotEntry* PlotPtr : VisiblePlots)
|
||||
{
|
||||
if (PlotPtr == nullptr)
|
||||
{ continue; }
|
||||
|
||||
if (PlotPtr->GraphIndex != PlotIndex)
|
||||
{ continue; }
|
||||
|
||||
if (PlotPtr->IsEventPlot)
|
||||
{
|
||||
ImPlot::SetupAxisLimits(PlotPtr->YAxis, 0, PlotPtr->MaxRow + 2, ImGuiCond_Always);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ImVec2 PlotMin = ImPlot::GetPlotPos();
|
||||
@@ -373,17 +395,21 @@ void FCogEngineWindow_Plots::RenderPlots(const TArray<FCogDebugPlotEntry*>& Visi
|
||||
//----------------------------------------------------------------
|
||||
// Pause the scrolling if the user drag inside
|
||||
//----------------------------------------------------------------
|
||||
const ImVec2 Mouse = ImGui::GetMousePos();
|
||||
if (Mouse.x > PlotMin.x
|
||||
&& Mouse.y > PlotMin.y
|
||||
&& Mouse.x < PlotMax.x
|
||||
&& Mouse.y < PlotMax.y
|
||||
&& ImGui::GetDragDropPayload() == nullptr)
|
||||
if (ImGui::IsWindowFocused())
|
||||
{
|
||||
const ImVec2 Drag = ImGui::GetMouseDragDelta(0);
|
||||
if (FMath::Abs(Drag.x) > Config->DragPauseSensitivity)
|
||||
const ImVec2 Mouse = ImGui::GetMousePos();
|
||||
if (Mouse.x > PlotMin.x
|
||||
&& Mouse.y > PlotMin.y
|
||||
&& Mouse.x < PlotMax.x
|
||||
&& Mouse.y < PlotMax.y
|
||||
&& ImGui::GetDragDropPayload() == nullptr)
|
||||
{
|
||||
FCogDebugPlot::Pause = true;
|
||||
const ImVec2 Drag = ImGui::GetMouseDragDelta(0);
|
||||
|
||||
if (FMath::Abs(Drag.x) > Config->DragPauseSensitivity)
|
||||
{
|
||||
FCogDebugPlot::Pause = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,43 +443,46 @@ void FCogEngineWindow_Plots::RenderPlots(const TArray<FCogDebugPlotEntry*>& Visi
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// Draw all the plots assigned to this row
|
||||
// Draw all the plots assigned to this graph
|
||||
//-----------------------------------------------------------
|
||||
for (FCogDebugPlotEntry* PlotPtr : VisiblePlots)
|
||||
for (FCogEngineConfig_Plots_GraphEntryInfo& Entry : GraphInfo.Entries)
|
||||
{
|
||||
if (PlotPtr == nullptr)
|
||||
{ continue; }
|
||||
FCogDebugHistory* History = FCogDebugPlot::FindEntry(Entry.Name);
|
||||
if (History == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FCogDebugPlotEntry& Plot = *PlotPtr;
|
||||
if (Plot.GraphIndex != PlotIndex)
|
||||
{ continue; }
|
||||
|
||||
ImPlot::SetAxis(Plot.YAxis);
|
||||
ImPlot::SetAxis(Entry.YAxis);
|
||||
|
||||
ImPlot::SetNextLineStyle(IMPLOT_AUTO_COL);
|
||||
const auto Label = StringCast<ANSICHAR>(*Plot.Name.ToString());
|
||||
const auto Label = StringCast<ANSICHAR>(*Entry.Name.ToString());
|
||||
|
||||
//-------------------------------------------------------
|
||||
// Plot Events
|
||||
//-------------------------------------------------------
|
||||
if (Plot.IsEventPlot)
|
||||
switch (History->Type)
|
||||
{
|
||||
RenderEvents(Plot, Label.Get(), PlotMin, PlotMax);
|
||||
}
|
||||
//-------------------------------------------------------
|
||||
// Plot Values
|
||||
//-------------------------------------------------------
|
||||
else if (Plot.Values.empty() == false)
|
||||
{
|
||||
RenderValues(Plot, Label.Get());
|
||||
case FCogDebugHistoryType::Event:
|
||||
{
|
||||
RenderEvents(*static_cast<FCogDebugEventHistory*>(History), Label.Get(), PlotMin, PlotMax);
|
||||
break;
|
||||
}
|
||||
|
||||
case FCogDebugHistoryType::Value:
|
||||
{
|
||||
RenderValues(*static_cast<FCogDebugValueHistory*>(History), Label.Get());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------
|
||||
// Allow legend item labels to be drag and drop sources
|
||||
//-------------------------------------------------------
|
||||
if (ImPlot::BeginDragDropSourceItem(Label.Get()))
|
||||
{
|
||||
const auto EntryName = StringCast<ANSICHAR>(*Plot.Name.ToString());
|
||||
const auto EntryName = StringCast<ANSICHAR>(*Entry.Name.ToString());
|
||||
ImGui::SetDragDropPayload("DragAndDrop", EntryName.Get(), EntryName.Length() + 1);
|
||||
ImGui::TextUnformatted(EntryName.Get());
|
||||
ImPlot::EndDragDropSource();
|
||||
@@ -467,10 +496,7 @@ void FCogEngineWindow_Plots::RenderPlots(const TArray<FCogDebugPlotEntry*>& Visi
|
||||
{
|
||||
if (const ImGuiPayload* Payload = ImGui::AcceptDragDropPayload("DragAndDrop"))
|
||||
{
|
||||
if (FCogDebugPlotEntry* Plot = FCogDebugPlot::FindEntry(FName((const char*)Payload->Data)))
|
||||
{
|
||||
Plot->AssignGraphAndAxis(PlotIndex, ImAxis_Y1);
|
||||
}
|
||||
AssignToGraphAndAxis(GetDroppedEntryName(Payload), GraphIndex, ImAxis_Y1);
|
||||
}
|
||||
ImPlot::EndDragDropTarget();
|
||||
}
|
||||
@@ -478,16 +504,14 @@ void FCogEngineWindow_Plots::RenderPlots(const TArray<FCogDebugPlotEntry*>& Visi
|
||||
//-------------------------------------------------------
|
||||
// Allow each y-axis to be a drag and drop target
|
||||
//-------------------------------------------------------
|
||||
for (int y = ImAxis_Y1; y <= ImAxis_Y3; ++y)
|
||||
for (int32 YAxisIndex = 0; YAxisIndex < Config->NumYAxis; ++YAxisIndex)
|
||||
{
|
||||
if (ImPlot::BeginDragDropTargetAxis(y))
|
||||
const ImAxis YAxis = ImAxis_Y1 + YAxisIndex;
|
||||
if (ImPlot::BeginDragDropTargetAxis(YAxis))
|
||||
{
|
||||
if (const ImGuiPayload* Payload = ImGui::AcceptDragDropPayload("DragAndDrop"))
|
||||
{
|
||||
if (FCogDebugPlotEntry* Plot = FCogDebugPlot::FindEntry(FName((const char*)Payload->Data)))
|
||||
{
|
||||
Plot->AssignGraphAndAxis(PlotIndex, y);
|
||||
}
|
||||
AssignToGraphAndAxis(GetDroppedEntryName(Payload), GraphIndex, YAxis);
|
||||
}
|
||||
ImPlot::EndDragDropTarget();
|
||||
}
|
||||
@@ -500,20 +524,21 @@ void FCogEngineWindow_Plots::RenderPlots(const TArray<FCogDebugPlotEntry*>& Visi
|
||||
{
|
||||
if (const ImGuiPayload* Payload = ImGui::AcceptDragDropPayload("DragAndDrop"))
|
||||
{
|
||||
if (FCogDebugPlotEntry* Plot = FCogDebugPlot::FindEntry(FName((const char*)Payload->Data)))
|
||||
{
|
||||
Plot->AssignGraphAndAxis(PlotIndex, ImAxis_Y1);
|
||||
}
|
||||
AssignToGraphAndAxis(GetDroppedEntryName(Payload), GraphIndex, ImAxis_Y1);
|
||||
}
|
||||
ImPlot::EndDragDropTarget();
|
||||
}
|
||||
|
||||
ImPlot::EndPlot();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImPlot::EndSubplots();
|
||||
}
|
||||
|
||||
ImPlot::PopStyleVar();
|
||||
|
||||
if (PushPlotBgStyle)
|
||||
{
|
||||
ImPlot::PopStyleColor();
|
||||
@@ -523,8 +548,13 @@ void FCogEngineWindow_Plots::RenderPlots(const TArray<FCogDebugPlotEntry*>& Visi
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogEngineWindow_Plots::RenderValues(FCogDebugPlotEntry& Entry, const char* Label) const
|
||||
void FCogEngineWindow_Plots::RenderValues(FCogDebugValueHistory& Entry, const char* Label) const
|
||||
{
|
||||
if (Entry.Values.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Value at cursor tooltip
|
||||
//----------------------------------------------------------------
|
||||
@@ -568,7 +598,7 @@ void FCogEngineWindow_Plots::RenderValues(FCogDebugPlotEntry& Entry, const char*
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogEngineWindow_Plots::RenderEvents(FCogDebugPlotEntry& Entry, const char* Label, const ImVec2& PlotMin, const ImVec2& PlotMax) const
|
||||
void FCogEngineWindow_Plots::RenderEvents(FCogDebugEventHistory& Entry, const char* Label, const ImVec2& PlotMin, const ImVec2& PlotMax) const
|
||||
{
|
||||
const ImVec2 Mouse = ImGui::GetMousePos();
|
||||
ImDrawList* PlotDrawList = ImPlot::GetPlotDrawList();
|
||||
@@ -581,7 +611,7 @@ void FCogEngineWindow_Plots::RenderEvents(FCogDebugPlotEntry& Entry, const char*
|
||||
ImVector<ImVec2> DummyData;
|
||||
DummyData.push_back(ImVec2(0, 0));
|
||||
DummyData.push_back(ImVec2(0, 8));
|
||||
ImPlot::PlotLine(Label, &DummyData[0].x, &DummyData[0].y, DummyData.size(), Entry.ValueOffset, 2 * sizeof(float));
|
||||
ImPlot::PlotLine(Label, &DummyData[0].x, &DummyData[0].y, DummyData.size(), Entry.EventOffset, 2 * sizeof(float));
|
||||
|
||||
const FCogDebugPlotEvent* HoveredEvent = nullptr;
|
||||
|
||||
@@ -641,7 +671,7 @@ void FCogEngineWindow_Plots::RenderEvents(FCogDebugPlotEntry& Entry, const char*
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogEngineWindow_Plots::RenderEventTooltip(const FCogDebugPlotEvent* HoveredEvent, const FCogDebugPlotEntry& Entry)
|
||||
void FCogEngineWindow_Plots::RenderEventTooltip(const FCogDebugPlotEvent* HoveredEvent, const FCogDebugHistory& Entry)
|
||||
{
|
||||
if (ImPlot::IsPlotHovered() && HoveredEvent != nullptr)
|
||||
{
|
||||
@@ -716,3 +746,54 @@ void FCogEngineWindow_Plots::RenderEventTooltip(const FCogDebugPlotEvent* Hovere
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FName FCogEngineWindow_Plots::GetDroppedEntryName(const ImGuiPayload* Payload)
|
||||
{
|
||||
return FName(static_cast<const char*>(Payload->Data));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogEngineWindow_Plots::AssignToGraphAndAxis(const FName InName, const int32 InGraphIndex, const ImAxis InYAxis)
|
||||
{
|
||||
UnassignToGraphAndAxis(InName);
|
||||
|
||||
FCogDebugHistory* History = FCogDebugPlot::FindEntry(InName);
|
||||
if (History == nullptr)
|
||||
{ return; }
|
||||
|
||||
History->GraphIndex = InGraphIndex;
|
||||
|
||||
FCogEngineConfig_Plots_GraphInfo& GraphInfo = Config->Graphs[InGraphIndex];
|
||||
|
||||
FCogEngineConfig_Plots_GraphEntryInfo* CorrespondingEntry = GraphInfo.Entries.FindByPredicate(
|
||||
[InName](const auto& InEntry) { return InEntry.Name == InName; });
|
||||
|
||||
if (CorrespondingEntry == nullptr)
|
||||
{
|
||||
GraphInfo.Entries.Add({InName, InYAxis});
|
||||
}
|
||||
else
|
||||
{
|
||||
CorrespondingEntry->YAxis = InYAxis;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void FCogEngineWindow_Plots::UnassignToGraphAndAxis(const FName InName)
|
||||
{
|
||||
const FCogDebugHistory* History = FCogDebugPlot::FindEntry(InName);
|
||||
if (History == nullptr)
|
||||
{ return; }
|
||||
|
||||
FCogEngineConfig_Plots_GraphInfo& GraphInfo = Config->Graphs[History->GraphIndex];
|
||||
|
||||
const int32 Index = GraphInfo.Entries.IndexOfByPredicate([InName](const auto& InEntry) { return InEntry.Name == InName; });
|
||||
if (Index != INDEX_NONE)
|
||||
{
|
||||
GraphInfo.Entries.RemoveAt(Index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,13 +3,17 @@
|
||||
#include "CoreMinimal.h"
|
||||
#include "CogCommonConfig.h"
|
||||
#include "CogWindow.h"
|
||||
#include "implot.h"
|
||||
#include "CogEngineWindow_Plots.generated.h"
|
||||
|
||||
struct FCogDebugEventHistory;
|
||||
struct FCogDebugValueHistory;
|
||||
struct ImVec2;
|
||||
struct FCogDebugPlotEvent;
|
||||
struct FCogDebugPlotEntry;
|
||||
struct FCogDebugHistory;
|
||||
class UCogEngineConfig_Plots;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
class COGENGINE_API FCogEngineWindow_Plots : public FCogWindow
|
||||
{
|
||||
typedef FCogWindow Super;
|
||||
@@ -28,23 +32,53 @@ protected:
|
||||
|
||||
virtual void RenderAllEntriesNames(const ImVec2& InSize);
|
||||
|
||||
virtual void RenderEntryName(const int Index, FCogDebugPlotEntry& Entry);
|
||||
virtual void RenderEntryName(const int Index, FCogDebugHistory& Entry);
|
||||
|
||||
virtual void RenderPlots(const TArray<FCogDebugPlotEntry*>& VisiblePlots) const;
|
||||
virtual void RenderPlots();
|
||||
|
||||
virtual void RenderMenu();
|
||||
|
||||
virtual void RenderValues(FCogDebugPlotEntry& Entry, const char* Label) const;
|
||||
virtual void RenderValues(FCogDebugValueHistory& Entry, const char* Label) const;
|
||||
|
||||
virtual void RenderEvents(FCogDebugPlotEntry& Entry, const char* Label, const ImVec2& PlotMin, const ImVec2& PlotMax) const;
|
||||
virtual void RenderEvents(FCogDebugEventHistory& Entry, const char* Label, const ImVec2& PlotMin, const ImVec2& PlotMax) const;
|
||||
|
||||
static void RenderEventTooltip(const FCogDebugPlotEvent* HoveredEvent, const FCogDebugPlotEntry& Entry);
|
||||
static void RenderEventTooltip(const FCogDebugPlotEvent* HoveredEvent, const FCogDebugHistory& Entry);
|
||||
|
||||
virtual void AssignToGraphAndAxis(const FName InName, const int32 InGraphIndex, const ImAxis InYAxis);
|
||||
|
||||
virtual void UnassignToGraphAndAxis(const FName InName);
|
||||
|
||||
virtual void RefreshPlotSettings();
|
||||
|
||||
static FName GetDroppedEntryName(const ImGuiPayload* Payload);
|
||||
|
||||
TObjectPtr<UCogEngineConfig_Plots> Config = nullptr;
|
||||
|
||||
bool bApplyTimeScale = false;
|
||||
};
|
||||
|
||||
private:
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
USTRUCT()
|
||||
struct FCogEngineConfig_Plots_GraphEntryInfo
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(Config)
|
||||
FName Name;
|
||||
|
||||
UPROPERTY(Config)
|
||||
int32 YAxis = 0;
|
||||
};
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
USTRUCT()
|
||||
struct FCogEngineConfig_Plots_GraphInfo
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(Config)
|
||||
TArray<FCogEngineConfig_Plots_GraphEntryInfo> Entries;
|
||||
|
||||
};
|
||||
|
||||
@@ -56,15 +90,23 @@ class UCogEngineConfig_Plots : public UCogCommonConfig
|
||||
|
||||
public:
|
||||
|
||||
static constexpr int32 MaxNumGraphs = 5;
|
||||
|
||||
UPROPERTY(Config)
|
||||
int NumGraphs = 1;
|
||||
|
||||
UPROPERTY(Config)
|
||||
int NumYAxis = 1;
|
||||
|
||||
UPROPERTY(Config)
|
||||
bool RecordValuesWhenPaused = true;
|
||||
|
||||
UPROPERTY(Config)
|
||||
float TimeRange = 20.0f;
|
||||
|
||||
UPROPERTY(Config)
|
||||
int32 NumRecordedValues = 2000;
|
||||
|
||||
UPROPERTY(Config)
|
||||
bool ShowTimeBarAtGameTime = true;
|
||||
|
||||
@@ -83,15 +125,29 @@ public:
|
||||
UPROPERTY(Config)
|
||||
bool DockEntries = false;
|
||||
|
||||
UPROPERTY(Config)
|
||||
float AutoFitPadding = 0.1f;
|
||||
|
||||
UPROPERTY(Config)
|
||||
FCogEngineConfig_Plots_GraphInfo Graphs[MaxNumGraphs];
|
||||
|
||||
virtual void Reset() override
|
||||
{
|
||||
NumGraphs = 1;
|
||||
TimeRange = 20.0f;
|
||||
RecordValuesWhenPaused = true;
|
||||
ShowTimeBarAtGameTime = true;
|
||||
ShowTimeBarAtCursor = true;
|
||||
ShowValueAtCursor = true;
|
||||
DragPauseSensitivity = 10.0f;
|
||||
PauseBackgroundColor = FColor(10, 0, 0, 255);
|
||||
DockEntries = false;
|
||||
NumRecordedValues = 2000;
|
||||
AutoFitPadding = 0.1f;
|
||||
|
||||
for (FCogEngineConfig_Plots_GraphInfo& Graph : Graphs)
|
||||
{
|
||||
Graph.Entries.Empty();
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user