/// <summary>
        ///
        /// </summary>
        /// <param name="points"></param>
        /// <param name="threshold">Must be between 0 and 1</param>
        /// <returns></returns>
        public static IPoint[] SimplifyByAngle(IPoint[] points, double threshold)
        {
            if (points == null || points.Length == 0)
            {
                return(null);
            }
            else if (points.Length == 2)
            {
                return(points);
            }

            List <Tuple <int, double> > angles = new List <Tuple <int, double> >();

            for (int i = 0; i < points.Length - 2; i++)
            {
                var angle = SpatialUtility.CalculateSemiCosineOfAngle(points[i], points[i + 1], points[i + 2]);

                angles.Add(new Tuple <int, double>(i + 1, angle));
            }

            //When i.Item2 ~ 1 it means points are on a line
            var filter1 = angles.Where(i => i.Item2 < threshold).Select(i => i.Item1).ToList();

            filter1.Insert(0, 0);

            filter1.Add(points.Length - 1);

            var output1 = filter1.Select(i => points[i]).ToArray();

            return(output1);
        }
        public static IPoint[] SimplifyByArea(IPoint[] points, double threshold)
        {
            if (points == null || points.Length == 0)
            {
                return(null);
            }
            else if (points.Length == 2)
            {
                return(points);
            }

            List <Tuple <int, double> > areas = new List <Tuple <int, double> >();

            for (int i = 0; i < points.Length - 2; i++)
            {
                var area = SpatialUtility.CalculateTriangleArea(points[i], points[i + 1], points[i + 2]);

                areas.Add(new Tuple <int, double>(i + 1, area));
            }

            var filter1 = areas.Where(i => i.Item2 > threshold).Select(i => i.Item1).ToList();

            filter1.Insert(0, 0);

            filter1.Add(points.Length - 1);

            var output1 = filter1.Select(i => points[i]).ToArray();

            return(output1);
        }
        public static IPoint[] AdditiveSimplifyByAreaPlus(IPoint[] points, double threshold)
        {
            if (points == null || points.Length == 0)
            {
                return(null);
            }
            else if (points.Length == 2)
            {
                return(points);
            }

            List <int> filtered = new List <int>();

            filtered.Add(0);

            int firstIndex = 0, secondIndex = 1, thirdIndex = 2;

            var totalAreaCoef = SpatialUtility.IsClockwise(points) ? 1 : -1;

            double tempArea = 0;

            while (thirdIndex < points.Length)
            {
                var area = SpatialUtility.CalculateTriangleSignedArea(points[firstIndex], points[secondIndex], points[thirdIndex]);

                var areaCheck2 = SpatialUtility.CalculateTriangleArea(points[firstIndex], points[(int)((firstIndex + thirdIndex) / 2.0)], points[thirdIndex]);

                tempArea += area;

                if (Math.Abs(tempArea) > threshold || areaCheck2 > threshold || (area * totalAreaCoef > 0))
                {
                    tempArea = 0;

                    filtered.Add(secondIndex);

                    firstIndex = secondIndex;
                }

                secondIndex = thirdIndex;

                thirdIndex = thirdIndex + 1;
            }

            if (filtered.Count == 1)
            {
                filtered.Add(points.Count() / 2);
            }

            filtered.Add(points.Length - 1);

            var output1 = filtered.Select(i => points[i]).ToArray();

            //if (!output1[0].Equals(output1[output1.Length - 1]))
            //{

            //}
            return(output1);
        }
        /// <summary>
        /// Additive Angle + Area Check
        /// </summary>
        /// <param name="points"></param>
        /// <param name="anglethreshold">Must be between 0 and 1</param>
        /// <returns></returns>
        public static IPoint[] AdditiveSimplifyByAngleArea(IPoint[] points, double angleThreshold, double areaThreshold)
        {
            if (points == null || points.Length == 0)
            {
                return(null);
            }
            else if (points.Length == 2)
            {
                return(points);
            }

            List <int> filtered = new List <int>();

            filtered.Add(0);

            int firstIndex = 0, secondIndex = 1, thirdIndex = 2;

            double tempArea = 0;

            while (thirdIndex < points.Length)
            {
                var angle = SpatialUtility.CalculateSemiCosineOfAngle(points[firstIndex], points[secondIndex], points[thirdIndex]);

                var area = SpatialUtility.CalculateTriangleArea(points[firstIndex], points[secondIndex], points[thirdIndex]);

                tempArea += area;


                //if (Math.Abs(angle) < threshold)
                //{
                if ((angle < 0 || angle < angleThreshold) || tempArea > areaThreshold)
                {
                    filtered.Add(secondIndex);

                    firstIndex = secondIndex;

                    tempArea = 0;
                }


                secondIndex = thirdIndex;

                thirdIndex = thirdIndex + 1;
            }

            if (filtered.Count == 1)
            {
                filtered.Add(points.Count() / 2);
            }

            filtered.Add(points.Length - 1);

            var output1 = filtered.Select(i => points[i]).ToArray();

            return(output1);
        }
        public static IPoint[] AdditiveSimplifyByDistance(IPoint[] points, double threshold)
        {
            if (points == null || points.Length == 0)
            {
                return(null);
            }
            else if (points.Length == 2)
            {
                return(points);
            }

            List <int> filtered = new List <int>();

            filtered.Add(0);

            int firstIndex = 0, secondIndex = 1;

            var temp = 0.0;

            while (secondIndex < points.Length)
            {
                var semiDistance = SpatialUtility.GetSemiDistance(points[firstIndex], points[secondIndex]);

                temp += semiDistance;

                if (temp > threshold)
                {
                    temp = 0;

                    filtered.Add(secondIndex);

                    firstIndex = secondIndex;
                }

                secondIndex++;
            }

            if (filtered.Count == 1)
            {
                filtered.Add(points.Count() / 2);
            }

            filtered.Add(points.Length - 1);

            var output1 = filtered.Select(i => points[i]).ToArray();

            return(output1);
        }