diff --git a/Content/MagicWandMap.umap b/Content/MagicWandMap.umap index ea280d4c79087af91cda2dfbfa349f9795314ad5..e503e7a9a9be7ad7408eafed26eba9c64434a752 100644 Binary files a/Content/MagicWandMap.umap and b/Content/MagicWandMap.umap differ diff --git a/Source/MetaCastBachelor/MagicWand.cpp b/Source/MetaCastBachelor/MagicWand.cpp index e5404af1f9dab6e9e20eac7ff95472b847b2b5c2..f78a34a216b23028ae67b70662782df48ac330a5 100644 --- a/Source/MetaCastBachelor/MagicWand.cpp +++ b/Source/MetaCastBachelor/MagicWand.cpp @@ -9,7 +9,7 @@ class FMagicWandSelectionTask; -UMagicWand::UMagicWand() : World(nullptr) +UMagicWand::UMagicWand() : CurrentSelection(), World(nullptr) { // Create the procedural mesh component ProceduralMesh = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("GeneratedMesh")); @@ -47,7 +47,7 @@ void UMagicWand::InitProceduralMesh() const MarchingCubeMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision); MarchingCubeMesh->SetMobility(EComponentMobility::Movable); MarchingCubeMesh->SetVisibility(true); - MarchingCubeMesh->AttachToComponent(MyPointCloud->PointCloudVisualizer, FAttachmentTransformRules::SnapToTargetIncludingScale); + MarchingCubeMesh->AttachToComponent(MyPointCloud->GetRootComponent(), FAttachmentTransformRules::SnapToTargetIncludingScale); } void UMagicWand::InitReferences() @@ -120,9 +120,9 @@ void UMagicWand::GenerateVoxelMeshWithCubes(const TArray<int32> Voxels) const }); } -void UMagicWand::GenerateVoxelMeshSmooth(const TSet<int32> Voxels) +void UMagicWand::GenerateVoxelMeshSmooth(const TSet<int32>& Voxels) { - if(IsMarchingCubesRunning) + if(IsMarchingCubesRunning || Voxels.IsEmpty()) return; IsMarchingCubesRunning = true; @@ -141,7 +141,21 @@ void UMagicWand::GenerateVoxelMeshSmooth(const TSet<int32> Voxels) UE::Geometry::FMarchingCubes MarchingCubes; MarchingCubes.Bounds = UE::Geometry::TAxisAlignedBox3(Min, Max); - MarchingCubes.CubeSize = MarchingCubeSize == 0 ? MyDensityField->GetStep().X : MarchingCubeSize; + + //MarchingCubes.CubeSize = MarchingCubeSize == 0 ? MyDensityField->GetStep().X : MarchingCubeSize; + const float Volume = MarchingCubes.Bounds.Volume(); + // Logarithmic scaling of the cube size based on volume + if(Volume > 50000) { + MarchingCubes.CubeSize = (3.0f * log10((Volume + 500) / 1000) - 4); + }else if(Volume > 500000) + { + MarchingCubes.CubeSize = 4.3; + }else { + MarchingCubes.CubeSize = MyDensityField->GetStep().X; + } + + MarchingCubes.CubeSize = FMath::Clamp(MarchingCubes.CubeSize, MyDensityField->GetStep().X, 10.0f); + MarchingCubes.CancelF = [this]() { return AbortMarchingCubes; }; @@ -298,6 +312,8 @@ void UMagicWand::HandleMetaSelectPressed(const FInputActionInstance& Instance) const FVector SelectionStartPositionLocal = MyPointCloud->PointCloudVisualizer->GetComponentTransform().InverseTransformPosition(SelectionStartPositionWorld); FindSeed(SelectionStartPositionLocal); + if(SeedPointIndex == INDEX_NONE) return; + CountSelectionTime = 1 / MagicWandUpdatesPerSecond; IsMagicWandInitialized = true; } @@ -315,31 +331,36 @@ void UMagicWand::HandleMetaSelectReleased(const FInputActionInstance& Instance) { UGameplayStatics::PlaySound2D(World, SelectionEndSound); } + if(!CurrentSelection.IsValid()) return; - TArray<int32> SelectionArray = CurrentSelection.GetPointIndexList(); - for(const int Index : SelectionArray) + TArray<FAtomicBool> SelectionArray = CurrentSelection.Get()->GetSelectionFlags(); + for(int i = 0; i < SelectionArray.Num(); i++) { - if(MyPointCloud && MyPointCloud->SelectionFlags.IsValidIndex(Index)) + if(SelectionArray[i].Value.load()) { - MyPointCloud->SelectionFlags[Index] = true; + MyPointCloud->SelectionFlags[i] = true; } } - - CurrentSelection = FSelectionManager(CurrentSelection.GetSelectionFlags().Num()); + + //CurrentSelection.Reset(); } void UMagicWand::HandleMetaEraseReleased(const FInputActionInstance& Instance) { Super::HandleMetaEraseReleased(Instance); - if(NumberThreads.GetValue() > 0 || CurrentSelection.SelectedCount() == 0) + if(NumberThreads.GetValue() > 0) { return; } //AbortMagicWand.Store(true); - CurrentSelection = FSelectionManager(CurrentSelection.GetSelectionFlags().Num()); - MyPointCloud->SelectionFlags = CurrentSelection.GetSelectionFlags(); + CurrentSelection = MakeShared<FSelectionManager>(MyPointCloud->SelectionFlags.Num(), MyDensityField->GetVoxelNumber()); + + for(int i = 0; i < MyPointCloud->SelectionFlags.Num(); i++) + { + MyPointCloud->SelectionFlags[i] = false; + } } // MAGIC WAND SELECTION @@ -359,27 +380,23 @@ void UMagicWand::PerformMagicWandSelection(const float ProximityThreshold) return; } - if (SeedPointIndex != INDEX_NONE) - { - if(SelectionStartSound) - { - UGameplayStatics::PlaySound2D(World, SelectionStartSound); - } - - CurrentSelection = GetSelectionResultsFromProximity(ProximityThreshold); - CurrentSelection.SelectIndex(SeedPointIndex); + if (SeedPointIndex == INDEX_NONE) return; + + if(SelectionStartSound) UGameplayStatics::PlaySound2D(World, SelectionStartSound); + + CurrentSelection = GetSelectionCacheResultCopy(ProximityThreshold); + CurrentSelection.Get()->SelectIndex(SeedPointIndex); - TQueue<int32>* ProcessQueue = new TQueue<int32>{}; + TQueue<int32>* ProcessQueue = new TQueue<int32>{}; - for (int32 Index : CurrentSelection.GetPointIndexList()) - { - ProcessQueue->Enqueue(Index); - } - - FMagicWandSelectionTask* Task = new FMagicWandSelectionTask(this, ProcessQueue, ProximityThreshold, CurrentSelection.SelectedCount()); - NumberThreads.Increment(); - Task->Start(); + for (int32 Index : CurrentSelection.Get()->GetPointsAsIndexList()) + { + ProcessQueue->Enqueue(Index); } + + FMagicWandSelectionTask* Task = new FMagicWandSelectionTask(this, ProcessQueue, ProximityThreshold, CurrentSelection.Get()->SelectedCount()); + NumberThreads.Increment(); + Task->Start(); } void UMagicWand::FindSeed(const FVector& InputPosition) @@ -435,9 +452,7 @@ void UMagicWand::ExpandFromAllPointsInQueue(TQueue<int32>* ProcessQueue, const f { //UE_LOG(LogTemp, Warning, TEXT("Opened New Thread! Number of Threads now: %d"), NumberThreads.GetValue()); - TSet<int32> MyFullVoxelList; - MyFullVoxelList.Reserve(1000000); - FVoxelPointLookupTable MyVoxelPointLookupTable = *MyDensityField->VoxelPointLookupTable; + const FVoxelPointLookupTable MyVoxelPointLookupTable = *MyDensityField->VoxelPointLookupTable; while (!ProcessQueue->IsEmpty()) { @@ -450,7 +465,7 @@ void UMagicWand::ExpandFromAllPointsInQueue(TQueue<int32>* ProcessQueue, const f { const FVector CurrentQueuePointPosition = MyPointCloud->PositionVectors[CurrentQueuePointIndex]; - ExpandFromPoint(ProcessQueue, ProximityThreshold, ThreadLoad, CurrentQueuePointPosition, &MyFullVoxelList, &MyVoxelPointLookupTable); + ExpandFromPoint(ProcessQueue, ProximityThreshold, ThreadLoad, CurrentQueuePointPosition, &MyVoxelPointLookupTable); } ThreadLoad--; @@ -459,7 +474,7 @@ void UMagicWand::ExpandFromAllPointsInQueue(TQueue<int32>* ProcessQueue, const f FinishSelectionThread(ProximityThreshold); } -void UMagicWand::ExpandFromPoint(TQueue<int32>* ProcessQueue, const float ProximityThreshold, int& ThreadLoad, const FVector& ExpansionPoint, TSet<int32>* MyFullVoxelList, FVoxelPointLookupTable* MyVoxelPointLookupTable) +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)); @@ -478,8 +493,9 @@ void UMagicWand::ExpandFromPoint(TQueue<int32>* ProcessQueue, const float Proxim for (int32 X = StartX; X <= EndX; ++X) { const int32 CurrentVoxelComparisonIndex = MyDensityField->GridPositionToIndex(X, Y, Z); - - if(MyFullVoxelList->Contains(CurrentVoxelComparisonIndex)) + + + if(CurrentSelection.Get()->IsFullVoxel(CurrentVoxelComparisonIndex)) { continue; } @@ -489,7 +505,7 @@ void UMagicWand::ExpandFromPoint(TQueue<int32>* ProcessQueue, const float Proxim int Selected = 0; for(const int32 CurrentPointComparisonIndex : PointIndices) { - if(CurrentSelection.IsSelected(CurrentPointComparisonIndex)) + if(CurrentSelection.Get()->IsSelected(CurrentPointComparisonIndex)) { Selected++; continue; @@ -504,13 +520,13 @@ void UMagicWand::ExpandFromPoint(TQueue<int32>* ProcessQueue, const float Proxim ThreadLoad++; Selected++; - CurrentSelection.SelectIndex(CurrentPointComparisonIndex); + CurrentSelection.Get()->SelectIndex(CurrentPointComparisonIndex); } } if(Selected == PointIndices.Num()) { - MyFullVoxelList->Add(CurrentVoxelComparisonIndex); + CurrentSelection.Get()->AddFullVoxel(CurrentVoxelComparisonIndex); } } } @@ -526,7 +542,27 @@ void UMagicWand::FinishSelectionThread(const float ProximityThreshold) { CacheSelectionResults(ProximityThreshold, CurrentSelection); - //UE_LOG(LogTemp, Error, TEXT("Finished!")); + FScopeLock Lock(&SelectionCacheLock); + + if(CurrentSelection.IsValid()) + { + TSet<int32> Voxels; + TArray<int32> PointIndices = CurrentSelection.Get()->GetPointsAsIndexList(); + for (int32 i = 0; i < PointIndices.Num(); i++) + { + const int Index = PointIndices[i]; + if(MyPointCloud->PositionVectors.IsValidIndex(Index)) + { + FVector Point = MyPointCloud->PositionVectors[Index]; + int32 VoxelFromIndex = MyDensityField->WorldPositionToIndex(Point); + Voxels.Add(VoxelFromIndex); + } + } + + GenerateVoxelMeshSmooth(Voxels); + } + + //CurrentSelection.Reset(); }else if(NumberThreads.GetValue() < 0) { UE_LOG(LogTemp, Error, TEXT("More Threads closed than opened!")); @@ -551,7 +587,7 @@ void UMagicWand::CreateNewExpansionThread(TQueue<int32>* ProcessQueue, int& Numb Task->Start(); } -void UMagicWand::CacheSelectionResults(const float ProximityThreshold, const FSelectionManager& SelectionResults) +void UMagicWand::CacheSelectionResults(const float ProximityThreshold, const TSharedPtr<FSelectionManager>& SelectionResults) { FScopeLock Lock(&SelectionCacheLock); // Check if the proximity range is already present @@ -562,27 +598,30 @@ void UMagicWand::CacheSelectionResults(const float ProximityThreshold, const FSe SortedProximityRanges.Sort(); // Update the cache with new indices for a given proximity range - SelectionCache.Add(ProximityThreshold, SelectionResults); + + const TSharedRef<FSelectionManager> CacheCopy = MakeShared<FSelectionManager>(*SelectionResults.Get()); // This will return a shared pointer + SelectionCache.Add(ProximityThreshold, CacheCopy); } } -FSelectionManager UMagicWand::GetSelectionResultsFromProximity(const float ProximityThreshold) -{ +TSharedPtr<FSelectionManager> UMagicWand::GetSelectionCacheResultCopy(const float ProximityThreshold) { FScopeLock Lock(&SelectionCacheLock); - - for(int i = SortedProximityRanges.Num() - 1; i >= 0; --i) - { - const float CurrentRange = SortedProximityRanges[i]; - - if(CurrentRange <= ProximityThreshold) - { - //UE_LOG(LogTemp, Error, TEXT("Cached selection found for proximity range %f, closest smaller proximity was %f"), ProximityThreshold, CurrentRange); - return SelectionCache[CurrentRange]; + + for (int i = SortedProximityRanges.Num() - 1; i >= 0; --i) { + + if (SortedProximityRanges[i] <= ProximityThreshold) { + FSelectionManager* CopiedManager(SelectionCache[SortedProximityRanges[i]].Get()); + return MakeShared<FSelectionManager>(*CopiedManager); // This will return a shared pointer } } - //UE_LOG(LogTemp, Error, TEXT("No cached selection found for proximity range %f"), ProximityThreshold); - return FSelectionManager(MyPointCloud->SelectionFlags.Num()); + // 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()); + SelectionCache.Add(ProximityThreshold, NewManager); + SortedProximityRanges.Add(ProximityThreshold); + SortedProximityRanges.Sort(); + + return NewManager; // Return the newly created manager } // TICK @@ -619,24 +658,6 @@ void UMagicWand::SelectParticles(const FVector& InputPosition) if(CountSelectionTime >= 1.0f / MagicWandUpdatesPerSecond && NumberThreads.GetValue() == 0) { CountSelectionTime = 0; - //AbortMagicWand.Store(false); PerformMagicWandSelection(ProximityThreshold); - - FScopeLock Lock(&SelectionCacheLock); - - TSet<int32> Voxels; - TArray<int32> PointIndices = CurrentSelection.GetPointIndexList(); - for (int32 i = 0; i < PointIndices.Num(); i++) - { - const int Index = PointIndices[i]; - if(MyPointCloud->PositionVectors.IsValidIndex(Index)) - { - FVector Point = MyPointCloud->PositionVectors[Index]; - int32 VoxelFromIndex = MyDensityField->WorldPositionToIndex(Point); - Voxels.Add(VoxelFromIndex); - } - } - - GenerateVoxelMeshSmooth(Voxels); } } \ No newline at end of file diff --git a/Source/MetaCastBachelor/MagicWand.h b/Source/MetaCastBachelor/MagicWand.h index 5137d0fc493030e5985dc89b12e7462112ff733b..ece7c863ef218a5253bfbf348f2d58b81f5ccd49 100644 --- a/Source/MetaCastBachelor/MagicWand.h +++ b/Source/MetaCastBachelor/MagicWand.h @@ -16,8 +16,8 @@ class UMagicWand : public UMetaCastBaseline // MAGIC WAND TAtomic<bool> AbortMagicWand = false; - FSelectionManager CurrentSelection; - TMap<float, FSelectionManager> SelectionCache; + TSharedPtr<FSelectionManager> CurrentSelection; + TMap<float, TSharedPtr<FSelectionManager>> SelectionCache; TArray<float> SortedProximityRanges; FVector SeedPointPositionLocal; FDensityField* MyDensityField; @@ -100,7 +100,7 @@ public: // VISUALIZATION void GenerateVoxelMeshWithCubes(const TArray<int32> Voxels) const; - void GenerateVoxelMeshSmooth(const TSet<int32> Voxels); + void GenerateVoxelMeshSmooth(const TSet<int32>& Voxels); // MAGIC WAND SELECTION @@ -108,11 +108,11 @@ public: void PerformMagicWandSelection(const float ProximityThreshold); void FindSeed(const FVector& InputPosition); void ExpandFromAllPointsInQueue(TQueue<int32>* ProcessQueue, const float ProximityThreshold, int ThreadLoad); - void ExpandFromPoint(TQueue<int32>* ProcessQueue, const float ProximityThreshold, int& ThreadLoad, const FVector& ExpansionPoint, TSet<int32>* MyFullVoxelList, FVoxelPointLookupTable* MyVoxelPointLookupTable); + void ExpandFromPoint(TQueue<int32>* ProcessQueue, const float ProximityThreshold, int& ThreadLoad, const FVector& ExpansionPoint, const FVoxelPointLookupTable* MyVoxelPointLookupTable) const; void FinishSelectionThread(const float ProximityThreshold); void CreateNewExpansionThread(TQueue<int32>* ProcessQueue, int& NumberToProcess, const float ProximityThreshold); - void CacheSelectionResults(const float ProximityThreshold, const FSelectionManager& SelectionResults); - FSelectionManager GetSelectionResultsFromProximity(float ProximityThreshold); + void CacheSelectionResults(const float ProximityThreshold, const TSharedPtr<FSelectionManager>& SelectionResults); + TSharedPtr<FSelectionManager> GetSelectionCacheResultCopy(float ProximityThreshold); // TICK diff --git a/Source/MetaCastBachelor/MagicWandSelectionManager.h b/Source/MetaCastBachelor/MagicWandSelectionManager.h index 2c88a4234b84f48eb83bdcc9e0e1e8dbbd7db286..46f7213884277b76ca595dc72829236814c007ef 100644 --- a/Source/MetaCastBachelor/MagicWandSelectionManager.h +++ b/Source/MetaCastBachelor/MagicWandSelectionManager.h @@ -1,58 +1,86 @@ #pragma once #include "CoreMinimal.h" +class FAtomicBool +{ +public: + FAtomicBool() : Value(false) {} + explicit FAtomicBool(const bool InValue) : Value(InValue) {} + + // Copy constructor + FAtomicBool(const FAtomicBool& Other) + { + Value.store(Other.Value.load()); + } + + // Assignment operator + FAtomicBool& operator=(const FAtomicBool& Other) + { + if (this != &Other) + { + Value.store(Other.Value.load()); + } + return *this; + } + + // Move constructor + FAtomicBool(FAtomicBool&& Other) noexcept : Value(Other.Value.load()) {} + + // Move assignment operator + FAtomicBool& operator=(FAtomicBool&& Other) noexcept + { + if (this != &Other) + { + Value.store(Other.Value.load()); + } + return *this; + } + + std::atomic<bool> Value; +}; + class FSelectionManager { - TArray<int32> PointIndexList; // List of selected indices - TArray<bool> SelectionFlags; // Flags to indicate if an index is selected - int SelectionCount; // Counter for selected indices - TSet<int32> FullVoxelList; + TArray<FAtomicBool> SelectionFlags; // Flags to indicate if an index is selected + TArray<FAtomicBool> FullVoxelFlags; // Flags to indicate if a voxel is full + FThreadSafeCounter SelectionPointCount; // Counter for selected indices public: - explicit FSelectionManager(const int32 NumElements) : SelectionCount(0) + explicit FSelectionManager(const int32 NumberOfPoints, const int32 NumberOfVoxels) : SelectionPointCount(0) { - SelectionFlags.Init(false, NumElements); - PointIndexList.Reserve(NumElements); // Preallocate space to optimize appending + SelectionFlags.Init(FAtomicBool(false), NumberOfPoints); + FullVoxelFlags.Init(FAtomicBool(false), NumberOfVoxels); } - explicit FSelectionManager(const TArray<bool>& InSelectionFlags) : SelectionFlags(InSelectionFlags) - { - SelectionCount = 0; - for (int i = 0; i < InSelectionFlags.Num(); i++) - { - if (InSelectionFlags[i]) - { - PointIndexList.Add(i); - SelectionCount++; - } - } - } - - FSelectionManager(): SelectionCount(0) {} - - + // Copy constructor + FSelectionManager(const FSelectionManager& Other) + : SelectionFlags(Other.SelectionFlags) + , FullVoxelFlags(Other.FullVoxelFlags) + , SelectionPointCount(Other.SelectionPointCount) + {} + /** Adds an index to the selection if it isn't already selected */ void SelectIndex(const int32 Index) { - if (!SelectionFlags[Index]) + if (IsValidIndex(Index) && !SelectionFlags[Index].Value.load()) { - PointIndexList.Add(Index); - SelectionFlags[Index] = true; - SelectionCount++; + //PointIndexList.Add(Index); + SelectionFlags[Index].Value.store(true); + SelectionPointCount.Increment(); } } /** Removes a specific index from the selection */ bool UnselectIndex(const int32 IndexToRemove) { - if (IsValidIndex(IndexToRemove) && SelectionFlags[IndexToRemove]) + if (IsValidIndex(IndexToRemove) && SelectionFlags[IndexToRemove].Value.load()) { - const int32 RemoveAt = PointIndexList.IndexOfByKey(IndexToRemove); - if (RemoveAt != INDEX_NONE) + //const int32 RemoveAt = PointIndexList.IndexOfByKey(IndexToRemove); + if (true/*RemoveAt != INDEX_NONE*/) { - PointIndexList.RemoveAt(RemoveAt); - SelectionFlags[IndexToRemove] = false; - SelectionCount--; + //PointIndexList.RemoveAt(RemoveAt); + SelectionFlags[IndexToRemove].Value.store(false); + SelectionPointCount.Decrement(); return true; } } @@ -62,27 +90,19 @@ public: /** Returns true if any indices are selected */ bool IsAnythingSelected() const { - return SelectionCount > 0; + return SelectionPointCount.GetValue() > 0; } /** Checks if a specific index is selected */ bool IsSelected(const int32 Index) const { - return IsValidIndex(Index) && SelectionFlags[Index]; - } - - /** Resets the selection status of all indices */ - void Reset() - { - SelectionFlags.Init(false, SelectionFlags.Num()); - PointIndexList.Empty(); - SelectionCount = 0; + return IsValidIndex(Index) && SelectionFlags[Index].Value.load(); } /** Returns the number of selected indices */ int32 SelectedCount() const { - return SelectionCount; + return SelectionPointCount.GetValue(); } /** Checks if the given index is within the valid range */ @@ -91,19 +111,55 @@ public: return Index >= 0 && Index < SelectionFlags.Num(); } - /** Returns the number of indices in the selection list (for debugging) */ - int32 QueueSize() const + const TArray<FAtomicBool>& GetSelectionFlags() const + { + return SelectionFlags; + } + + // Thread-safe method to add a full voxel + void AddFullVoxel(const int32 VoxelIndex) { - return PointIndexList.Num(); + if(FullVoxelFlags.IsValidIndex(VoxelIndex)) + { + FullVoxelFlags[VoxelIndex].Value.store(true); + }else + { + UE_LOG(LogTemp, Error, TEXT("Voxel index %d is out of bounds when writing."), VoxelIndex); + } } - const TArray<int32>& GetPointIndexList() const - { - return PointIndexList; - } + // Thread-safe method to check if a voxel is full + bool IsFullVoxel(const int32 VoxelIndex) const + { + if(FullVoxelFlags.IsValidIndex(VoxelIndex)) + { + return FullVoxelFlags[VoxelIndex].Value.load(); + } + UE_LOG(LogTemp, Error, TEXT("Voxel index %d is out of bounds when reading."), VoxelIndex); + return false; + } - const TArray<bool>& GetSelectionFlags() const - { - return SelectionFlags; - } + // Batch update method to add multiple full voxels + void BatchAddFullVoxels(const TArray<int32>& VoxelIndices) + { + for (const int32 VoxelIndex : VoxelIndices) + { + FullVoxelFlags[VoxelIndex].Value.store(true); + } + } + + TArray<int> GetPointsAsIndexList() + { + TArray<int> PointIndexList; + PointIndexList.Reserve(SelectionPointCount.GetValue()); + + for (int32 Index = 0; Index < SelectionFlags.Num(); ++Index) + { + if (SelectionFlags[Index].Value.load()) + { + PointIndexList.Add(Index); + } + } + return PointIndexList; + } }; \ No newline at end of file