Files

338 lines
13 KiB
C++
Raw Permalink Normal View History

// Copyright Epic Games, Inc. All Rights Reserved.
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
#include "VisualLoggerCanvasRenderer.h"
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
#include "EngineGlobals.h"
#include "CanvasItem.h"
#include "Engine/Canvas.h"
#include "Engine/Engine.h"
#include "SceneView.h"
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
#include "VisualLogger/VisualLogger.h"
#include "LogVisualizerSettings.h"
#include "LogVisualizerSessionSettings.h"
#include "VisualLoggerDatabase.h"
#include "LogVisualizerPublic.h"
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
#include "VisualLoggerTimeSliderController.h"
#include "Debug/ReporterGraph.h"
Copying //UE4/Dev-Sequencer to //UE4/Main ========================== MAJOR FEATURES + CHANGES ========================== Change 2859626 on 2016/02/08 by Max.Preussner Editor: Added SaveAs functionality to content asset editors Change 2859666 on 2016/02/08 by Max.Chen Sequencer: Fix crash in CheckForWorldGCLeaks when loading a new map because spawnables are left behind. #jira UE-25616 Change 2859685 on 2016/02/08 by Max.Chen Sequencer: Add prompt to save sub level sequences if they are dirty #jira UE-26510 Change 2859715 on 2016/02/08 by Thomas.Sarkanen Adding actor spawning recording Actors are queued for record on spawn then added to the list like manually-specifed ones. Changed almost everything about UActorRecording. We now record on a per-component basis, with property tracks encapsulated in each actor recording. Much effort is expended to make sure that the correct components are owned by their respective actors, as we can add and remove components at runtime, but they must be created up-front in the UMovieScene Blueprints. We go as far as to add our own SCS nodes to make sure components are correctly spawned. Fixed infinite loop in FSequencer::AddSpawnable. Fixed visibility track instance to work with scene components as well as actors. Fixed particle track instance to work on UParticleSystemComponent rather than just AEmitters. Added particle recorder. Moved animation recording into an animation property recorder rather than having it as a special case. This still uses the animation recorder under the hood. Moved old-style Matinee animation control into FMovieSceneSkeletalAnimationTrackInstance & made this work on USkeletalMeshComponents directly, rather than via the old interface. Exposed SetMatineeAnimPositionInner and PreviewMatineeSetAnimPositionInner in FAnimMontageInstance so those utility functions can be used externally to Engine. Added a predicate version of UMovieScene::FindPossessable. Exposed UMovieSceneParticleSection::AddKey externally via MOVIESCENETRACKS_API so I can programmatically add keys. Fixed a crash in FScalableFloatDetails::CustomizeHeader when selecting PIE projectiles in Orion. Moved all recorders over to recording Actors or Components & store UObjects instead of AActors. Allowed skeletal animation tracks on components as well as actors. Change 2862675 on 2016/02/10 by Max.Chen Sequencer: Add option to link the sequencer curve editor with the sequencer timeline. Under General Options->Link Curve Editor Time Range. The default is false, so the sequencer and curve editor have separate time ranges. #jira UE-25933 Change 2862699 on 2016/02/11 by Max.Chen Sequencer: Added a playback status of jumping which the AudioTrack and Skeletal Mesh Track (anim notifies) ignores for updates. This is used to updating thumbnail at certain times. #jira UE-26447, UE-26671 Change 2862712 on 2016/02/11 by Max.Chen Sequencer: Fix spawnables firing off their particles. Disable auto activate on spawnable components #jira UE-26390 Change 2862719 on 2016/02/11 by Max.Preussner Editor: Refactored detail customizations for colors, rotators, vectors - broke color and rotator customizations out into their own files - added vector customizations (placeholder) - cleaned up localization namespaces, forward declarations Change 2866454 on 2016/02/14 by Max.Preussner Sequencer: Removed ULevelEditorSequencerSettings; moved default settings into INI Change 2866455 on 2016/02/14 by Thomas.Sarkanen Sequence recorder can now record replays Added extra edtior-only UI to the replay playback controls to record sequences. Curretnly very placeholder: only records the entire sequence and provides no feedback in the UI if it is recording. Fixed bindings to recorded objects not working in various circumstances. Added the ability to manually create a binding. Recompiled actor blueprints post-record if we added components. Fixed a null ptr dereference in FOrionTeamUIInfo::Update. Removed tolerances when reducing tracks - they are now 'very small'. Added actor filter so actors of certain classes can be recorded. Change 2866458 on 2016/02/14 by Max.Chen Sequencer: Fix anim notifies that fire at shot cuts. Anim notifies are fired from the last position to the current position. When jumping cuts, we want the delta to be 0 so that the anim notifies before the shot are not fired off. #jira UE-26390, UE-26671 Change 2866459 on 2016/02/14 by Max.Chen Sequencer: Add option to toggle visibility of combined keys Change 2866466 on 2016/02/14 by Frank.Fella Sequencer - Add a track for controlling streamed level visibilty and remove visibility code from the master level blueprint. Change 2866470 on 2016/02/14 by Max.Chen Sequencer: Add return value to indicate data has changed when a section has been added. This fixes a bug where creating a new section doesn't seem to add a key. #jira UE-26837 Change 2866481 on 2016/02/14 by Max.Preussner Sequencer: Implemented Presets for adding tracks automatically based on actor type (UE-24513) #Jira: UE-24513 Change 2866482 on 2016/02/14 by Max.Chen Sequencer: Allow for any actor that has a camera component to be a camera cut. #jira UE-26777 Change 2866484 on 2016/02/14 by Thomas.Sarkanen Added in/out times to sequence recording Also added the optional ability to record different actor types (heroes, projectiles, minions). Change 2866495 on 2016/02/14 by Max.Chen Sequencer: Need to limit camera control to the section bounds of the camera cut otherwise, control won't be relinquished back to player at the end of the playback. #jira UE-26886 [CL 2874647 by Max Chen in Main branch]
2016-02-19 21:36:27 -05:00
namespace LogVisualizer
{
FORCEINLINE bool PointInFrustum(UCanvas* Canvas, const FVector& Location)
{
return Canvas->SceneView->ViewFrustum.IntersectBox(Location, FVector::ZeroVector);
}
FORCEINLINE void DrawText(UCanvas* Canvas, UFont* Font, const FString& TextToDraw, const FVector& WorldLocation)
{
if (PointInFrustum(Canvas, WorldLocation))
{
const FVector3f ScreenLocation = UE::LWC::NarrowWorldPositionChecked(Canvas->Project(WorldLocation));
Canvas->DrawText(Font, *TextToDraw, ScreenLocation.X, ScreenLocation.Y);
}
}
FORCEINLINE void DrawTextCentered(UCanvas* Canvas, UFont* Font, const FString& TextToDraw, const FVector& WorldLocation)
{
if (PointInFrustum(Canvas, WorldLocation))
{
const FVector3f ScreenLocation = UE::LWC::NarrowWorldPositionChecked(Canvas->Project(WorldLocation));
float TextXL = 0.f;
float TextYL = 0.f;
Canvas->StrLen(Font, TextToDraw, TextXL, TextYL);
Canvas->DrawText(Font, *TextToDraw, ScreenLocation.X - TextXL / 2.0f, ScreenLocation.Y - TextYL / 2.0f);
}
}
FORCEINLINE void DrawTextShadowed(UCanvas* Canvas, UFont* Font, const FString& TextToDraw, const FVector& WorldLocation)
{
if (PointInFrustum(Canvas, WorldLocation))
{
const FVector3f ScreenLocation = UE::LWC::NarrowWorldPositionChecked(Canvas->Project(WorldLocation));
float TextXL = 0.f;
float TextYL = 0.f;
Canvas->StrLen(Font, TextToDraw, TextXL, TextYL);
Canvas->SetDrawColor(FColor::Black);
Canvas->DrawText(Font, *TextToDraw, 1 + ScreenLocation.X - TextXL / 2.0f, 1 + ScreenLocation.Y - TextYL / 2.0f);
Canvas->SetDrawColor(FColor::White);
Canvas->DrawText(Font, *TextToDraw, ScreenLocation.X - TextXL / 2.0f, ScreenLocation.Y - TextYL / 2.0f);
}
}
}
FVisualLoggerCanvasRenderer::FVisualLoggerCanvasRenderer()
: bDirtyData(true)
{
FLogVisualizer::Get().GetEvents().OnFiltersChanged.AddRaw(this, &FVisualLoggerCanvasRenderer::DirtyCachedData);
FVisualLoggerDatabase::Get().GetEvents().OnRowSelectionChanged.AddRaw(this, &FVisualLoggerCanvasRenderer::ObjectSelectionChanged);
FVisualLoggerDatabase::Get().GetEvents().OnItemSelectionChanged.AddRaw(this, &FVisualLoggerCanvasRenderer::OnItemSelectionChanged);
}
FVisualLoggerCanvasRenderer::~FVisualLoggerCanvasRenderer()
{
FLogVisualizer::Get().GetEvents().OnFiltersChanged.RemoveAll(this);
FVisualLoggerDatabase::Get().GetEvents().OnRowSelectionChanged.RemoveAll(this);
FVisualLoggerDatabase::Get().GetEvents().OnItemSelectionChanged.RemoveAll(this);
}
void FVisualLoggerCanvasRenderer::ResetData()
{
SelectedEntry = FVisualLogEntry();
DirtyCachedData();
}
void FVisualLoggerCanvasRenderer::OnItemSelectionChanged(const FVisualLoggerDBRow& ChangedRow, int32 SelectedItemIndex)
{
SelectedEntry = ChangedRow.GetCurrentItemIndex() != INDEX_NONE ? ChangedRow.GetCurrentItem().Entry : FVisualLogEntry();
DirtyCachedData();
}
void FVisualLoggerCanvasRenderer::ObjectSelectionChanged(const TArray<FName>& RowNames)
{
DirtyCachedData();
}
void FVisualLoggerCanvasRenderer::DrawOnCanvas(class UCanvas* Canvas, class APlayerController*)
{
if (GEngine == NULL)
{
return;
}
UWorld* World = FLogVisualizer::Get().GetWorld();
if (World == NULL)
{
return;
}
UFont* Font = GEngine->GetSmallFont();
FCanvasTextItem TextItem(FVector2D::ZeroVector, FText::GetEmpty(), Font, FLinearColor::White);
const FString TimeStampString = FString::Printf(TEXT("%.2f"), SelectedEntry.TimeStamp);
LogVisualizer::DrawTextShadowed(Canvas, Font, TimeStampString, SelectedEntry.Location);
if (bDirtyData && FLogVisualizer::Get().GetTimeSliderController().IsValid())
{
const FVisualLoggerTimeSliderArgs& TimeSliderArgs = FLogVisualizer::Get().GetTimeSliderController()->GetTimeSliderArgs();
TRange<double> LocalViewRange = TimeSliderArgs.ViewRange.Get();
const double LocalViewRangeMin = LocalViewRange.GetLowerBoundValue();
const double LocalViewRangeMax = LocalViewRange.GetUpperBoundValue();
const double LocalSequenceLength = LocalViewRangeMax - LocalViewRangeMin;
const double WindowHalfWidth = LocalSequenceLength * TimeSliderArgs.CursorSize.Get() * 0.5;
const FVector2D TimeStampWindow(SelectedEntry.TimeStamp - WindowHalfWidth, SelectedEntry.TimeStamp + WindowHalfWidth);
CollectedGraphs.Reset();
const TArray<FName>& RowNames = FVisualLoggerDatabase::Get().GetSelectedRows();
for (auto RowName : RowNames)
{
if (FVisualLoggerDatabase::Get().IsRowVisible(RowName) == false)
{
continue;
}
const TArray<FVisualLoggerGraph>& AllGraphs = FVisualLoggerGraphsDatabase::Get().GetGraphsByOwnerName(RowName);
for (const FVisualLoggerGraph& CurrentGraph : AllGraphs)
{
const FName GraphName = CurrentGraph.GetGraphName();
const FName OwnerName = CurrentGraph.GetOwnerName();
if (FVisualLoggerGraphsDatabase::Get().IsGraphVisible(OwnerName, GraphName) == false)
{
continue;
}
for (auto DataIt(CurrentGraph.GetConstDataIterator()); DataIt; ++DataIt)
{
const FVisualLoggerGraphData& GraphData = *DataIt;
const bool bIsGraphDataDisabled = FVisualLoggerFilters::Get().IsGraphDataDisabled(GraphName, GraphData.DataName);
if (bIsGraphDataDisabled)
{
continue;
}
const FName FullGraphName = *FString::Printf(TEXT("%s$%s"), *RowName.ToString(), *GraphName.ToString());
FGraphData &CollectedGraphData = CollectedGraphs.FindOrAdd(FullGraphName);
FGraphLineData &LineData = CollectedGraphData.GraphLines.FindOrAdd(GraphData.DataName);
LineData.DataName = GraphData.DataName;
LineData.Samples = GraphData.Samples;
LineData.LeftExtreme = FVector2D::ZeroVector;
LineData.RightExtreme = FVector2D::ZeroVector;
int32 LeftSideOutsideIndex = INDEX_NONE;
int32 RightSideOutsideIndex = INDEX_NONE;
for (int32 SampleIndex = 0; SampleIndex < GraphData.Samples.Num(); SampleIndex++)
{
const FVector2D& SampleValue = GraphData.Samples[SampleIndex];
CollectedGraphData.Min.X = FMath::Min(CollectedGraphData.Min.X, SampleValue.X);
CollectedGraphData.Min.Y = FMath::Min(CollectedGraphData.Min.Y, SampleValue.Y);
CollectedGraphData.Max.X = FMath::Max(CollectedGraphData.Max.X, SampleValue.X);
CollectedGraphData.Max.Y = FMath::Max(CollectedGraphData.Max.Y, SampleValue.Y);
const double CurrentTimeStamp = GraphData.TimeStamps[SampleIndex];
if (CurrentTimeStamp < TimeStampWindow.X)
{
LineData.LeftExtreme = SampleValue;
}
else if (CurrentTimeStamp > TimeStampWindow.Y)
{
LineData.RightExtreme = SampleValue;
break;
}
}
}
}
}
bDirtyData = false;
}
if (ULogVisualizerSessionSettings::StaticClass()->GetDefaultObject<ULogVisualizerSessionSettings>()->bEnableGraphsVisualization)
{
DrawHistogramGraphs(Canvas, NULL);
}
const TMap<FName, FVisualLogExtensionInterface*>& Extensions = FVisualLogger::Get().GetAllExtensions();
for (const auto& CurrentExtension : Extensions)
{
CurrentExtension.Value->DrawData(FVisualLoggerEditorInterface::Get(), Canvas);
}
}
void FVisualLoggerCanvasRenderer::DrawHistogramGraphs(class UCanvas* Canvas, class APlayerController*)
{
if (FLogVisualizer::Get().GetTimeSliderController().IsValid() == false)
{
return;
}
const float GoldenRatioConjugate = 0.618033988749895f;
if (CollectedGraphs.Num() > 0)
{
const FVisualLoggerTimeSliderArgs& TimeSliderArgs = FLogVisualizer::Get().GetTimeSliderController()->GetTimeSliderArgs();
TRange<double> LocalViewRange = TimeSliderArgs.ViewRange.Get();
const double LocalViewRangeMin = LocalViewRange.GetLowerBoundValue();
const double LocalViewRangeMax = LocalViewRange.GetUpperBoundValue();
const double LocalSequenceLength = LocalViewRangeMax - LocalViewRangeMin;
const double WindowHalfWidth = LocalSequenceLength * TimeSliderArgs.CursorSize.Get() * 0.5f;
const FVector2D TimeStampWindow(SelectedEntry.TimeStamp - WindowHalfWidth, SelectedEntry.TimeStamp + WindowHalfWidth);
const FColor GraphsBackgroundColor = ULogVisualizerSettings::StaticClass()->GetDefaultObject<ULogVisualizerSettings>()->GraphsBackgroundColor;
const int NumberOfGraphs = CollectedGraphs.Num();
const int32 NumberOfColumns = FMath::CeilToInt(FMath::Sqrt(static_cast<float>(NumberOfGraphs)));
int32 NumberOfRows = FMath::FloorToInt((float)NumberOfGraphs / (float)NumberOfColumns);
if (NumberOfGraphs - NumberOfRows * NumberOfColumns > 0)
{
NumberOfRows += 1;
}
const int32 MaxNumberOfGraphs = FMath::Max(NumberOfRows, NumberOfColumns);
const float GraphWidth = 0.8f / NumberOfColumns;
const float GraphHeight = 0.8f / NumberOfRows;
const float XGraphSpacing = 0.2f / (MaxNumberOfGraphs + 1);
const float YGraphSpacing = 0.2f / (MaxNumberOfGraphs + 1);
const float StartX = XGraphSpacing;
float StartY = 0.5f + (0.5f * NumberOfRows - 1) * (GraphHeight + YGraphSpacing);
float CurrentX = StartX;
float CurrentY = StartY;
int32 GraphIndex = 0;
int32 CurrentColumn = 0;
int32 CurrentRow = 0;
bool bDrawExtremesOnGraphs = ULogVisualizerSettings::StaticClass()->GetDefaultObject<ULogVisualizerSettings>()->bDrawExtremesOnGraphs;
const bool bConstrainGraphToLocalMinMax = ULogVisualizerSettings::StaticClass()->GetDefaultObject<ULogVisualizerSettings>()->bConstrainGraphToLocalMinMax;
for (auto It(CollectedGraphs.CreateIterator()); It; ++It)
{
TWeakObjectPtr<UReporterGraph> HistogramGraph = Canvas->GetReporterGraph();
if (!HistogramGraph.IsValid())
{
break;
}
HistogramGraph->SetNumGraphLines(It->Value.GraphLines.Num());
int32 LineIndex = 0;
UFont* Font = GEngine->GetSmallFont();
int32 MaxStringSize = 0;
float Hue = 0;
auto& CategoriesForGraph = UsedGraphCategories.FindOrAdd(It->Key.ToString());
It->Value.GraphLines.KeySort(FNameLexicalLess());
if (bConstrainGraphToLocalMinMax)
{
It->Value.Min.Y = FLT_MAX;
It->Value.Max.Y = -FLT_MAX;
}
for (auto LinesIt(It->Value.GraphLines.CreateConstIterator()); LinesIt; ++LinesIt)
{
const FString DataName = LinesIt->Value.DataName.ToString();
int32 CategoryIndex = CategoriesForGraph.Find(DataName);
if (CategoryIndex == INDEX_NONE)
{
CategoryIndex = CategoriesForGraph.AddUnique(DataName);
}
Hue = CategoryIndex * GoldenRatioConjugate;
if (Hue > 1)
{
Hue -= FMath::FloorToFloat(Hue);
}
HistogramGraph->GetGraphLine(LineIndex)->Color = FLinearColor::MakeFromHSV8(static_cast<uint8>(Hue * 255), 200, 244);
HistogramGraph->GetGraphLine(LineIndex)->LineName = DataName;
HistogramGraph->GetGraphLine(LineIndex)->Data.Append(LinesIt->Value.Samples);
HistogramGraph->GetGraphLine(LineIndex)->LeftExtreme = LinesIt->Value.LeftExtreme;
HistogramGraph->GetGraphLine(LineIndex)->RightExtreme = LinesIt->Value.RightExtreme;
int32 DummyY, StringSizeX;
StringSize(Font, StringSizeX, DummyY, *LinesIt->Value.DataName.ToString());
MaxStringSize = StringSizeX > MaxStringSize ? StringSizeX : MaxStringSize;
++LineIndex;
if (bConstrainGraphToLocalMinMax)
{
for (const FVector2D& SampleData : LinesIt->Value.Samples)
{
if (SampleData.X >= TimeStampWindow.X && SampleData.X <= TimeStampWindow.Y)
{
It->Value.Min.Y = FMath::Min(It->Value.Min.Y, SampleData.Y);
It->Value.Max.Y = FMath::Max(It->Value.Max.Y, SampleData.Y);
}
}
}
}
FVector2D GraphSpaceSize;
GraphSpaceSize.Y = GraphSpaceSize.X = 0.8 / CollectedGraphs.Num();
HistogramGraph->SetGraphScreenSize(CurrentX, CurrentX + GraphWidth, CurrentY, CurrentY + GraphHeight);
CurrentX += GraphWidth + XGraphSpacing;
HistogramGraph->SetAxesMinMax(FVector2D(TimeStampWindow.X, It->Value.Min.Y), FVector2D(TimeStampWindow.Y, It->Value.Max.Y));
HistogramGraph->DrawCursorOnGraph(true);
HistogramGraph->UseTinyFont(CollectedGraphs.Num() >= 5);
HistogramGraph->SetCursorLocation(FloatCastChecked<float>(SelectedEntry.TimeStamp, /* Precision */ 1./16.));
HistogramGraph->SetNumThresholds(0);
HistogramGraph->SetStyles(EGraphAxisStyle::Grid, EGraphDataStyle::Lines);
HistogramGraph->SetBackgroundColor(GraphsBackgroundColor);
HistogramGraph->SetLegendPosition(/*bShowHistogramLabelsOutside*/ false ? ELegendPosition::Outside : ELegendPosition::Inside);
HistogramGraph->OffsetDataSets(/*bOffsetDataSet*/false);
HistogramGraph->DrawExtremesOnGraph(bDrawExtremesOnGraphs);
HistogramGraph->bVisible = true;
HistogramGraph->Draw(Canvas);
++GraphIndex;
if (++CurrentColumn >= NumberOfColumns)
{
CurrentColumn = 0;
CurrentRow++;
CurrentX = StartX;
CurrentY -= GraphHeight + YGraphSpacing;
}
}
}
}