// Draw the tangent and normal to the equation at diffAt
        // Method takes a Graphics object to draw to the panel and the target of differention set by the user
        private float DrawTangentAndNormalLines(Graphics g, float diffAt)
        {
            // Create a new object to allow the estimation of the derivative of the target equation TargetFunction at diffAt
            var diff = new CentralDifferenceMethod(TargetFunction, diffAt);

            // The gradient of the function at diffAt
            double grad;

            try
            {
                // Evaluate the target function at diffAt
                float fa = TargetFunction.EvaluateF(diffAt.ToString(CultureInfo.InvariantCulture));

                // Obtain the value of the first derivative of the functiona at diffAt
                // This will become the gradient of the tangent line
                grad = diff.DeriveFirst();

                PointF originPoint = ToCartesianPoint(0, 0);
                PointF widthPoint = ToCartesianPoint(Width, 0);

                // If the gradient is defined
                if (!Double.IsNaN(grad))
                {
                    // Convert the gradient to a float to allow for drawing and get the normal gradient
                    float tangentGrad = Convert.ToSingle(grad);
                    float normalGrad = Convert.ToSingle(-1 / grad);

                    // Set the NormalGrad property to the gradient of the normal line, rounding to avoid innacuracies
                    NormalGrad = (float) Math.Round(normalGrad, 3);

                    // If the gradient is defined and can be drawn
                    if (Math.Abs(grad) < 1250)
                    {
                        // Draw the tangent to the function
                        DrawLine(g, Color.Red, 3F, originPoint.X, fa + tangentGrad * (originPoint.X - diffAt), widthPoint.X, fa + tangentGrad * (widthPoint.X - diffAt), false);

                        // If the normal is defined and can be drawn
                        if (Math.Abs(grad) > 0.010)
                        {
                            // Draw the normal with a dashed line
                            DrawLine(g, Color.Gray, 3F, originPoint.X, fa + normalGrad * (originPoint.X - diffAt), widthPoint.X, fa + normalGrad * (widthPoint.X - diffAt), true);
                        }
                    }

                    // Get the equations of the tangent and normal and set the corresponding fields to their values
                    Tangent = "y - " + Math.Round(fa, 3) + " = " + Math.Round(grad, 3) + " (x - " +
                              Math.Round(diffAt, 3) + ")";
                    Normal = "y - " + Math.Round(fa, 3) + " = " + NormalGrad + " (x - " + Math.Round(diffAt, 3) + ")";
                }
                else
                {
                    // If the gradient is not defined return NaN
                    return Single.NaN;
                }
            }
            catch (Exception)
            {
                // If any errors occured return NaN
                return Single.NaN;
            }

            // Return the gradient of the function
            return Convert.ToSingle(grad);
        }
        // Method tells the grapher to find any local extrema within the graph view
        // Method returns a list of Extrema objects, each representing a local minimum or maximum
        // A function has a minimum or maxmimum when the first derivative is zero
        // If the second derivative at a minimum or maximum is negative, then the function is at a
        // maximum. Otherwise the function is at a minimum
        // Currently this method can produce repeated and innaccurate results due to innacuracies in the
        // calculation of the derivatives
        public List <Extrema> FindExtrema()
        {
            // Get the values of bounds of the current graph view
            // These values will become the bounds of the searching radius
            PointF min = ToCartesianPoint(0, 5);
            PointF max = ToCartesianPoint(Width, 5);

            // Set the current target and visible target to the minimum visible point on the graph
            float currentTarget = min.X;

            // The target variable defines the point visible to the user during the calculation
            target = min.X;

            // Set the finding field to true
            finding = true;

            // Obtain an object to estimate the derivatives of the target function
            var m = new CentralDifferenceMethod(TargetFunction, currentTarget);

            // Make a list object to hold the extrema that have been found
            var ex = new List <Extrema>();

            try
            {
                // While the target is still in the searching bounds
                while (currentTarget < max.X)
                {
                    // Estimate the first derivative
                    double res = m.DeriveFirst();

                    // If the first derivative is within a tolerance to zero
                    if (Math.Abs(Math.Round(res, 3)) < 0.0025)
                    {
                        // Calculate the second derivative
                        double second = m.DeriveSecond();

                        // If the function is at a maximum
                        if (second < 0)
                        {
                            // Add a new Extrema to the list with the current target point
                            ex = AddExtrema(ex, currentTarget, "Local Maximum");
                        }
                        else
                        {
                            // The function is at a minimum
                            // Add a new Extrema to the list with the current target point
                            ex = AddExtrema(ex, currentTarget, "Local Minimum");
                        }
                    }

                    // Increment the target points
                    currentTarget += 0.001F;
                    target        += 0.08F;

                    // Set the target point of the differentiation object to get ready for thex next calculation
                    m.TargetPoint = currentTarget;

                    // If the grapher is still searching, repaint the graph to update the user on progress
                    if (target < max.X)
                    {
                        Invalidate();
                        Update();
                    }
                }
            }
            catch (Exception)
            {
                // Prompt the user if any errors occur during the calculations
                MessageBox.Show("Unable to find extrema", "Eror", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return(null);
            }

            // Prompt the user that the grapher has finished searching
            MessageBox.Show("Finished finding extrema", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);

            // Reset variables and return the list of extrema found
            finding = false;
            target  = max.X;
            Invalidate();
            return(ex);
        }
        // Method tells the grapher to find any local extrema within the graph view
        // Method returns a list of Extrema objects, each representing a local minimum or maximum
        // A function has a minimum or maxmimum when the first derivative is zero
        // If the second derivative at a minimum or maximum is negative, then the function is at a
        // maximum. Otherwise the function is at a minimum
        // Currently this method can produce repeated and innaccurate results due to innacuracies in the
        // calculation of the derivatives
        public List<Extrema> FindExtrema()
        {
            // Get the values of bounds of the current graph view
            // These values will become the bounds of the searching radius
            PointF min = ToCartesianPoint(0, 5);
            PointF max = ToCartesianPoint(Width, 5);

            // Set the current target and visible target to the minimum visible point on the graph
            float currentTarget = min.X;

            // The target variable defines the point visible to the user during the calculation
            target = min.X;

            // Set the finding field to true
            finding = true;

            // Obtain an object to estimate the derivatives of the target function
            var m = new CentralDifferenceMethod(TargetFunction, currentTarget);

            // Make a list object to hold the extrema that have been found
            var ex = new List<Extrema>();

            try
            {
                // While the target is still in the searching bounds
                while (currentTarget < max.X)
                {
                    // Estimate the first derivative
                    double res = m.DeriveFirst();

                    // If the first derivative is within a tolerance to zero
                    if (Math.Abs(Math.Round(res, 3)) < 0.0025)
                    {
                        // Calculate the second derivative
                        double second = m.DeriveSecond();

                        // If the function is at a maximum
                        if (second < 0)
                        {
                            // Add a new Extrema to the list with the current target point
                            ex = AddExtrema(ex, currentTarget, "Local Maximum");
                        }
                        else
                        {
                            // The function is at a minimum
                            // Add a new Extrema to the list with the current target point
                            ex = AddExtrema(ex, currentTarget, "Local Minimum");
                        }
                    }

                    // Increment the target points
                    currentTarget += 0.001F;
                    target += 0.08F;

                    // Set the target point of the differentiation object to get ready for thex next calculation
                    m.TargetPoint = currentTarget;

                    // If the grapher is still searching, repaint the graph to update the user on progress
                    if (target < max.X)
                    {
                        Invalidate();
                        Update();
                    }
                }
            }
            catch (Exception)
            {
                // Prompt the user if any errors occur during the calculations
                MessageBox.Show("Unable to find extrema", "Eror", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return null;
            }

            // Prompt the user that the grapher has finished searching
            MessageBox.Show("Finished finding extrema", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);

            // Reset variables and return the list of extrema found
            finding = false;
            target = max.X;
            Invalidate();
            return ex;
        }
Example #4
0
        // Draw the tangent and normal to the equation at diffAt
        // Method takes a Graphics object to draw to the panel and the target of differention set by the user
        private float DrawTangentAndNormalLines(Graphics g, float diffAt)
        {
            // Create a new object to allow the estimation of the derivative of the target equation TargetFunction at diffAt
            var diff = new CentralDifferenceMethod(TargetFunction, diffAt);

            // The gradient of the function at diffAt
            double grad;

            try
            {
                // Evaluate the target function at diffAt
                float fa = TargetFunction.EvaluateF(diffAt.ToString(CultureInfo.InvariantCulture));

                // Obtain the value of the first derivative of the functiona at diffAt
                // This will become the gradient of the tangent line
                grad = diff.DeriveFirst();


                PointF originPoint = ToCartesianPoint(0, 0);
                PointF widthPoint  = ToCartesianPoint(Width, 0);

                // If the gradient is defined
                if (!Double.IsNaN(grad))
                {
                    // Convert the gradient to a float to allow for drawing and get the normal gradient
                    float tangentGrad = Convert.ToSingle(grad);
                    float normalGrad  = Convert.ToSingle(-1 / grad);

                    // Set the NormalGrad property to the gradient of the normal line, rounding to avoid innacuracies
                    NormalGrad = (float)Math.Round(normalGrad, 3);

                    // If the gradient is defined and can be drawn
                    if (Math.Abs(grad) < 1250)
                    {
                        // Draw the tangent to the function
                        DrawLine(g, Color.Red, 3F, originPoint.X, fa + tangentGrad * (originPoint.X - diffAt), widthPoint.X, fa + tangentGrad * (widthPoint.X - diffAt), false);

                        // If the normal is defined and can be drawn
                        if (Math.Abs(grad) > 0.010)
                        {
                            // Draw the normal with a dashed line
                            DrawLine(g, Color.Gray, 3F, originPoint.X, fa + normalGrad * (originPoint.X - diffAt), widthPoint.X, fa + normalGrad * (widthPoint.X - diffAt), true);
                        }
                    }

                    // Get the equations of the tangent and normal and set the corresponding fields to their values
                    Tangent = "y - " + Math.Round(fa, 3) + " = " + Math.Round(grad, 3) + " (x - " +
                              Math.Round(diffAt, 3) + ")";
                    Normal = "y - " + Math.Round(fa, 3) + " = " + NormalGrad + " (x - " + Math.Round(diffAt, 3) + ")";
                }
                else
                {
                    // If the gradient is not defined return NaN
                    return(Single.NaN);
                }
            }
            catch (Exception)
            {
                // If any errors occured return NaN
                return(Single.NaN);
            }

            // Return the gradient of the function
            return(Convert.ToSingle(grad));
        }