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