/// <summary>
        /// Iterates over the given data, which has to match the allocated size, to check whether data points are inside the segment or not.
        /// </summary>
        /// <param name="segment">The segment this segmentation is going to be applied to.</param>
        /// <param name="data">Base data volume</param>
        /// <param name="parameters">Region grow parameters for the segmentation</param>
        /// <returns>The ThreadGroupState to enable progress monitoring and callback on finish. May return null if previous work has not yet been finished.</returns>
        public override ThreadGroupState Fit(Segment segment, int[] data, RegionGrowParameter parameters)
        {
            if (segment._currentWorkload.Working > 0)
            {
                return(null);
            }

            segment._currentWorkload.Reset();
            segment._currentWorkload.TotalProgress = 1;
            segment._currentWorkload.Register();

            var t = new Thread(() => StartRegionGrow(segment, data, parameters))
            {
                IsBackground = true
            };

            _runningThreads.Add(t);

            t.Start();

            return(segment._currentWorkload);
        }
        /// <summary>
        /// Region grow implementation
        /// </summary>
        /// <param name="segment">The segment this segmentation is going to be applied to.</param>
        /// <param name="data">Base data volume</param>
        /// <param name="regionGrowParameter">Region grow parameters for the segmentation</param>
        private void StartRegionGrow(Segment segment, IReadOnlyList <int> data,
                                     RegionGrowParameter regionGrowParameter)
        {
            var pending = new Queue <Voxel>(20000);
            var visited = new Segment(Color.clear);

            visited.Allocate(segment.Width, segment.Height, segment.Slices);

            var seedVoxel = new Voxel(regionGrowParameter.X, regionGrowParameter.Y, regionGrowParameter.Z);

            pending.Enqueue(seedVoxel);
            visited.Set(seedVoxel.X, seedVoxel.Y, seedVoxel.Z);
            segment.Set(seedVoxel.X, seedVoxel.Y, seedVoxel.Z);

            var intensityBase = data[GetIndex(seedVoxel, segment.Width, segment.Height)];

            var intensityLower = intensityBase - regionGrowParameter.Threshold;
            var intensityUpper = intensityBase + regionGrowParameter.Threshold;

            long processed = 0;

            while (pending.Count > 0)
            {
                var currentVec = pending.Dequeue();

                var currentIdx = GetIndex(currentVec, segment.Width, segment.Height);

                var curVal = data[currentIdx];

                if (curVal < intensityLower || curVal > intensityUpper)
                {
                    continue;
                }

                segment.Set(currentVec.X, currentVec.Y, currentVec.Z);

                Voxel neighbor;

                if (currentVec.X > 0)
                {
                    neighbor = new Voxel(currentVec.X - 1, currentVec.Y, currentVec.Z);

                    if (!visited.Contains(neighbor.X, neighbor.Y, neighbor.Z))
                    {
                        pending.Enqueue(neighbor);
                        visited.Set(neighbor.X, neighbor.Y, neighbor.Z);
                    }
                }

                if (currentVec.X < segment.Width - 1)
                {
                    neighbor = new Voxel(currentVec.X + 1, currentVec.Y, currentVec.Z);

                    if (!visited.Contains(neighbor.X, neighbor.Y, neighbor.Z))
                    {
                        pending.Enqueue(neighbor);
                        visited.Set(neighbor.X, neighbor.Y, neighbor.Z);
                    }
                }

                if (currentVec.Y > 0)
                {
                    neighbor = new Voxel(currentVec.X, currentVec.Y - 1, currentVec.Z);

                    if (!visited.Contains(neighbor.X, neighbor.Y, neighbor.Z))
                    {
                        pending.Enqueue(neighbor);
                        visited.Set(neighbor.X, neighbor.Y, neighbor.Z);
                    }
                }

                if (currentVec.Y < segment.Height - 1)
                {
                    neighbor = new Voxel(currentVec.X, currentVec.Y + 1, currentVec.Z);

                    if (!visited.Contains(neighbor.X, neighbor.Y, neighbor.Z))
                    {
                        pending.Enqueue(neighbor);
                        visited.Set(neighbor.X, neighbor.Y, neighbor.Z);
                    }
                }
                if (currentVec.Z > 0)
                {
                    neighbor = new Voxel(currentVec.X, currentVec.Y, currentVec.Z - 1);

                    if (!visited.Contains(neighbor.X, neighbor.Y, neighbor.Z))
                    {
                        pending.Enqueue(neighbor);
                        visited.Set(neighbor.X, neighbor.Y, neighbor.Z);
                    }
                }
                if (currentVec.Z < segment.Slices - 1)
                {
                    neighbor = new Voxel(currentVec.X, currentVec.Y, currentVec.Z + 1);

                    if (!visited.Contains(neighbor.X, neighbor.Y, neighbor.Z))
                    {
                        pending.Enqueue(neighbor);
                        visited.Set(neighbor.X, neighbor.Y, neighbor.Z);
                    }
                }

                if (processed % 40000 == 0)
                {
                    Thread.Sleep(50);
                }

                processed++;
            }

            segment._currentWorkload.IncrementProgress();
            segment._currentWorkload.Done();
            _runningThreads.Remove(Thread.CurrentThread);
        }