Skip to content
Snippets Groups Projects
Commit 067a2e94 authored by Timon Römer's avatar Timon Römer
Browse files

Adds Redo and Erase

parent 47a868cf
Branches
No related tags found
No related merge requests found
Showing
with 252 additions and 58 deletions
File added
No preview for this file type
File added
File added
No preview for this file type
File added
File added
#include "Utilities.h"
#include "DensityField.h"
#include "MetaCastBachelor/PointStorage/DensityField.h"
void FUtilities::QuickSort(TArray<float>& Array, const int32 Begin, const int32 End)
{
......
#pragma once
#include "DensityField.h"
#include "MetaCastBachelor/PointStorage/DensityField.h"
class FUtilities
......
#include "DensityField.h"
#include "PointCloud.h"
#include "Utilities.h"
#include "Math/UnrealMathUtility.h"
#include "MetaCastBachelor/General/Utilities.h"
// INITIALIZATION FUNCTIONS
......@@ -15,7 +15,7 @@ void FDensityField::InitializeDensityField(const APointCloud* PointCloud, const
InitializeDataStructures(MinBounds, MaxBounds, AxisNumbers.X, AxisNumbers.Y, AxisNumbers.Z);
VoxelPointLookupTable = new FVoxelPointLookupTable();
VoxelPointLookupTable->Initialize(PointCloud->PositionVectors, this);
VoxelPointLookupTable->Initialize(PointCloud->GetPositionVectors(), this);
//CalculateVoxelDensities_2(PointCloud, PointCloud->InfluenceRadius);
CalculateVoxelDensitiesSimple(PointCloud, PointCloud->InfluenceRadius);
......@@ -67,14 +67,14 @@ void FDensityField::CalculateVoxelDensities(const APointCloud* PointCloud, const
UE_LOG(LogTemp, Log, TEXT("Cleared previous densities."));
// Iterate over each particle
for (const FVector& ParticlePosition : PointCloud->PositionVectors)
for (const FVector& ParticlePosition : PointCloud->GetPositionVectors())
{
const int32 StartX = FMath::Max(0, FMath::FloorToInt((ParticlePosition.X - InfluenceRadius - PointCloud->MinBounds.X) / XStep));
const int32 EndX = FMath::Min(XNum - 1, FMath::FloorToInt((ParticlePosition.X + InfluenceRadius - PointCloud->MinBounds.X) / XStep));
const int32 StartY = FMath::Max(0, FMath::FloorToInt((ParticlePosition.Y - InfluenceRadius - PointCloud->MinBounds.Y) / YStep));
const int32 EndY = FMath::Min(YNum - 1, FMath::FloorToInt((ParticlePosition.Y + InfluenceRadius - PointCloud->MinBounds.Y) / YStep));
const int32 StartZ = FMath::Max(0, FMath::FloorToInt((ParticlePosition.Z - InfluenceRadius - PointCloud->MinBounds.Z) / ZStep));
const int32 EndZ = FMath::Min(ZNum - 1, FMath::FloorToInt((ParticlePosition.Z + InfluenceRadius - PointCloud->MinBounds.Z) / ZStep));
const int32 StartX = FMath::Max(0, FMath::FloorToInt((ParticlePosition.X - InfluenceRadius - PointCloud->GetMinBounds().X) / XStep));
const int32 EndX = FMath::Min(XNum - 1, FMath::FloorToInt((ParticlePosition.X + InfluenceRadius - PointCloud->GetMinBounds().X) / XStep));
const int32 StartY = FMath::Max(0, FMath::FloorToInt((ParticlePosition.Y - InfluenceRadius - PointCloud->GetMinBounds().Y) / YStep));
const int32 EndY = FMath::Min(YNum - 1, FMath::FloorToInt((ParticlePosition.Y + InfluenceRadius - PointCloud->GetMinBounds().Y) / YStep));
const int32 StartZ = FMath::Max(0, FMath::FloorToInt((ParticlePosition.Z - InfluenceRadius - PointCloud->GetMinBounds().Z) / ZStep));
const int32 EndZ = FMath::Min(ZNum - 1, FMath::FloorToInt((ParticlePosition.Z + InfluenceRadius - PointCloud->GetMinBounds().Z) / ZStep));
for (int32 Z = StartZ; Z <= EndZ; ++Z)
{
......@@ -135,7 +135,7 @@ void FDensityField::CalculateVoxelDensitiesSimple(const APointCloud* PointCloud,
UE_LOG(LogTemp, Log, TEXT("Cleared previous densities."));
// Iterate over each particle
for (const FVector& ParticlePosition : PointCloud->PositionVectors)
for (const FVector& ParticlePosition : PointCloud->GetPositionVectors())
{
// Calculate which voxels the particle influences
const int32 StartX = FMath::Max(0, static_cast<int32>((ParticlePosition.X - InfluenceRadius - GridOrigin.X) / XStep));
......@@ -258,14 +258,14 @@ void FDensityField::CalculateVoxelDensities_2(const APointCloud* PointCloud, con
void FDensityField::SetInitialDensityEstimates(const APointCloud* PointCloud, const float InfluenceRadius)
{
// Iterate over each particle
for (const FVector& ParticlePosition : PointCloud->PositionVectors)
for (const FVector& ParticlePosition : PointCloud->GetPositionVectors())
{
const int32 StartX = FMath::Max(0, FMath::FloorToInt((ParticlePosition.X - InfluenceRadius - PointCloud->MinBounds.X) / XStep));
const int32 EndX = FMath::Min(XNum - 1, FMath::FloorToInt((ParticlePosition.X + InfluenceRadius - PointCloud->MinBounds.X) / XStep));
const int32 StartY = FMath::Max(0, FMath::FloorToInt((ParticlePosition.Y - InfluenceRadius - PointCloud->MinBounds.Y) / YStep));
const int32 EndY = FMath::Min(YNum - 1, FMath::FloorToInt((ParticlePosition.Y + InfluenceRadius - PointCloud->MinBounds.Y) / YStep));
const int32 StartZ = FMath::Max(0, FMath::FloorToInt((ParticlePosition.Z - InfluenceRadius - PointCloud->MinBounds.Z) / ZStep));
const int32 EndZ = FMath::Min(ZNum - 1, FMath::FloorToInt((ParticlePosition.Z + InfluenceRadius - PointCloud->MinBounds.Z) / ZStep));
const int32 StartX = FMath::Max(0, FMath::FloorToInt((ParticlePosition.X - InfluenceRadius - PointCloud->GetMinBounds().X) / XStep));
const int32 EndX = FMath::Min(XNum - 1, FMath::FloorToInt((ParticlePosition.X + InfluenceRadius - PointCloud->GetMinBounds().X) / XStep));
const int32 StartY = FMath::Max(0, FMath::FloorToInt((ParticlePosition.Y - InfluenceRadius - PointCloud->GetMinBounds().Y) / YStep));
const int32 EndY = FMath::Min(YNum - 1, FMath::FloorToInt((ParticlePosition.Y + InfluenceRadius - PointCloud->GetMinBounds().Y) / YStep));
const int32 StartZ = FMath::Max(0, FMath::FloorToInt((ParticlePosition.Z - InfluenceRadius - PointCloud->GetMinBounds().Z) / ZStep));
const int32 EndZ = FMath::Min(ZNum - 1, FMath::FloorToInt((ParticlePosition.Z + InfluenceRadius - PointCloud->GetMinBounds().Z) / ZStep));
for (int32 Z = StartZ; Z <= EndZ; ++Z)
{
......@@ -310,11 +310,11 @@ void FDensityField::CalculateVoxelDensities_2(const APointCloud* PointCloud, con
SetInitialDensityEstimates(PointCloud, InfluenceRadius);
TArray<float> SmoothingLengths;
SmoothingLengths.Init(InfluenceRadius, PointCloud->PositionVectors.Num());
SmoothingLengths.Init(InfluenceRadius, PointCloud->GetPositionVectors().Num());
// Iterate over each particle
int Count = 0;
for (const FVector& ParticlePosition : PointCloud->PositionVectors)
for (const FVector& ParticlePosition : PointCloud->GetPositionVectors())
{
const int VoxelIndex = WorldPositionToIndex(ParticlePosition);
const float TotalVoxelDensity = IndexToVoxelDensity(VoxelIndex);
......@@ -340,15 +340,15 @@ void FDensityField::CalculateVoxelDensities_2(const APointCloud* PointCloud, con
// Iterate over each particle
Count = 0;
for (const FVector& ParticlePosition : PointCloud->PositionVectors)
for (const FVector& ParticlePosition : PointCloud->GetPositionVectors())
{
const float NewInfluenceRadius = SmoothingLengths[Count];
const int32 StartX = FMath::Max(0, FMath::FloorToInt((ParticlePosition.X - NewInfluenceRadius - PointCloud->MinBounds.X) / XStep));
const int32 EndX = FMath::Min(XNum - 1, FMath::FloorToInt((ParticlePosition.X + NewInfluenceRadius - PointCloud->MinBounds.X) / XStep));
const int32 StartY = FMath::Max(0, FMath::FloorToInt((ParticlePosition.Y - NewInfluenceRadius - PointCloud->MinBounds.Y) / YStep));
const int32 EndY = FMath::Min(YNum - 1, FMath::FloorToInt((ParticlePosition.Y + NewInfluenceRadius - PointCloud->MinBounds.Y) / YStep));
const int32 StartZ = FMath::Max(0, FMath::FloorToInt((ParticlePosition.Z - NewInfluenceRadius - PointCloud->MinBounds.Z) / ZStep));
const int32 EndZ = FMath::Min(ZNum - 1, FMath::FloorToInt((ParticlePosition.Z + NewInfluenceRadius - PointCloud->MinBounds.Z) / ZStep));
const int32 StartX = FMath::Max(0, FMath::FloorToInt((ParticlePosition.X - NewInfluenceRadius - PointCloud->GetMinBounds().X) / XStep));
const int32 EndX = FMath::Min(XNum - 1, FMath::FloorToInt((ParticlePosition.X + NewInfluenceRadius - PointCloud->GetMinBounds().X) / XStep));
const int32 StartY = FMath::Max(0, FMath::FloorToInt((ParticlePosition.Y - NewInfluenceRadius - PointCloud->GetMinBounds().Y) / YStep));
const int32 EndY = FMath::Min(YNum - 1, FMath::FloorToInt((ParticlePosition.Y + NewInfluenceRadius - PointCloud->GetMinBounds().Y) / YStep));
const int32 StartZ = FMath::Max(0, FMath::FloorToInt((ParticlePosition.Z - NewInfluenceRadius - PointCloud->GetMinBounds().Z) / ZStep));
const int32 EndZ = FMath::Min(ZNum - 1, FMath::FloorToInt((ParticlePosition.Z + NewInfluenceRadius - PointCloud->GetMinBounds().Z) / ZStep));
for (int32 Z = StartZ; Z <= EndZ; ++Z)
{
......
......@@ -3,15 +3,15 @@
#include "HAL/PlatformFilemanager.h"
#include "GenericPlatform/GenericPlatformFile.h"
PointCloudDataReader::PointCloudDataReader()
FPointCloudDataReader::FPointCloudDataReader()
{
}
PointCloudDataReader::~PointCloudDataReader()
FPointCloudDataReader::~FPointCloudDataReader()
{
}
bool PointCloudDataReader::LoadBytesFromFile(const FString& Filename, TArray<uint8>& OutByteArray)
bool FPointCloudDataReader::LoadBytesFromFile(const FString& Filename, TArray<uint8>& OutByteArray)
{
if (!FFileHelper::LoadFileToArray(OutByteArray, *Filename))
{
......@@ -22,7 +22,7 @@ bool PointCloudDataReader::LoadBytesFromFile(const FString& Filename, TArray<uin
}
TArray<FVector> PointCloudDataReader::LoadPointCloudData(const FString& Filename)
TArray<FVector> FPointCloudDataReader::LoadPointCloudData(const FString& Filename)
{
// Array to store the vectors
TArray<FVector> PointCloud;
......@@ -47,7 +47,7 @@ TArray<FVector> PointCloudDataReader::LoadPointCloudData(const FString& Filename
return PointCloud;
}
TArray<int32> PointCloudDataReader::LoadFlags(const FString& Filename)
TArray<int32> FPointCloudDataReader::LoadFlags(const FString& Filename)
{
TArray<int32> Flags;
TArray<uint8> ByteArray;
......@@ -68,7 +68,7 @@ TArray<int32> PointCloudDataReader::LoadFlags(const FString& Filename)
return Flags;
}
TArray<FVector> PointCloudDataReader::NormalizeData(const TArray<FVector>& Data, const FVector& Center,const FVector& Scale, FVector& OutputMin, FVector& OutputMax)
TArray<FVector> FPointCloudDataReader::NormalizeData(const TArray<FVector>& Data, const FVector& Center,const FVector& Scale, FVector& OutputMin, FVector& OutputMax)
{
FVector Min(FLT_MAX, FLT_MAX, FLT_MAX);
FVector Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
......
......@@ -19,6 +19,7 @@ void APointCloud::BeginPlay()
ReadPointCloudFromFile(PointInputData, FlagInputData);
InitPointCloudVisualizer();
SetupDensityFieldFromPointCloud();
SaveStateAndUpdate();
//UKdtreeBPLibrary::BuildKdtree(MyKdTree, PositionVectors);
//UKdtreeBPLibrary::DumpKdtreeToConsole(MyKdTree);
......@@ -27,8 +28,8 @@ void APointCloud::BeginPlay()
void APointCloud::ReadPointCloudFromFile(const FFilePath FileNamePoints, const FFilePath FileNameFlags)
{
const TArray<FVector> LoadedPointCloud = PointCloudDataReader::LoadPointCloudData(FileNamePoints.FilePath);
TArray<int32> LoadedPointCloudFlags = PointCloudDataReader::LoadFlags(FileNameFlags.FilePath);
const TArray<FVector> LoadedPointCloud = FPointCloudDataReader::LoadPointCloudData(FileNamePoints.FilePath);
TArray<int32> LoadedPointCloudFlags = FPointCloudDataReader::LoadFlags(FileNameFlags.FilePath);
// Initialize the boolean array for flags with all elements set to false
TArray<bool> BooleanFlags;
......@@ -45,7 +46,7 @@ void APointCloud::ReadPointCloudFromFile(const FFilePath FileNamePoints, const F
UE_LOG(LogTemp, Warning, TEXT("Loaded %d points and flags"), LoadedPointCloud.Num());
this->PositionVectors = PointCloudDataReader::NormalizeData(LoadedPointCloud, FVector::One() * 50, FVector::One() * 100, MinBounds, MaxBounds);
this->PositionVectors = FPointCloudDataReader::NormalizeData(LoadedPointCloud, FVector::One() * 50, FVector::One() * 100, MinBounds, MaxBounds);
UE_LOG(LogTemp, Warning, TEXT("Minimum: %s ; Maximum: %s"), *MinBounds.ToString(), *MaxBounds.ToString());
......@@ -139,7 +140,7 @@ void APointCloud::DrawVoxel(const int Index, const float Time) const
}
void APointCloud::ColorPointsInVoxels(const TArray<int32> VoxelIndices)
void APointCloud::SelectAllPointsInVoxels(const TArray<int32> VoxelIndices)
{
FScopeLock Lock(&DataGuard);
if (!MyDensityField)
......@@ -159,13 +160,90 @@ void APointCloud::ColorPointsInVoxels(const TArray<int32> VoxelIndices)
}
}
}
SaveStateAndUpdate();
}
void APointCloud::AddToSelection(const TArray<bool>& NewSelection)
{
if (NewSelection.Num() != SelectionFlags.Num())
{
UE_LOG(LogTemp, Error, TEXT("New selection array size does not match the current selection size."));
return;
}
// Called every frame
void APointCloud::Tick(float DeltaTime)
// Add the current state to the undo manager
MyUndoRedoManager.AddState(SelectionFlags);
for (int32 i = 0; i < NewSelection.Num(); ++i)
{
Super::Tick(DeltaTime);
if (NewSelection[i])
{
SelectionFlags[i] = true;
}
}
UpdateSelection();
}
void APointCloud::SubtractFromSelection(const TArray<bool>& NewSelection)
{
if (NewSelection.Num() != SelectionFlags.Num())
{
UE_LOG(LogTemp, Error, TEXT("New selection array size does not match the current selection size."));
return;
}
// Add the current state to the undo manager
MyUndoRedoManager.AddState(SelectionFlags);
for (int32 i = 0; i < NewSelection.Num(); ++i)
{
if (NewSelection[i])
{
SelectionFlags[i] = false;
}
}
UpdateSelection();
}
void APointCloud::SetSelection(const TArray<bool>& NewSelection)
{
if (NewSelection.Num() != SelectionFlags.Num())
{
UE_LOG(LogTemp, Error, TEXT("New selection array size does not match the current selection size."));
return;
}
// Add the current state to the undo manager
MyUndoRedoManager.AddState(SelectionFlags);
SelectionFlags = NewSelection;
UpdateSelection();
}
void APointCloud::Undo()
{
TArray<bool> PreviousState;
if (MyUndoRedoManager.Undo(PreviousState))
{
SelectionFlags = PreviousState;
UpdateSelection();
}
}
void APointCloud::Redo()
{
TArray<bool> NextState;
if (MyUndoRedoManager.Redo(NextState))
{
SelectionFlags = NextState;
UpdateSelection();
}
}
void APointCloud::Tick(const float DeltaTime)
{
Super::Tick(DeltaTime);
}
......@@ -2,8 +2,9 @@
#include "CoreMinimal.h"
#include "GPUPointCloudRendererComponent.h"
#include "KdtreeCommon.h"
#include "UndoRedoManager.h"
#include "GameFramework/Actor.h"
#include "MetaCastBachelor/DensityField.h"
#include "MetaCastBachelor/PointStorage/DensityField.h"
#include "PointCloud.generated.h"
class UGPUPointCloudRendererComponent;
......@@ -12,9 +13,7 @@ class APointCloud : public AActor
{
GENERATED_BODY()
public:
mutable FCriticalSection DataGuard;
FDensityField* MyDensityField;
FVector MinBounds;
FVector MaxBounds;
FKdtree MyKdTree;
......@@ -33,6 +32,10 @@ public:
UPROPERTY()
UGPUPointCloudRendererComponent* PointCloudVisualizer;
FUndoRedoManager MyUndoRedoManager;
public:
FDensityField* MyDensityField;
UPROPERTY(EditAnywhere)
float InfluenceRadius = 3.0f;
......@@ -60,17 +63,72 @@ public:
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
void UpdateSelection();
virtual void Tick(float DeltaTime) override;
void UpdateSelection();
public:
void DrawVoxel(const int Index, float Time) const;
void ColorPointsInVoxels(const TArray<int32> VoxelIndices);
void SelectAllPointsInVoxels(const TArray<int32> VoxelIndices);
void AddToSelection(const TArray<bool>& NewSelection);
void SubtractFromSelection(const TArray<bool>& NewSelection);
void SetSelection(const TArray<bool>& NewSelection);
void Undo();
void Redo();
const TArray<FVector>& GetPositionVectors() const
{
return PositionVectors;
}
const FVector& GetMinBounds() const
{
return MinBounds;
}
const FVector& GetMaxBounds() const
{
return MaxBounds;
}
UGPUPointCloudRendererComponent* GetPointCloudVisualizer() const
{
return PointCloudVisualizer;
}
void SetSelectionFlag(const int32 Index, const bool bValue)
{
if (SelectionFlags.IsValidIndex(Index))
{
SelectionFlags[Index] = bValue;
}
else
{
UE_LOG(LogTemp, Warning, TEXT("SetSelectionFlag: Invalid index %d"), Index);
}
}
bool GetSelectionFlag(const int32 Index) const
{
if (SelectionFlags.IsValidIndex(Index))
{
return SelectionFlags[Index];
}
UE_LOG(LogTemp, Warning, TEXT("GetSelectionFlag: Invalid index %d"), Index);
return false;
}
void SaveStateAndUpdate()
{
MyUndoRedoManager.AddState(SelectionFlags);
UpdateSelection();
}
int32 GetNumberOfPoints() const
{
return SelectionFlags.Num();
}
UFUNCTION(BlueprintCallable)
void ReadPointCloudFromFile(FFilePath FileNamePoints, FFilePath FileNameFlags);
};
......@@ -4,11 +4,11 @@
#include "CoreMinimal.h"
class PointCloudDataReader
class FPointCloudDataReader
{
public:
PointCloudDataReader();
~PointCloudDataReader();
FPointCloudDataReader();
~FPointCloudDataReader();
static bool LoadBytesFromFile(const FString& Filename, TArray<uint8>& OutByteArray);
static TArray<FVector> LoadPointCloudData(const FString& Filename);
......
#pragma once
#include "CoreMinimal.h"
class FUndoRedoManager
{
public:
// Structure to hold the state of selection flags
struct FSelectionState
{
TArray<bool> SelectionFlags;
};
// Adds a new state to the undo stack
void AddState(const TArray<bool>& NewState)
{
// Clear the redo stack when a new state is added
RedoStack.Empty();
// Add the new state to the undo stack
UndoStack.Push(FSelectionState{ NewState });
}
// Undoes the last operation
bool Undo(TArray<bool>& OutState)
{
if (UndoStack.Num() > 1)
{
// Move the current state to the redo stack
RedoStack.Push(UndoStack.Pop());
// Get the last state from the undo stack
OutState = UndoStack.Top().SelectionFlags;
return true;
}
return false;
}
// Redoes the last undone operation
bool Redo(TArray<bool>& OutState)
{
if (RedoStack.Num() > 0)
{
// Move the state from the redo stack to the undo stack
UndoStack.Push(RedoStack.Pop());
// Get the state from the undo stack
OutState = UndoStack.Top().SelectionFlags;
return true;
}
return false;
}
private:
TArray<FSelectionState> UndoStack;
TArray<FSelectionState> RedoStack;
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment