diff --git a/Content/Erase.uasset b/Content/Erase.uasset new file mode 100644 index 0000000000000000000000000000000000000000..fc7778b74d7444b17945e38b7b2a7d2291fd24f6 Binary files /dev/null and b/Content/Erase.uasset differ diff --git a/Content/Input/MetaCast_Config.uasset b/Content/Input/MetaCast_Config.uasset index 6008636d1068d073885b94a6b1470c3cd167bbf6..fd2a0349fe298b04f51242b2bab1313da3b1f56e 100644 Binary files a/Content/Input/MetaCast_Config.uasset and b/Content/Input/MetaCast_Config.uasset differ diff --git a/Content/Input/Redo.uasset b/Content/Input/Redo.uasset new file mode 100644 index 0000000000000000000000000000000000000000..ccdac7e926c4986559e869fb82595e1cad3b7e6d Binary files /dev/null and b/Content/Input/Redo.uasset differ diff --git a/Content/Input/Undo.uasset b/Content/Input/Undo.uasset new file mode 100644 index 0000000000000000000000000000000000000000..47f410de2063efce969be779b3a3a66b2c3e688d Binary files /dev/null and b/Content/Input/Undo.uasset differ diff --git a/Content/MagicWandMap.umap b/Content/MagicWandMap.umap index e503e7a9a9be7ad7408eafed26eba9c64434a752..d02fcaa70c32895380315ccb53e21ebb4aded783 100644 Binary files a/Content/MagicWandMap.umap and b/Content/MagicWandMap.umap differ diff --git a/Content/Redo.uasset b/Content/Redo.uasset new file mode 100644 index 0000000000000000000000000000000000000000..9ce8c21f68e3a2e291a0231af6b929a769e1c23b Binary files /dev/null and b/Content/Redo.uasset differ diff --git a/Content/Undo.uasset b/Content/Undo.uasset new file mode 100644 index 0000000000000000000000000000000000000000..8e85ab4f54ebddfc3ae615f8283e75792a377b0f Binary files /dev/null and b/Content/Undo.uasset differ diff --git a/Source/MetaCastBachelor/Utilities.cpp b/Source/MetaCastBachelor/General/Utilities.cpp similarity index 99% rename from Source/MetaCastBachelor/Utilities.cpp rename to Source/MetaCastBachelor/General/Utilities.cpp index c3581b95a017e689cfede3e732ec0fff2995f108..d4dd18402540d88a1bfb34f3f56d864df267d164 100644 --- a/Source/MetaCastBachelor/Utilities.cpp +++ b/Source/MetaCastBachelor/General/Utilities.cpp @@ -1,5 +1,5 @@ #include "Utilities.h" -#include "DensityField.h" +#include "MetaCastBachelor/PointStorage/DensityField.h" void FUtilities::QuickSort(TArray<float>& Array, const int32 Begin, const int32 End) { diff --git a/Source/MetaCastBachelor/Utilities.h b/Source/MetaCastBachelor/General/Utilities.h similarity index 93% rename from Source/MetaCastBachelor/Utilities.h rename to Source/MetaCastBachelor/General/Utilities.h index b453f8263106d69d8614f128c6a7a66b78f58143..a85d0c47b5382a18836383d7ef7ee9884c6eb8d6 100644 --- a/Source/MetaCastBachelor/Utilities.h +++ b/Source/MetaCastBachelor/General/Utilities.h @@ -1,5 +1,5 @@ #pragma once -#include "DensityField.h" +#include "MetaCastBachelor/PointStorage/DensityField.h" class FUtilities diff --git a/Source/MetaCastBachelor/DensityField.cpp b/Source/MetaCastBachelor/PointStorage/DensityField.cpp similarity index 92% rename from Source/MetaCastBachelor/DensityField.cpp rename to Source/MetaCastBachelor/PointStorage/DensityField.cpp index 0af1af8019c8539660da23f9d575f761fb0c33de..6cba753fa34dce1515356753719a0487f182bd48 100644 --- a/Source/MetaCastBachelor/DensityField.cpp +++ b/Source/MetaCastBachelor/PointStorage/DensityField.cpp @@ -1,7 +1,7 @@ #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) { diff --git a/Source/MetaCastBachelor/DensityField.h b/Source/MetaCastBachelor/PointStorage/DensityField.h similarity index 100% rename from Source/MetaCastBachelor/DensityField.h rename to Source/MetaCastBachelor/PointStorage/DensityField.h diff --git a/Source/MetaCastBachelor/InputDataReader.cpp b/Source/MetaCastBachelor/PointStorage/InputDataReader.cpp similarity index 83% rename from Source/MetaCastBachelor/InputDataReader.cpp rename to Source/MetaCastBachelor/PointStorage/InputDataReader.cpp index ba6e05e806881d00886c90aae71b3a425e7f9c56..e09c78257d6054163c8061e16f0fcb078b20efaa 100644 --- a/Source/MetaCastBachelor/InputDataReader.cpp +++ b/Source/MetaCastBachelor/PointStorage/InputDataReader.cpp @@ -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); diff --git a/Source/MetaCastBachelor/PointCloud.cpp b/Source/MetaCastBachelor/PointStorage/PointCloud.cpp similarity index 66% rename from Source/MetaCastBachelor/PointCloud.cpp rename to Source/MetaCastBachelor/PointStorage/PointCloud.cpp index 0eb4c858e7b6153576a83ac6fc77c9203387fbae..a9199b7fd712ff940c44bcdd8774d841ff769c6e 100644 --- a/Source/MetaCastBachelor/PointCloud.cpp +++ b/Source/MetaCastBachelor/PointStorage/PointCloud.cpp @@ -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; + } + + // Add the current state to the undo manager + MyUndoRedoManager.AddState(SelectionFlags); + + for (int32 i = 0; i < NewSelection.Num(); ++i) + { + if (NewSelection[i]) + { + SelectionFlags[i] = true; + } + } + + UpdateSelection(); +} -// Called every frame -void APointCloud::Tick(float DeltaTime) +void APointCloud::SubtractFromSelection(const TArray<bool>& NewSelection) { - Super::Tick(DeltaTime); - + 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); +} diff --git a/Source/MetaCastBachelor/PointCloud.h b/Source/MetaCastBachelor/PointStorage/PointCloud.h similarity index 55% rename from Source/MetaCastBachelor/PointCloud.h rename to Source/MetaCastBachelor/PointStorage/PointCloud.h index 68b6499becbfef3bf8418d98dad08bdc37cfb567..60181109a646440b91af7e9bad490addfe7b706b 100644 --- a/Source/MetaCastBachelor/PointCloud.h +++ b/Source/MetaCastBachelor/PointStorage/PointCloud.h @@ -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; @@ -11,14 +12,12 @@ UCLASS() class APointCloud : public AActor { GENERATED_BODY() - -public: + mutable FCriticalSection DataGuard; - FDensityField* MyDensityField; FVector MinBounds; FVector MaxBounds; FKdtree MyKdTree; - + UPROPERTY() TArray<FVector> PositionVectors; @@ -33,7 +32,11 @@ 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); - }; diff --git a/Source/MetaCastBachelor/PointCloudDataReader.h b/Source/MetaCastBachelor/PointStorage/PointCloudDataReader.h similarity index 86% rename from Source/MetaCastBachelor/PointCloudDataReader.h rename to Source/MetaCastBachelor/PointStorage/PointCloudDataReader.h index 87c214b113c231ab34a39d2e8b7d16ec41a76c55..a374d5b9578af90ccda68e1d0b3518b15faec0c9 100644 --- a/Source/MetaCastBachelor/PointCloudDataReader.h +++ b/Source/MetaCastBachelor/PointStorage/PointCloudDataReader.h @@ -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); diff --git a/Source/MetaCastBachelor/PointStorage/UndoRedoManager.cpp b/Source/MetaCastBachelor/PointStorage/UndoRedoManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Source/MetaCastBachelor/PointStorage/UndoRedoManager.h b/Source/MetaCastBachelor/PointStorage/UndoRedoManager.h new file mode 100644 index 0000000000000000000000000000000000000000..0ea22243a3526c038ef96e2cffe085e7816211b7 --- /dev/null +++ b/Source/MetaCastBachelor/PointStorage/UndoRedoManager.h @@ -0,0 +1,58 @@ +#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; +}; diff --git a/Source/MetaCastBachelor/VoxelPointLookupTable.cpp b/Source/MetaCastBachelor/PointStorage/VoxelPointLookupTable.cpp similarity index 100% rename from Source/MetaCastBachelor/VoxelPointLookupTable.cpp rename to Source/MetaCastBachelor/PointStorage/VoxelPointLookupTable.cpp diff --git a/Source/MetaCastBachelor/VoxelPointLookupTable.h b/Source/MetaCastBachelor/PointStorage/VoxelPointLookupTable.h similarity index 100% rename from Source/MetaCastBachelor/VoxelPointLookupTable.h rename to Source/MetaCastBachelor/PointStorage/VoxelPointLookupTable.h diff --git a/Source/MetaCastBachelor/FMagicWandSelectionTask.h b/Source/MetaCastBachelor/SelectionMethods/FMagicWandSelectionTask.h similarity index 100% rename from Source/MetaCastBachelor/FMagicWandSelectionTask.h rename to Source/MetaCastBachelor/SelectionMethods/FMagicWandSelectionTask.h diff --git a/Source/MetaCastBachelor/MagicWand.cpp b/Source/MetaCastBachelor/SelectionMethods/MagicWand.cpp similarity index 89% rename from Source/MetaCastBachelor/MagicWand.cpp rename to Source/MetaCastBachelor/SelectionMethods/MagicWand.cpp index f78a34a216b23028ae67b70662782df48ac330a5..654f419355c699935bd2ef801ef5c1d429943c92 100644 --- a/Source/MetaCastBachelor/MagicWand.cpp +++ b/Source/MetaCastBachelor/SelectionMethods/MagicWand.cpp @@ -1,9 +1,9 @@ #include "MagicWand.h" -#include "Utilities.h" #include "FMagicWandSelectionTask.h" #include "Components/LineBatchComponent.h" #include "Generators/MarchingCubes.h" #include "Kismet/GameplayStatics.h" +#include "MetaCastBachelor/PointStorage/PointCloud.h" // INITIALIZATION @@ -309,7 +309,7 @@ void UMagicWand::HandleMetaSelectPressed(const FInputActionInstance& Instance) SeedPointIndex = INDEX_NONE; const FVector SelectionStartPositionWorld = SelectionObject->GetComponentLocation(); - const FVector SelectionStartPositionLocal = MyPointCloud->PointCloudVisualizer->GetComponentTransform().InverseTransformPosition(SelectionStartPositionWorld); + const FVector SelectionStartPositionLocal = MyPointCloud->GetPointCloudVisualizer()->GetComponentTransform().InverseTransformPosition(SelectionStartPositionWorld); FindSeed(SelectionStartPositionLocal); if(SeedPointIndex == INDEX_NONE) return; @@ -338,9 +338,11 @@ void UMagicWand::HandleMetaSelectReleased(const FInputActionInstance& Instance) { if(SelectionArray[i].Value.load()) { - MyPointCloud->SelectionFlags[i] = true; + MyPointCloud->SetSelectionFlag(i, true); } } + + MyPointCloud->SaveStateAndUpdate(); //CurrentSelection.Reset(); } @@ -355,12 +357,33 @@ void UMagicWand::HandleMetaEraseReleased(const FInputActionInstance& Instance) } //AbortMagicWand.Store(true); - CurrentSelection = MakeShared<FSelectionManager>(MyPointCloud->SelectionFlags.Num(), MyDensityField->GetVoxelNumber()); + if(EraseSound) UGameplayStatics::PlaySound2D(World, EraseSound); + CurrentSelection = MakeShared<FSelectionManager>(MyPointCloud->GetNumberOfPoints(), MyDensityField->GetVoxelNumber()); - for(int i = 0; i < MyPointCloud->SelectionFlags.Num(); i++) + for(int i = 0; i < MyPointCloud->GetNumberOfPoints(); i++) { - MyPointCloud->SelectionFlags[i] = false; + MyPointCloud->SetSelectionFlag(i, false); } + + MyPointCloud->SaveStateAndUpdate(); +} + +void UMagicWand::HandleUndoAction(const FInputActionInstance& Instance) +{ + Super::HandleUndoAction(Instance); + if(NumberThreads.GetValue() > 0) return; + + if(UndoSound) UGameplayStatics::PlaySound2D(World, UndoSound); + MyPointCloud->Undo(); +} + +void UMagicWand::HandleRedoAction(const FInputActionInstance& Instance) +{ + Super::HandleRedoAction(Instance); + if(NumberThreads.GetValue() > 0) return; + + if(RedoSound) UGameplayStatics::PlaySound2D(World, RedoSound); + MyPointCloud->Redo(); } // MAGIC WAND SELECTION @@ -374,12 +397,6 @@ void UMagicWand::InitMagicWandSelection() void UMagicWand::PerformMagicWandSelection(const float ProximityThreshold) { - if (MyPointCloud->SelectionFlags.Num() != MyPointCloud->PositionVectors.Num()) - { - UE_LOG(LogTemp, Error, TEXT("PerformMagicWandSelection: Positions and SelectionFlags array sizes do not match.")); - return; - } - if (SeedPointIndex == INDEX_NONE) return; if(SelectionStartSound) UGameplayStatics::PlaySound2D(World, SelectionStartSound); @@ -432,7 +449,7 @@ void UMagicWand::FindSeed(const FVector& InputPosition) for(const int32 CurrentIndex : PointIndices) { - FVector CurrentPosition = MyPointCloud->PositionVectors[CurrentIndex]; + FVector CurrentPosition = MyPointCloud->GetPositionVectors()[CurrentIndex]; const float Distance = FVector::Dist(InputPosition, CurrentPosition); if (Distance < MinDistance) { @@ -444,7 +461,7 @@ void UMagicWand::FindSeed(const FVector& InputPosition) if(SeedPointIndex != INDEX_NONE) { - SeedPointPositionLocal = MyPointCloud->PositionVectors[SeedPointIndex]; + SeedPointPositionLocal = MyPointCloud->GetPositionVectors()[SeedPointIndex]; } } @@ -461,9 +478,9 @@ void UMagicWand::ExpandFromAllPointsInQueue(TQueue<int32>* ProcessQueue, const f int32 CurrentQueuePointIndex; ProcessQueue->Dequeue(CurrentQueuePointIndex); - if(MyPointCloud && MyPointCloud->PositionVectors.IsValidIndex(CurrentQueuePointIndex)) + if(MyPointCloud && MyPointCloud->GetPositionVectors().IsValidIndex(CurrentQueuePointIndex)) { - const FVector CurrentQueuePointPosition = MyPointCloud->PositionVectors[CurrentQueuePointIndex]; + const FVector CurrentQueuePointPosition = MyPointCloud->GetPositionVectors()[CurrentQueuePointIndex]; ExpandFromPoint(ProcessQueue, ProximityThreshold, ThreadLoad, CurrentQueuePointPosition, &MyVoxelPointLookupTable); } @@ -477,12 +494,12 @@ void UMagicWand::ExpandFromAllPointsInQueue(TQueue<int32>* ProcessQueue, const f void UMagicWand::ExpandFromPoint(TQueue<int32>* ProcessQueue, const float ProximityThreshold, int& ThreadLoad, const FVector& ExpansionPoint, const FVoxelPointLookupTable* MyVoxelPointLookupTable) const { const FVector Step = MyDensityField->GetStep(); - const int32 StartX = FMath::Max(0, FMath::FloorToInt((ExpansionPoint.X - ProximityThreshold - MyPointCloud->MinBounds.X) / Step.X)); - const int32 EndX = FMath::Min(MyDensityField->GetXNum() - 1, FMath::FloorToInt((ExpansionPoint.X + ProximityThreshold - MyPointCloud->MinBounds.X) / Step.X)); - const int32 StartY = FMath::Max(0, FMath::FloorToInt((ExpansionPoint.Y - ProximityThreshold - MyPointCloud->MinBounds.Y) / Step.Y)); - const int32 EndY = FMath::Min(MyDensityField->GetYNum() - 1, FMath::FloorToInt((ExpansionPoint.Y + ProximityThreshold - MyPointCloud->MinBounds.Y) / Step.Y)); - const int32 StartZ = FMath::Max(0, FMath::FloorToInt((ExpansionPoint.Z - ProximityThreshold - MyPointCloud->MinBounds.Z) / Step.Z)); - const int32 EndZ = FMath::Min(MyDensityField->GetZNum() - 1, FMath::FloorToInt((ExpansionPoint.Z + ProximityThreshold - MyPointCloud->MinBounds.Z) / Step.Z)); + const int32 StartX = FMath::Max(0, FMath::FloorToInt((ExpansionPoint.X - ProximityThreshold - MyPointCloud->GetMinBounds().X) / Step.X)); + const int32 EndX = FMath::Min(MyDensityField->GetXNum() - 1, FMath::FloorToInt((ExpansionPoint.X + ProximityThreshold - MyPointCloud->GetMinBounds().X) / Step.X)); + const int32 StartY = FMath::Max(0, FMath::FloorToInt((ExpansionPoint.Y - ProximityThreshold - MyPointCloud->GetMinBounds().Y) / Step.Y)); + const int32 EndY = FMath::Min(MyDensityField->GetYNum() - 1, FMath::FloorToInt((ExpansionPoint.Y + ProximityThreshold - MyPointCloud->GetMinBounds().Y) / Step.Y)); + const int32 StartZ = FMath::Max(0, FMath::FloorToInt((ExpansionPoint.Z - ProximityThreshold - MyPointCloud->GetMinBounds().Z) / Step.Z)); + const int32 EndZ = FMath::Min(MyDensityField->GetZNum() - 1, FMath::FloorToInt((ExpansionPoint.Z + ProximityThreshold - MyPointCloud->GetMinBounds().Z) / Step.Z)); const float SquaredProximityThreshold = ProximityThreshold * ProximityThreshold; @@ -511,7 +528,7 @@ void UMagicWand::ExpandFromPoint(TQueue<int32>* ProcessQueue, const float Proxim continue; } - FVector CurrentComparisonPosition = MyPointCloud->PositionVectors[CurrentPointComparisonIndex]; + FVector CurrentComparisonPosition = MyPointCloud->GetPositionVectors()[CurrentPointComparisonIndex]; const double SquaredDistance = FVector::DistSquared(ExpansionPoint, CurrentComparisonPosition); if (SquaredDistance <= SquaredProximityThreshold) @@ -551,9 +568,9 @@ void UMagicWand::FinishSelectionThread(const float ProximityThreshold) for (int32 i = 0; i < PointIndices.Num(); i++) { const int Index = PointIndices[i]; - if(MyPointCloud->PositionVectors.IsValidIndex(Index)) + if(MyPointCloud->GetPositionVectors().IsValidIndex(Index)) { - FVector Point = MyPointCloud->PositionVectors[Index]; + FVector Point = MyPointCloud->GetPositionVectors()[Index]; int32 VoxelFromIndex = MyDensityField->WorldPositionToIndex(Point); Voxels.Add(VoxelFromIndex); } @@ -616,7 +633,7 @@ TSharedPtr<FSelectionManager> UMagicWand::GetSelectionCacheResultCopy(const floa } // If no valid selection manager is found, create a new one and add it to the map - TSharedPtr<FSelectionManager> NewManager = MakeShared<FSelectionManager>(MyPointCloud->SelectionFlags.Num(), MyDensityField->GetVoxelNumber()); + TSharedPtr<FSelectionManager> NewManager = MakeShared<FSelectionManager>(MyPointCloud->GetNumberOfPoints(), MyDensityField->GetVoxelNumber()); SelectionCache.Add(ProximityThreshold, NewManager); SortedProximityRanges.Add(ProximityThreshold); SortedProximityRanges.Sort(); @@ -643,14 +660,14 @@ void UMagicWand::SelectParticles(const FVector& InputPosition) if(!IsMagicWandInitialized) return; const FVector CurrentSelectionPositionWorld = SelectionObject->GetComponentLocation(); - const FVector CurrentSelectionPositionLocal = MyPointCloud->PointCloudVisualizer->GetComponentTransform().InverseTransformPosition(CurrentSelectionPositionWorld); + const FVector CurrentSelectionPositionLocal = MyPointCloud->GetPointCloudVisualizer()->GetComponentTransform().InverseTransformPosition(CurrentSelectionPositionWorld); const float ProximityThreshold = FMath::Clamp(FVector::Dist(SeedPointPositionLocal, CurrentSelectionPositionLocal) / ThresholdDistanceScaling, MinThreshold, MaxThreshold); const FVector SelectionDirectionLocal = CurrentSelectionPositionLocal - SeedPointPositionLocal; const FVector ProximityEndPointLocal = SeedPointPositionLocal + (SelectionDirectionLocal.GetSafeNormal() * ProximityThreshold); - const FVector SelectionStartPositionWorld = MyPointCloud->PointCloudVisualizer->GetComponentTransform().TransformPosition(SeedPointPositionLocal); - const FVector ProximityEndPointWorld = MyPointCloud->PointCloudVisualizer->GetComponentTransform().TransformPosition(ProximityEndPointLocal); + const FVector SelectionStartPositionWorld = MyPointCloud->GetPointCloudVisualizer()->GetComponentTransform().TransformPosition(SeedPointPositionLocal); + const FVector ProximityEndPointWorld = MyPointCloud->GetPointCloudVisualizer()->GetComponentTransform().TransformPosition(ProximityEndPointLocal); GenerateCylinderBetweenPoints(SelectionStartPositionWorld, ProximityEndPointWorld, 0.05, 8, FColor::Red, 1); GenerateCylinderBetweenPoints((CurrentSelectionPositionWorld - ProximityEndPointWorld).GetSafeNormal() * 0.05 + ProximityEndPointWorld, CurrentSelectionPositionWorld, 0.05, 8, FColor::Green, 0); diff --git a/Source/MetaCastBachelor/MagicWand.h b/Source/MetaCastBachelor/SelectionMethods/MagicWand.h similarity index 92% rename from Source/MetaCastBachelor/MagicWand.h rename to Source/MetaCastBachelor/SelectionMethods/MagicWand.h index ece7c863ef218a5253bfbf348f2d58b81f5ccd49..f12fc010a718bea58aa7a810fb7e5b97535926a3 100644 --- a/Source/MetaCastBachelor/MagicWand.h +++ b/Source/MetaCastBachelor/SelectionMethods/MagicWand.h @@ -80,6 +80,15 @@ class UMagicWand : public UMetaCastBaseline UPROPERTY(EditAnywhere) USoundWave* SelectionStartSound; + UPROPERTY(EditAnywhere) + USoundWave* UndoSound; + + UPROPERTY(EditAnywhere) + USoundWave* RedoSound; + + UPROPERTY(EditAnywhere) + USoundWave* EraseSound; + public: int32 SeedPointIndex; @@ -95,6 +104,8 @@ public: virtual void HandleMetaSelectReleased(const FInputActionInstance& Instance) override; virtual void HandleMetaSelectPressed(const FInputActionInstance& Instance) override; virtual void HandleMetaEraseReleased(const FInputActionInstance& Instance) override; + virtual void HandleUndoAction(const FInputActionInstance& Instance) override; + virtual void HandleRedoAction(const FInputActionInstance& Instance) override; virtual void EraseParticles(const FVector& InputPosition) override; // VISUALIZATION diff --git a/Source/MetaCastBachelor/MagicWandSelectionManager.h b/Source/MetaCastBachelor/SelectionMethods/MagicWandSelectionManager.h similarity index 100% rename from Source/MetaCastBachelor/MagicWandSelectionManager.h rename to Source/MetaCastBachelor/SelectionMethods/MagicWandSelectionManager.h diff --git a/Source/MetaCastBachelor/MetaCastBaseline.cpp b/Source/MetaCastBachelor/SelectionMethods/MetaCastBaseline.cpp similarity index 75% rename from Source/MetaCastBachelor/MetaCastBaseline.cpp rename to Source/MetaCastBachelor/SelectionMethods/MetaCastBaseline.cpp index 6485e9d66fbf1f9b00c58da7bd3fe0e17de9a230..50b47ae33075d40d82a7851f2963fed28b8d781d 100644 --- a/Source/MetaCastBachelor/MetaCastBaseline.cpp +++ b/Source/MetaCastBachelor/SelectionMethods/MetaCastBaseline.cpp @@ -3,9 +3,7 @@ #include "EnhancedInputComponent.h" #include "EnhancedInputSubsystems.h" -UMetaCastBaseline::UMetaCastBaseline() : - SelectionObject(nullptr), EnhancedInputComponent(nullptr), MetaSelectAction(nullptr), MetaEraseAction(nullptr), - InputMappingContext(nullptr), LeftHandComponent(nullptr), MyPointCloud(nullptr) +UMetaCastBaseline::UMetaCastBaseline() : SelectionObject(nullptr), EnhancedInputComponent(nullptr), MetaSelectAction(nullptr), MetaEraseAction(nullptr), UndoAction(nullptr), RedoAction(nullptr), InputMappingContext(nullptr), LeftHandComponent(nullptr), MyPointCloud(nullptr) { PrimaryComponentTick.bCanEverTick = true; } @@ -78,6 +76,16 @@ void UMetaCastBaseline::InitInputBindings() UE_LOG(LogTemp, Error, TEXT("MetaEraseAction is not set. Please assign it in the editor.")); } + if (!UndoAction) + { + UE_LOG(LogTemp, Error, TEXT("UndoAction is not set. Please assign it in the editor.")); + } + + if (!RedoAction) + { + UE_LOG(LogTemp, Error, TEXT("RedoAction is not set. Please assign it in the editor.")); + } + const APlayerController* PlayerController = GetWorld()->GetFirstPlayerController(); if(!PlayerController) @@ -103,10 +111,13 @@ void UMetaCastBaseline::InitInputBindings() EnhancedInputComponent->BindAction(MetaSelectAction, ETriggerEvent::Started, this, &UMetaCastBaseline::HandleMetaSelectPressed); EnhancedInputComponent->BindAction(MetaSelectAction, ETriggerEvent::Completed, this, &UMetaCastBaseline::HandleMetaSelectReleased); - EnhancedInputComponent->BindAction(MetaSelectAction, ETriggerEvent::Canceled, this, &UMetaCastBaseline::HandleMetaSelectReleased); EnhancedInputComponent->BindAction(MetaEraseAction, ETriggerEvent::Started, this, &UMetaCastBaseline::HandleMetaErasePressed); EnhancedInputComponent->BindAction(MetaEraseAction, ETriggerEvent::Completed, this, &UMetaCastBaseline::HandleMetaEraseReleased); + + EnhancedInputComponent->BindAction(UndoAction, ETriggerEvent::Completed, this, &UMetaCastBaseline::HandleUndoAction); + + EnhancedInputComponent->BindAction(RedoAction, ETriggerEvent::Completed, this, &UMetaCastBaseline::HandleRedoAction); } } @@ -130,6 +141,14 @@ void UMetaCastBaseline::HandleMetaEraseReleased(const FInputActionInstance& Inst this->Erase = Instance.GetValue().Get<bool>(); } +void UMetaCastBaseline::HandleUndoAction(const FInputActionInstance& Instance) +{ +} + +void UMetaCastBaseline::HandleRedoAction(const FInputActionInstance& Instance) +{ +} + void UMetaCastBaseline::AttachToHand(const float DeltaTime) const { if (MyPointCloud && LeftHandComponent) @@ -170,40 +189,34 @@ void UMetaCastBaseline::TickComponent(const float DeltaTime, const ELevelTick Ti void UMetaCastBaseline::SelectParticles(const FVector& InputPosition) { - if (MyPointCloud->SelectionFlags.Num() != MyPointCloud->PositionVectors.Num()) - { - UE_LOG(LogTemp, Warning, TEXT("SelectParticles: Positions and SelectionFlags array sizes do not match.")); - return; - } - - for (int32 i = 0; i < MyPointCloud->PositionVectors.Num(); i++) + for (int32 i = 0; i < MyPointCloud->GetNumberOfPoints(); i++) { - FVector CurrentPoint = MyPointCloud->PositionVectors[i]; + FVector CurrentPoint = MyPointCloud->GetPositionVectors()[i]; - CurrentPoint = MyPointCloud->PointCloudVisualizer->GetComponentTransform().TransformPosition(CurrentPoint); - if ((InputPosition - CurrentPoint).Size() < SelectionRadius / 2 && !MyPointCloud->SelectionFlags[i]) + CurrentPoint = MyPointCloud->GetPointCloudVisualizer()->GetComponentTransform().TransformPosition(CurrentPoint); + if ((InputPosition - CurrentPoint).Size() < SelectionRadius / 2 && !MyPointCloud->GetSelectionFlag(i)) { - MyPointCloud->SelectionFlags[i] = true; // Set the flag to true as it's now selected + MyPointCloud->SetSelectionFlag(i, true); // Set the flag to true as it's now selected } } } void UMetaCastBaseline::EraseParticles(const FVector& InputPosition) { - if (MyPointCloud->SelectionFlags.Num() != MyPointCloud->PositionVectors.Num()) + if (MyPointCloud->GetNumberOfPoints() != MyPointCloud->GetPositionVectors().Num()) { UE_LOG(LogTemp, Warning, TEXT("Erase: Positions and SelectionFlags array sizes do not match.")); return; } - for (int32 i = 0; i < MyPointCloud->PositionVectors.Num(); i++) + for (int32 i = 0; i < MyPointCloud->GetPositionVectors().Num(); i++) { - FVector CurrentPoint = MyPointCloud->PositionVectors[i]; + FVector CurrentPoint = MyPointCloud->GetPositionVectors()[i]; - CurrentPoint = MyPointCloud->PointCloudVisualizer->GetComponentTransform().TransformPosition(CurrentPoint); - if ((InputPosition - CurrentPoint).Size() < SelectionRadius / 2 && !MyPointCloud->SelectionFlags[i]) + CurrentPoint = MyPointCloud->GetPointCloudVisualizer()->GetComponentTransform().TransformPosition(CurrentPoint); + if ((InputPosition - CurrentPoint).Size() < SelectionRadius / 2 && !MyPointCloud->GetSelectionFlag(i)) { - MyPointCloud->SelectionFlags[i] = false; // Set the flag to false as it's now erased + MyPointCloud->SetSelectionFlag(i, false); // Set the flag to false as it's now erased } } } diff --git a/Source/MetaCastBachelor/MetaCastBaseline.h b/Source/MetaCastBachelor/SelectionMethods/MetaCastBaseline.h similarity index 87% rename from Source/MetaCastBachelor/MetaCastBaseline.h rename to Source/MetaCastBachelor/SelectionMethods/MetaCastBaseline.h index c27aa364137be80b37f1063eb7fa8f7ee0b560cd..5bc02362fc0f8c413b053c6b8952b6e34ebef923 100644 --- a/Source/MetaCastBachelor/MetaCastBaseline.h +++ b/Source/MetaCastBachelor/SelectionMethods/MetaCastBaseline.h @@ -2,7 +2,7 @@ #include "CoreMinimal.h" #include "InputAction.h" #include "Components/SceneComponent.h" -#include "PointCloud.h" +#include "MetaCastBachelor/PointStorage/PointCloud.h" #include "MetaCastBaseline.generated.h" @@ -30,6 +30,12 @@ public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input") UInputAction* MetaEraseAction; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input") + UInputAction* UndoAction; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input") + UInputAction* RedoAction; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Input") UInputMappingContext* InputMappingContext; @@ -62,10 +68,16 @@ public: UMetaCastBaseline(); void InitLeftHand(); void InitInputBindings(); + virtual void HandleMetaSelectPressed(const FInputActionInstance& Instance); virtual void HandleMetaErasePressed(const FInputActionInstance& Instance); + virtual void HandleMetaSelectReleased(const FInputActionInstance& Instance); virtual void HandleMetaEraseReleased(const FInputActionInstance& Instance); + + virtual void HandleUndoAction(const FInputActionInstance& Instance); + virtual void HandleRedoAction(const FInputActionInstance& Instance); + void AttachToHand(float DeltaTime) const; void InitPointCloudReference(); void InitSelectionObject(); diff --git a/Source/MetaCastBachelor/MetaCastInteractable.cpp b/Source/MetaCastBachelor/SelectionMethods/MetaCastInteractable.cpp similarity index 99% rename from Source/MetaCastBachelor/MetaCastInteractable.cpp rename to Source/MetaCastBachelor/SelectionMethods/MetaCastInteractable.cpp index 076778607f04a52fb12b7e0d9b4eaf37ae759046..1303468eea6e99fa5a21ba2be72e1bd2de87b535 100644 --- a/Source/MetaCastBachelor/MetaCastInteractable.cpp +++ b/Source/MetaCastBachelor/SelectionMethods/MetaCastInteractable.cpp @@ -14,6 +14,7 @@ UMetaCastInteractable::UMetaCastInteractable() } + // Called when the game starts void UMetaCastInteractable::BeginPlay() { diff --git a/Source/MetaCastBachelor/MetaCastInteractable.h b/Source/MetaCastBachelor/SelectionMethods/MetaCastInteractable.h similarity index 100% rename from Source/MetaCastBachelor/MetaCastInteractable.h rename to Source/MetaCastBachelor/SelectionMethods/MetaCastInteractable.h diff --git a/Source/MetaCastBachelor/MetaPoint.cpp b/Source/MetaCastBachelor/SelectionMethods/MetaPoint.cpp similarity index 90% rename from Source/MetaCastBachelor/MetaPoint.cpp rename to Source/MetaCastBachelor/SelectionMethods/MetaPoint.cpp index e42fe892d1832d81a714777f8d1bc68910814102..f6d7040d5c6fcb7486f822c0ce1f165952eed939 100644 --- a/Source/MetaCastBachelor/MetaPoint.cpp +++ b/Source/MetaCastBachelor/SelectionMethods/MetaPoint.cpp @@ -1,6 +1,7 @@ #include "MetaPoint.h" -#include "Utilities.h" #include "Generators/MarchingCubes.h" +#include "MetaCastBachelor/General/Utilities.h" +#include "MetaCastBachelor/PointStorage/PointCloud.h" UMetaPoint::UMetaPoint() : LocalMaximumIndex(0), MyDensityField(nullptr), MetaPointThreshold(0), World(nullptr) { @@ -21,7 +22,7 @@ void UMetaPoint::BeginPlay() UE_LOG(LogTemp, Warning, TEXT("Invalid world provided.")); } - ProceduralMesh->AttachToComponent(MyPointCloud->PointCloudVisualizer, FAttachmentTransformRules::SnapToTargetIncludingScale); + ProceduralMesh->AttachToComponent(MyPointCloud->GetRootComponent(), FAttachmentTransformRules::SnapToTargetIncludingScale); ProceduralMesh->SetMaterial(0, SelectionVolumeMat); ProceduralMesh->SetMaterial(1, SelectionVolumeMat); ProceduralMesh->SetMaterial(2, SelectionVolumeMat); @@ -54,11 +55,10 @@ void UMetaPoint::HandleMetaSelectReleased(const FInputActionInstance& Instance) ProceduralMesh->ClearAllMeshSections(); ProceduralMesh->SetVisibility(false); - - + AsyncTask(ENamedThreads::Type::AnyBackgroundHiPriTask, [this]() { - MyPointCloud->ColorPointsInVoxels(FloodedIndices); + MyPointCloud->SelectAllPointsInVoxels(FloodedIndices); }); } @@ -74,10 +74,23 @@ void UMetaPoint::HandleMetaEraseReleased(const FInputActionInstance& Instance) Super::HandleMetaEraseReleased(Instance); //deselect all particles - for (int32 i = 0; i < MyPointCloud->SelectionFlags.Num(); ++i) + for (int32 i = 0; i < MyPointCloud->GetNumberOfPoints(); ++i) { - MyPointCloud->SelectionFlags[i] = false; + MyPointCloud->SetSelectionFlag(i, false); } + MyPointCloud->SaveStateAndUpdate(); +} + +void UMetaPoint::HandleUndoAction(const FInputActionInstance& Instance) +{ + Super::HandleUndoAction(Instance); + MyPointCloud->Undo(); +} + +void UMetaPoint::HandleRedoAction(const FInputActionInstance& Instance) +{ + Super::HandleRedoAction(Instance); + MyPointCloud->Redo(); } void UMetaPoint::InitMetaPointSelection() @@ -86,7 +99,7 @@ void UMetaPoint::InitMetaPointSelection() MyDensityField = MyPointCloud->MyDensityField; // Convert the world position of the selection object to the local position relative to the point cloud - const FVector SelectionLocalPosition = MyPointCloud->PointCloudVisualizer->GetComponentTransform().InverseTransformPosition(SelectionWorldPosition); + const FVector SelectionLocalPosition = MyPointCloud->GetPointCloudVisualizer()->GetComponentTransform().InverseTransformPosition(SelectionWorldPosition); // Perform gradient ascent to find the local maximum starting from the converted local position LocalMaximum = FUtilities::FollowGradientToMaximum(MyDensityField, SelectionLocalPosition); @@ -137,7 +150,7 @@ void UMetaPoint::SelectParticles(const FVector& InputPosition) LastHandInput = SelectionWorldPosition; // Convert the world position of the selection object to the local position relative to the point cloud - const FVector SelectionLocalPosition = MyPointCloud->PointCloudVisualizer->GetComponentTransform().InverseTransformPosition(SelectionWorldPosition); + const FVector SelectionLocalPosition = MyPointCloud->GetPointCloudVisualizer()->GetComponentTransform().InverseTransformPosition(SelectionWorldPosition); // Perform gradient ascent to find the local maximum starting from the converted local position LocalMaximum = FUtilities::FollowGradientToMaximum(MyDensityField, SelectionLocalPosition); diff --git a/Source/MetaCastBachelor/MetaPoint.h b/Source/MetaCastBachelor/SelectionMethods/MetaPoint.h similarity index 92% rename from Source/MetaCastBachelor/MetaPoint.h rename to Source/MetaCastBachelor/SelectionMethods/MetaPoint.h index 8a8591ce298e915c093ae347ef8a4651ffc50dcd..dfb9ba54b8463cf11a415246cac20f994dc4b506 100644 --- a/Source/MetaCastBachelor/MetaPoint.h +++ b/Source/MetaCastBachelor/SelectionMethods/MetaPoint.h @@ -59,6 +59,8 @@ public: virtual void HandleMetaSelectReleased(const FInputActionInstance& Instance) override; virtual void HandleMetaSelectPressed(const FInputActionInstance& Instance) override; virtual void HandleMetaEraseReleased(const FInputActionInstance& Instance) override; + virtual void HandleUndoAction(const FInputActionInstance& Instance) override; + virtual void HandleRedoAction(const FInputActionInstance& Instance) override; void InitMetaPointSelection(); void GenerateVoxelMeshWithCubes(const TArray<int32> Voxels) const; diff --git a/Source/MetaCastBachelor/MarchingCubes.compute b/Source/MetaCastBachelor/Visuals/MarchingCubes.compute similarity index 100% rename from Source/MetaCastBachelor/MarchingCubes.compute rename to Source/MetaCastBachelor/Visuals/MarchingCubes.compute diff --git a/Source/MetaCastBachelor/MarchingCubesGPU.cpp b/Source/MetaCastBachelor/Visuals/MarchingCubesGPU.cpp similarity index 100% rename from Source/MetaCastBachelor/MarchingCubesGPU.cpp rename to Source/MetaCastBachelor/Visuals/MarchingCubesGPU.cpp diff --git a/Source/MetaCastBachelor/MarchingCubesGPU.h b/Source/MetaCastBachelor/Visuals/MarchingCubesGPU.h similarity index 100% rename from Source/MetaCastBachelor/MarchingCubesGPU.h rename to Source/MetaCastBachelor/Visuals/MarchingCubesGPU.h diff --git a/Source/MetaCastBachelor/MyMarchingCubes.cpp b/Source/MetaCastBachelor/Visuals/MyMarchingCubes.cpp similarity index 100% rename from Source/MetaCastBachelor/MyMarchingCubes.cpp rename to Source/MetaCastBachelor/Visuals/MyMarchingCubes.cpp diff --git a/Source/MetaCastBachelor/MyMarchingCubes.h b/Source/MetaCastBachelor/Visuals/MyMarchingCubes.h similarity index 100% rename from Source/MetaCastBachelor/MyMarchingCubes.h rename to Source/MetaCastBachelor/Visuals/MyMarchingCubes.h