첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
유용한 소스 코드가 있으면 icodebroker@naver.com으로 보내주시면 감사합니다.
블로그 자료는 자유롭게 사용하세요.

■ 사용자 입력 수식 그래프 그리기

------------------------------------------------------------------------------------------------------------------------


TestProject.zip


MainWindow.xaml

 

 

<Window x:Class="TestProject.MainWindow"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Width="800"

    Height="600"

    Title="사용자 입력 수식 그래프 그리기"

    FontFamily="나눔고딕코딩"

    FontSize="16">

    <Grid

        Margin="10"

        Background="LightGreen">

        <Grid.Resources>

            <Style TargetType="{x:Type Label}">

                <Setter Property="VerticalAlignment" Value="Center" />

            </Style>

            <Style TargetType="{x:Type TextBox}">

                <Setter Property="VerticalAlignment" Value="Center" />

                <Setter Property="TextAlignment"     Value="Right"  />

            </Style>

        </Grid.Resources>

        <Grid.ColumnDefinitions>

            <ColumnDefinition Width="*" />

        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>

            <RowDefinition Height="Auto" />

            <RowDefinition Height="*"    />

        </Grid.RowDefinitions>

        <Grid Grid.Row="0" Grid.Column="0">

            <Grid.ColumnDefinitions>

                <ColumnDefinition Width="Auto" />

                <ColumnDefinition Width="Auto" />

                <ColumnDefinition Width="Auto" />

                <ColumnDefinition Width="*"    />

            </Grid.ColumnDefinitions>

            <Grid.RowDefinitions>

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

            </Grid.RowDefinitions>

            <Label Grid.Row="0" Grid.Column="0"

                Margin="5"

                Content="수식" />

            <TextBox Name="equationTextBox" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="3"

                Margin="0 5 0 5"

                Padding="5"

                HorizontalContentAlignment="Left"

                Text="10 * Math.Sin(x) / x" />

            <TextBox Name="minimymXTextBox" Grid.Row="1" Grid.Column="0"

                Margin="5"

                Padding="5"

                Text="-20" />

            <Label Grid.Row="1" Grid.Column="1"

                Margin="5"

                HorizontalAlignment="Center"

                Content="&lt;= X &lt;=" />

            <TextBox Name="maximymXTextBox" Grid.Row="1" Grid.Column="2"

                Margin="5"

                Padding="5"

                Text="20" />

            <TextBox Name="minimumYTextBox" Grid.Row="2" Grid.Column="0"

                Margin="5"

                Padding="5"

                Text="-5" />

            <Label Grid.Row="2" Grid.Column="1"

                Margin="5"

                HorizontalAlignment="Center"

                Content="&lt;= Y &lt;=" />

            <TextBox Name="maximumYTextBox" Grid.Row="2" Grid.Column="2"

                Margin="5"

                Padding="5"

                Text="20" />

            <Button Name="drawButton" Grid.Row="1" Grid.Column="3" Grid.RowSpan="2"

                Width="100"

                Height="30"

                Content="그리기" />

        </Grid>

        <Border Grid.Row="1" Grid.Column="0"

            Margin="5"

            BorderBrush="Black"

            BorderThickness="1">

            <Canvas Name="canvas"

                VerticalAlignment="Stretch"

                HorizontalAlignment="Stretch"

                Background="White" />

        </Border>

    </Grid>

</Window>

 

 

MainWindow.xaml.cs

 

 

using System;

using System.CodeDom.Compiler;

using System.Collections.Generic;

using System.Reflection;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Media;

using System.Windows.Shapes;

 

namespace TestProject

{

    /// <summary>

    /// 메인 윈도우

    /// </summary>

    public partial class MainWindow : Window

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field

        ////////////////////////////////////////////////////////////////////////////////////////// Private

 

        #region Field

 

        /// <summary>

        /// 월드→장치 매트릭스

        /// </summary>

        private Matrix worldToDeviceMatrix;

        

        /// <summary>

        /// 장치→월드 매트릭스

        /// </summary>

        private Matrix deviceToWorldMatrix;

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 생성자 - MainWindow()

 

        /// <summary>

        /// 생성자

        /// </summary>

        public MainWindow()

