public static Point2D CubicBezierEnvelope0(
            Point2D point,
            Rectangle2D bounds,
            Point2D topLeft, Point2D topLeftH, Point2D topLeftV,
            Point2D topRight, Point2D topRightH, Point2D topRightV,
            Point2D bottomRight, Point2D bottomRightH, Point2D bottomRightV,
            Point2D bottomLeft, Point2D bottomLeftH, Point2D bottomLeftV)
        {
            // topLeft                             topRight
            //   0--------0                 0----------0
            //   |   topLeftH             topRightH    |
            //   |                                     |
            //   |                                     |
            //   0 topLeftV                  topRightV 0
            //
            //
            //
            //   0 bottomLeftV            bottomRightV 0
            //   |                                     |
            //   |                                     |
            //   |  bottomLeftH         bottomRightH   |
            //   0--------0                 0----------0
            // bottomLeft                       bottomRight
            //
            // Install "Match Margin" Extension to enable word match highlighting, to help visualize where a variable resides in the ASCI map.

            var normal      = (X : (point.X - bounds.X) / bounds.Width, Y : (point.Y - bounds.Top) / bounds.Height);
            var leftAnchor  = BezierInterpolateCubic2DTests.CubicBezierCurve(normal.Y, topLeft.X, topLeft.Y, topLeftV.X, topLeftV.Y, bottomLeftV.X, bottomLeftV.Y, bottomLeft.X, bottomLeft.Y);
            var leftHandle  = InterpolateLinear2DTests.LinearInterpolate2D(normal.Y, topLeftH.X, topLeftH.Y, bottomLeftH.X, bottomLeftH.Y);
            var rightHandle = InterpolateLinear2DTests.LinearInterpolate2D(normal.Y, topRightH.X, topRightH.Y, bottomRightH.X, bottomRightH.Y);
            var rightAnchor = BezierInterpolateCubic2DTests.CubicBezierCurve(normal.Y, topRight.X, topRight.Y, topRightV.X, topRightV.Y, bottomRightV.X, bottomRightV.Y, bottomRight.X, bottomRight.Y);

            return(new Point2D(BezierInterpolateCubic2DTests.CubicBezierCurve(normal.X, leftAnchor.X, leftAnchor.Y, leftHandle.X, leftHandle.Y, rightHandle.X, rightHandle.Y, rightAnchor.X, rightAnchor.Y)));
        }
        public static (Point2D, double) FindNearestPoint(double aX, double aY, double bX, double bY, double cX, double cY, double dX, double dY, double x, double y)
        {
            var(a, b, c, d, e, f) = GetQuinticPolynomialFromCubicBezierAndPointTests.GetQuinticPolynomialFromCubicBezierAndPoint(aX, aY, bX, bY, cX, cY, dX, dY, x, y);
            var candidates = FindAllRealRootsInIntervalTests.FindAllRootsInInterval(0d, 1d, a, b, c, d, e, f);

            // Find the candidate that yields the closest point on the bezier to the given point.
            var t           = double.NaN;
            var output      = (double.NaN, double.NaN);
            var minDistance = double.MaxValue;

            foreach (var candidate in candidates)
            {
                var ptAtCandidate = BezierInterpolateCubic2DTests.CubicBezierInterpolate2D(candidate, aX, aY, bX, bY, cX, cY, dX, dY);
                var distance      = SquareDistanceTests.SquareDistance(ptAtCandidate.X, ptAtCandidate.Y, x, y);
                if (distance < minDistance)
                {
                    minDistance = distance;
                    t           = candidate;
                    output      = ptAtCandidate;
                }
            }

            return(output, t);
        }