/// <summary>
        /// Groups the hand points if closer than threshold.
        /// </summary>
        public static List <CameraSpacePoint> GroupHandPoints(List <int> handPointsIndexes)
        {
            List <CameraSpacePoint> handPoints = new List <CameraSpacePoint>();

            for (int i = 0; i < handPointsIndexes.Count; i++)
            {
                handPoints.Add(GlobVar.SubtractedFilteredPointCloud[handPointsIndexes[i]]);
            }

            DisjointSet3D disjointSetHandPoints = new DisjointSet3D(handPoints);

            for (int i = 0; i < handPoints.Count; i++)
            {
                for (int j = 0; j < handPoints.Count; j++)
                {
                    if (i == j)
                    {
                        continue;
                    }
                    ;
                    if (GlobUtils.GetEuclideanDistance(handPoints[i], handPoints[j]) < Thresholds.HandPointGroupingMaxDistance)
                    {
                        disjointSetHandPoints.Union(i, j);
                    }
                }
            }

            List <CameraSpacePoint> handCenterPoints = FilterHandCandidates(disjointSetHandPoints);

            return(handCenterPoints);
        }
        /// <summary>
        /// Filters the hand candidate points. Only the two candidate points belonging to the two biggest sets are kept.
        /// </summary>
        private static List <CameraSpacePoint> FilterHandCandidates(DisjointSet3D groupedSet)
        {
            Dictionary <int, int> filteredCandidatesIndex = new Dictionary <int, int>();

            for (int i = 0; i < groupedSet.Count; i++)
            {
                int setRepresentative = groupedSet.Find(i);
                int setSize           = groupedSet.SetSize(setRepresentative);
                if (!filteredCandidatesIndex.ContainsKey(setRepresentative) && setSize > Thresholds.HandPointMinCount)
                {
                    filteredCandidatesIndex.Add(setRepresentative, setSize);
                }
            }

            var sortedFilteredCandidatesIndex = filteredCandidatesIndex.OrderByDescending(kvp => kvp.Value);

            var filteredTopCandidates = new List <CameraSpacePoint>();

            int counter = 0;

            foreach (var filteredCandidate in sortedFilteredCandidatesIndex)
            {
                var cp = new CameraSpacePoint
                {
                    X = groupedSet.AverageX[filteredCandidate.Key],
                    Y = groupedSet.AverageY[filteredCandidate.Key],
                    Z = groupedSet.AverageZ[filteredCandidate.Key]
                };
                filteredTopCandidates.Add(cp);

                counter++;
                if (counter == 2)
                {
                    break;
                }
            }

            return(filteredTopCandidates);
        }
        /// <summary>
        /// Filters the hand candidate points. Only the two candidate points belonging to the two biggest sets are kept.
        /// </summary>
        private static List<CameraSpacePoint> FilterHandCandidates(DisjointSet3D groupedSet)
        {
            Dictionary<int, int> filteredCandidatesIndex = new Dictionary<int, int>();

            for (int i = 0; i < groupedSet.Count; i++)
            {
                int setRepresentative = groupedSet.Find(i);
                int setSize = groupedSet.SetSize(setRepresentative);
                if (!filteredCandidatesIndex.ContainsKey(setRepresentative) && setSize > Thresholds.HandPointMinCount)
                {
                    filteredCandidatesIndex.Add(setRepresentative, setSize);
                }
            }

            var sortedFilteredCandidatesIndex = filteredCandidatesIndex.OrderByDescending(kvp => kvp.Value);

            var filteredTopCandidates = new List<CameraSpacePoint>();

            int counter = 0;
            foreach (var filteredCandidate in sortedFilteredCandidatesIndex)
            {
                var cp = new CameraSpacePoint
                {
                    X = groupedSet.AverageX[filteredCandidate.Key],
                    Y = groupedSet.AverageY[filteredCandidate.Key],
                    Z = groupedSet.AverageZ[filteredCandidate.Key]
                };
                filteredTopCandidates.Add(cp);

                counter++;
                if (counter == 2)
                {
                    break;
                }
            }

            return filteredTopCandidates;
        }
        /// <summary>
        /// Groups the hand points if closer than threshold.
        /// </summary>
        public static List<CameraSpacePoint> GroupHandPoints(List<int> handPointsIndexes)
        {
            List<CameraSpacePoint> handPoints = new List<CameraSpacePoint>();
            for (int i = 0; i < handPointsIndexes.Count; i++)
            {
                handPoints.Add(GlobVar.SubtractedFilteredPointCloud[handPointsIndexes[i]]);
            }

            DisjointSet3D disjointSetHandPoints = new DisjointSet3D(handPoints);
            for (int i = 0; i < handPoints.Count; i++)
            {
                for (int j = 0; j < handPoints.Count; j++)
                {
                    if (i == j) { continue; };
                    if (GlobUtils.GetEuclideanDistance(handPoints[i], handPoints[j]) < Thresholds.HandPointGroupingMaxDistance)
                    {
                        disjointSetHandPoints.Union(i, j);
                    }
                }
            }

            List<CameraSpacePoint> handCenterPoints = FilterHandCandidates(disjointSetHandPoints);

            return handCenterPoints;
        }