        {

            InitializeComponent();

 

            Loaded                += Window_Loaded;

            this.drawButton.Click += drawButton_Click;

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method

        ////////////////////////////////////////////////////////////////////////////////////////// Private

        //////////////////////////////////////////////////////////////////////////////// Event

 

        #region 윈도우 로드시 처리하기 - Window_Loaded(sender, e)

 

        /// <summary>

        /// 윈도우 로드시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void Window_Loaded(object sender, RoutedEventArgs e)

        {

            int equation = 2;

 

            switch(equation)

            {

                case 1 :

 

                    this.equationTextBox.Text = "10 * Math.Sin(x) / x";

                    this.minimymXTextBox.Text = "-20";

                    this.maximymXTextBox.Text = "20";

                    this.minimumYTextBox.Text = "-5";

                    this.maximumYTextBox.Text = "20";

 

                    break;

 

                case 2 :

 

                    this.equationTextBox.Text = "x * x / (x * x - 1)";

                    this.minimymXTextBox.Text = "-6";

                    this.maximymXTextBox.Text = "6";

                    this.minimumYTextBox.Text = "-3";

                    this.maximumYTextBox.Text = "5";

 

                    break;

 

                case 3 :

 

                    this.equationTextBox.Text = "5 * Math.Sin(x)";

                    this.minimymXTextBox.Text = "-6";

                    this.maximymXTextBox.Text = "6";

                    this.minimumYTextBox.Text = "-4";

                    this.maximumYTextBox.Text = "4";

 

                    break;

 

                case 4 :

 

                    this.equationTextBox.Text = "Math.Abs(x) / x / 2";

                    this.minimymXTextBox.Text = "-3";

                    this.maximymXTextBox.Text = "3";

                    this.minimumYTextBox.Text = "-2";

                    this.maximumYTextBox.Text = "2";

 

                    break;

            }

        }

 

        #endregion

        #region 그리기 버튼 클릭시 처리하기 - drawButton_Click(sender, e)

 

        /// <summary>

        /// 그리기 버튼 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void drawButton_Click(object sender, RoutedEventArgs e)

        {

            DrawGraph();

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////// Function

 

        #region 매트릭스 설정하기 - SetMatrix(worldMinimumX, worldMaximumX, worldMinimumY, worldMaximumY,

            deviceMinimumX, deviceMaximumX, deviceMinimumY, deviceMaximumY)

 

        /// <summary>

        /// 매트릭스 설정하기

        /// </summary>

        /// <param name="worldMinimumX">월드 최소 X</param>

        /// <param name="worldMaximumX">월드 최대 X</param>

        /// <param name="worldMinimumY">월드 최소 Y</param>

        /// <param name="worldMaximumY">월드 최대 Y</param>

        /// <param name="deviceMinimumX">장치 최소 X</param>

        /// <param name="deviceMaximumX">장치 최대 X</param>

        /// <param name="deviceMinimumY">장치 최소 Y</param>

        /// <param name="deviceMaximumY">장치 최대 Y</param>

        private void SetMatrix

        (

            double worldMinimumX,

            double worldMaximumX,

            double worldMinimumY,

            double worldMaximumY,

            double deviceMinimumX,

            double deviceMaximumX,

            double deviceMinimumY,

            double deviceMaximumY

        )

        {

            this.worldToDeviceMatrix = Matrix.Identity;

 

            this.worldToDeviceMatrix.Translate(-worldMinimumX, -worldMinimumY);

 

            double scaleX = (deviceMaximumX - deviceMinimumX) / (worldMaximumX - worldMinimumX);

            double scaleY = (deviceMaximumY - deviceMinimumY) / (worldMaximumY - worldMinimumY);

 

            this.worldToDeviceMatrix.Scale(scaleX, scaleY);

 

            this.worldToDeviceMatrix.Translate(deviceMinimumX, deviceMinimumY);

 

            this.deviceToWorldMatrix = worldToDeviceMatrix;

 

            this.deviceToWorldMatrix.Invert();

        }

 

        #endregion

        #region 장치 포인트 구하기 - GetDevicePoint(worldPoint)

 

        /// <summary>

        /// 장치 포인트 구하기

        /// </summary>

        /// <param name="worldPoint">월드 포인트</param>

        /// <returns>장치 포인트</returns>

        private Point GetDevicePoint(Point worldPoint)

        {

            return this.worldToDeviceMatrix.Transform(worldPoint);

        }

 

        #endregion

        #region 월드 포인트 구하기 - GetWorldPoint(devicePoint)

 

        /// <summary>

        /// 월드 포인트 구하기

        /// </summary>

        /// <param name="devicePoint">장치 포인트</param>

        /// <returns>월드 포인트</returns>

        private Point GetWorldPoint(Point devicePoint)

        {

            return deviceToWorldMatrix.Transform(devicePoint);

        }

 

        #endregion

        #region 축 그리기 - DrawAxes(canvas, minimumX, maximumX, minimumY, maximumY)

 

        /// <summary>

        /// 축 그리기

        /// </summary>

        /// <param name="canvas">캔버스</param>

        /// <param name="minimumX">최소 X</param>

        /// <param name="maximumX">최대 X</param>

        /// <param name="minimumY">최소 Y</param>

        /// <param name="maximumY">최대 Y</param>

        private void DrawAxes(Canvas canvas, double minimumX, double maximumX, double minimumY, double maximumY)

        {

            const double TICK_WIDTH = 0.5;

 

            double startX = (int)minimumX;

 

            if(startX == minimumX)

            {

                startX++;

            }

 

            double stopX = (int)maximumX;

 

            if(stopX == maximumX)

            {

                stopX--;

            }

 

            Point point       = new Point(0, 0);

            Point originPoint = GetDevicePoint(point);

 

            GeometryGroup xAxisGeometryGroup = new GeometryGroup();

 

            xAxisGeometryGroup.Children.Add

            (

                new LineGeometry

                (

                    new Point(0                      , originPoint.Y),

                    new Point(this.canvas.ActualWidth, originPoint.Y)

                )

            );

 

            double y1 = -TICK_WIDTH / 2;

            double y2 =  TICK_WIDTH / 2;

 

            for(double x = startX; x <= stopX; x++)

            {

                Point point1 = new Point(x, y1);

                Point point2 = new Point(x, y2);

 

                xAxisGeometryGroup.Children.Add

                (

                    new LineGeometry

                    (

                        GetDevicePoint(point1),

                        GetDevicePoint(point2)

                    )

                );

            }

 

            Path xAxisPath = new Path();

 

            xAxisPath.Stroke          = Brushes.Black;

            xAxisPath.StrokeThickness = 1;

            xAxisPath.Data            = xAxisGeometryGroup;

 

            this.canvas.Children.Add(xAxisPath);

 

            double startY = (int)minimumY;

 

            if(startY == minimumY)

            {

                startY++;

            }

 

            double stopY = (int)maximumY;

 

            if(stopY == maximumY)

            {

                stopY--;

            }

 

            GeometryGroup yAxisGeometryGroup = new GeometryGroup();

 

            yAxisGeometryGroup.Children.Add

            (

                new LineGeometry

                (

                    new Point(originPoint.X, 0),

                    new Point(originPoint.X, this.canvas.ActualHeight)

                )

            );

 

            double x1 = -TICK_WIDTH / 2;

            double x2 =  TICK_WIDTH / 2;

 

            for(double y = startY; y <= stopY; y++)

            {

                Point point1 = new Point(x1, y);

                Point point2 = new Point(x2, y);

 

                yAxisGeometryGroup.Children.Add

                (

                    new LineGeometry

                    (

                        GetDevicePoint(point1),

                        GetDevicePoint(point2)

                    )

                );

            }

 

            Path yAxisPath = new Path();

 

            yAxisPath.Stroke          = Brushes.Black;

            yAxisPath.StrokeThickness = 1;

            yAxisPath.Data            = yAxisGeometryGroup;

 

            this.canvas.Children.Add(yAxisPath);

        }

 

        #endregion

        #region 다각선 설정하기 - SetPolyline(canvas, pointList)

 

        /// <summary>

        /// 다각선 설정하기

        /// </summary>

        /// <param name="canvas">캔버스</param>

        /// <param name="pointList">포인트 리스트</param>

        private void SetPolyline(Canvas canvas, List<Point> pointList)

        {

            PointCollection pointCollection = new PointCollection(pointList.Count);

 

            foreach(Point point in pointList)

            {

                pointCollection.Add(GetDevicePoint(point));

            }

 

            Polyline polyline = new Polyline();

 

            polyline.Stroke          = Brushes.Red;

            polyline.StrokeThickness = 1;

            polyline.Points          = pointCollection;

 

            canvas.Children.Add(polyline);

        }

 

        #endregion

        #region 곡선 그리기 - DrawCurve(canvas, minimumX, maximumX, minimumY, maximumY, equation)

 

        /// <summary>

        /// 곡선 그리기

        /// </summary>

        /// <param name="canvas">캔버스</param>

        /// <param name="minimumX">최소 X</param>

        /// <param name="maximumX">최대 X</param>

        /// <param name="minimumY">최소 Y</param>

        /// <param name="maximumY">최대 Y</param>

        /// <param name="equation">수식</param>

        private void DrawCurve(Canvas canvas, double minimumX, double maximumX, double minimumY, double maximumY, string equation)

        {

            string functionText = "using System;"                               +

                                  "public static class Evaluator"               +

                                  "{"                                           +

                                  "    public static double Evaluate(double x)" +

                                  "    {"                                       +

                                  "        return " + equation + ";"            +

                                  "    }"                                       +

                                  "}";

 

            CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider("C#");

 

            CompilerParameters compilerParameters = new CompilerParameters();

 

            compilerParameters.GenerateInMemory   = true;

            compilerParameters.GenerateExecutable = false;

 

            CompilerResults compilerResults = codeDomProvider.CompileAssemblyFromSource(compilerParameters, functionText);

 

            if(compilerResults.Errors.Count > 0)

            {

                string message = "Error compiling the expression.";

 

                foreach(CompilerError compilerError in compilerResults.Errors)

                {

                    message += "\n" + compilerError.ErrorText;

                }

 

                MessageBox.Show(message, "ERROR", MessageBoxButton.OK, MessageBoxImage.Error);

            }

            else

            {

                Type evaluatorType = compilerResults.CompiledAssembly.GetType("Evaluator");

 

                MethodInfo methodInfo = evaluatorType.GetMethod("Evaluate");

 

                Point point0 = GetWorldPoint(new Point(0, 0));

                Point point1 = GetWorldPoint(new Point(1, 1));

 

                double deltaX = point1.X - point0.X;

 

                List<Point> pointList = new List<Point>();

 

                bool lastPointInBounds = false;

 

                for(double x = minimumX; x <= maximumX; x += deltaX)

                {

                    bool pointInBounds = false;

 

                    try

                    {

                        object[] methodParameterArray = new object[] { x };

 

                        double y = (double)methodInfo.Invoke(null, methodParameterArray);

 

                        if(double.IsNaN(y))

                        {

                        }

                        else

                        {

                            if(y < minimumY)

                            {

                                y = minimumY;

                            }

                            else if(y > maximumY)

                            {

                                y = maximumY;

                            }

                            else

                            {

                                pointInBounds = true;

                            }

 

                            pointList.Add(new Point(x, y));

                        }

 

                        if(!pointInBounds)

                        {

                            if(lastPointInBounds)

                            {

                                SetPolyline(this.canvas, pointList);

 

                                pointList.Clear();

                            }

                            else

                            {

                                if(pointList.Count > 1)

                                {

                                    pointList.RemoveAt(0);

                                }

                            }

                        }

 

                        lastPointInBounds = pointInBounds;

                    }

                    catch(Exception exception)

                    {

                        MessageBox.Show(exception.Message);

 

                        return;

                    }

                }

 

                if(pointList.Count > 1)

                {

                    SetPolyline(this.canvas, pointList);

                }

            }

        }

 

        #endregion

        #region 그래프 그리기 - DrawGraph()

 

        /// <summary>

        /// 그래프 그리기

        /// </summary>

        private void DrawGraph()

        {

            this.canvas.Children.Clear();

 

            double worldMinimumX = double.Parse(this.minimymXTextBox.Text);

            double worldMaximumX = double.Parse(this.maximymXTextBox.Text);

            double worldMinimumY = double.Parse(this.minimumYTextBox.Text);

            double worldMaximumY = double.Parse(this.maximumYTextBox.Text);

 

            string equation = this.equationTextBox.Text;

 

            double deviceMaximumX = this.canvas.ActualWidth;

            double deviceMaximumY = this.canvas.ActualHeight;

 

            SetMatrix(worldMinimumX, worldMaximumX, worldMinimumY, worldMaximumY, 0, deviceMaximumX, deviceMaximumY, 0);

 

            DrawAxes(this.canvas, worldMinimumX, worldMaximumX, worldMinimumY, worldMaximumY);

 

            DrawCurve(this.canvas, worldMinimumX, worldMaximumX, worldMinimumY, worldMaximumY, equation);

        }

 

        #endregion

    }

}

 

------------------------------------------------------------------------------------------------------------------------

Posted by 사용자 icodebroker

댓글을 달아 주세요