/// <summary>
        /// Create a rectangle from two sides of a triangle.
        /// </summary>
        /// <param name="a">First point.</param>
        /// <param name="b">Second point.</param>
        /// <param name="c">third point.</param>
        /// <returns>Rectangle.</returns>
        private static Rectangle3d CreateTwoSidedRectangle(Point3d a, Point3d b, Point3d c)
        {
            Vector3d ba = a - b;
            Vector3d bc = c - b;
            Vector3d ac = c - a;

            if (ba.IsZero)
            {
                return(CreateDegenerateRectangle(a, c));
            }
            if (bc.IsZero)
            {
                return(CreateDegenerateRectangle(a, b));
            }
            if (ac.IsZero)
            {
                return(CreateDegenerateRectangle(a, b));
            }

            double lba = ba.Length;
            double lbc = bc.Length;

            Plane plane;

            if (lba > lbc)
            {
                plane = new Plane(b, ba, bc);
            }
            else
            {
                plane = new Plane(b, bc, ba);
                plane.Flip();
            }
            return(new Rectangle3d(plane, new Interval(0, lba), new Interval(0, lbc)));
        }
        /// <summary>
        /// Attempts to create a rectangle from a polyline. This method only works well for
        /// polylines that already closely resemble rectangles. If the polyline contains
        /// more than four vertices, the least significant ones will be ignored. If the
        /// polylines is non-orthogonal, the discrepancies will be averaged away.
        /// This method should not be used as a Rectangle fitter.
        /// </summary>
        /// <param name="polyline">Polyline to parse.</param>
        /// <param name="deviation">On success, the deviation will contain the largest deviation between the polyline and the rectangle.</param>
        /// <param name="angleDeviation">On success, the angleDeviation will contain the largest deviation (in radians) between the polyline edges and the rectangle edges.</param>
        /// <returns>A rectangle that is shaped similarly to the polyline or Rectangle3d.Unset
        /// if the polyline does not represent a rectangle.</returns>
        public static Rectangle3d CreateFromPolyline(IEnumerable <Point3d> polyline, out double deviation, out double angleDeviation)
        {
            if (polyline == null)
            {
                throw new ArgumentNullException(nameof(polyline));
            }

            deviation      = 0.0;
            angleDeviation = 0.0;

            // Remove consecutive identical vertices.
            Point3d        prev   = Point3d.Unset;
            List <Point3d> points = new List <Point3d>();

            foreach (Point3d point in polyline)
            {
                if (point == prev)
                {
                    continue;
                }
                if (!point.IsValid)
                {
                    continue;
                }
                points.Add(point);
                prev = point;
            }

            // Remove closing vertex.
            if (points.Count > 1 && points[0] == points[points.Count - 1])
            {
                points.RemoveAt(points.Count - 1);
            }

            // Special degenerate cases.
            if (points.Count == 0)
            {
                return(Unset);
            }
            if (points.Count == 1)
            {
                return(CreateDegenerateRectangle(points[0], points[0]));
            }
            if (points.Count == 2)
            {
                return(CreateDegenerateRectangle(points[0], points[1]));
            }
            if (points.Count == 3)
            {
                points.Add(points[0] + (points[2] - points[1]));
            }
            if (points.Count > 5)
            {
                RecursiveReduceVertices(points);
            }

            Point3d  centre = 0.25 * (points[0] + points[1] + points[2] + points[3]);
            Vector3d xaxis  = (points[1] - points[0]) + (points[2] - points[3]);
            Vector3d yaxis  = (points[3] - points[0]) + (points[2] - points[1]);
            bool     flip   = false;

            if (xaxis.Length < yaxis.Length)
            {
                flip = true;
                Vector3d cache = xaxis;
                xaxis = yaxis;
                yaxis = cache;
            }

            Plane plane = new Plane(centre, xaxis, yaxis);

            if (flip)
            {
                plane.Flip();
            }

            double x0, x1, x2, x3;
            double y0, y1, y2, y3;

            plane.ClosestParameter(points[0], out x0, out y0);
            plane.ClosestParameter(points[1], out x1, out y1);
            plane.ClosestParameter(points[2], out x2, out y2);
            plane.ClosestParameter(points[3], out x3, out y3);

            Interval xdomain = new Interval(0.5 * x0 + 0.5 * x3, 0.5 * x1 + 0.5 * x2);
            Interval ydomain = new Interval(0.5 * y0 + 0.5 * y1, 0.5 * y2 + 0.5 * y3);

            Rectangle3d rec = new Rectangle3d(plane, xdomain, ydomain);

            ComputeDeviation(rec, points, out deviation, out angleDeviation);
            return(rec);
        }