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

■ PolyBezierSegment 클래스 : 곡선 그리기

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


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="PolyBezierSegment 클래스 : 곡선 그리기"

    FontFamily="나눔고딕코딩"

    FontSize="16">

    <Grid>

        <Grid.ColumnDefinitions>

            <ColumnDefinition Width="*"    />

            <ColumnDefinition Width="Auto" />

        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>

            <RowDefinition Height="Auto" />

            <RowDefinition Height="*"    />

        </Grid.RowDefinitions>

        <ScrollBar Name="tensionScrollBar" Grid.Row="0" Grid.Column="0"

            Margin="10"

            Orientation="Horizontal"

            Minimum="0"

            Maximum="50"

            SmallChange="1"

            LargeChange="10"

            Value="10" />

        <Label Name="tensionLabel" Grid.Row="0" Grid.Column="1"

            Margin="10"

            Content="1.0" />

        <Canvas Name="canvas" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"

            Background="White" />

    </Grid>

</Window>

 

 

MainWindow.xaml.cs

 

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Controls.Primitives;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Shapes;

 

namespace TestProject

{

    /// <summary>

    /// 메인 윈도우

    /// </summary>

    public partial class MainWindow : Window

    {

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

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

 

        #region Field

 

        /// <summary>

        /// 텐션

        /// </summary>

        private double tension = 1.0;

 

        /// <summary>

        /// 그리기 여부

        /// </summary>

        private bool isDrawing = true;

 

        /// <summary>

        /// 포인트 리스트

        /// </summary>

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

 

        #endregion

 

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

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

 

        #region 생성자 - MainWindow()

 

        /// <summary>

        /// 생성자

        /// </summary>

        public MainWindow()

        {

            InitializeComponent();

 

            Loaded                       += Window_Loaded;

            this.tensionScrollBar.Scroll += tensionScrollBar_Scroll;

            this.canvas.MouseDown        += canvas_MouseDown;

        }

 

        #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)

        {

            DrawCurve(1.0);

        }

 

        #endregion

        #region 텐션 스크롤바 스크롤시 처리하기 - tensionScrollBar_Scroll(sender, e)

 

        /// <summary>

        /// 텐션 스크롤바 스크롤시 처리하기

        /// </summary>

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

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

        private void tensionScrollBar_Scroll(object sender, ScrollEventArgs e)

        {

            this.tension = this.tensionScrollBar.Value / 10;

 

            this.tensionLabel.Content = this.tension.ToString("0.0");

 

            DrawCurve(this.tension);

        }

 

        #endregion

        #region 캔버스 마우스 DOWN 처리하기 - canvas_MouseDown(sender, e)

 

        /// <summary>

        /// 캔버스 마우스 DOWN 처리하기

        /// </summary>

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

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

        private void canvas_MouseDown(object sender, MouseButtonEventArgs e)

        {

            if(this.isDrawing)

            {

                if(e.ChangedButton == MouseButton.Left)

                {

                    this.pointList.Add(e.GetPosition(this.canvas));

                }

                else

                {

                    this.isDrawing = false;

                }

            }

            else

            {

                this.isDrawing = true;

 

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

 

                this.pointList.Add(e.GetPosition(this.canvas));

            }

 

            DrawCurve(this.tension);

        }

 

        #endregion

 

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

 

        #region 곡선 포인트 배열 구하기 - GetCurvePointArray(sourcePointArray, tension)

 

        /// <summary>

        /// 곡선 포인트 배열 구하기

        /// </summary>

        /// <param name="sourcePointArray">소스 포인트 배열</param>

        /// <param name="tension">텐션</param>

        /// <returns>곡선 포인트 배열</returns>

        private Point[] GetCurvePointArray(Point[] sourcePointArray, double tension)

        {

            if(sourcePointArray.Length < 2)

            {

                return null;

            }

 

            double controlScale = tension / 0.5 * 0.175;

 

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

 

            targetPointList.Add(sourcePointArray[0]);

 

            for(int i = 0; i < sourcePointArray.Length - 1; i++)

            {

                Point previousPoint = sourcePointArray[Math.Max(i - 1, 0)];

                Point point         = sourcePointArray[i];

                Point nextPoint     = sourcePointArray[i + 1];

                Point nextNextPoint = sourcePointArray[Math.Min(i + 2, sourcePointArray.Length - 1)];

 

                double deltaX1 = nextPoint.X - previousPoint.X;

                double deltaY1 = nextPoint.Y - previousPoint.Y;

 

                Point point1 = sourcePointArray[i];

                Point point4 = nextPoint;

 

                double deltaX2 = nextPoint.X - previousPoint.X;

                double deltaY2 = nextPoint.Y - previousPoint.Y;

 

                Point point2 = new Point

                (

                    point.X + controlScale * deltaX2,

                    point.Y + controlScale * deltaY2

                );

 

                deltaX2 = nextNextPoint.X - point.X;

                deltaY2 = nextNextPoint.Y - point.Y;

 

                Point point3 = new Point

                (

                    nextPoint.X - controlScale * deltaX2,

                    nextPoint.Y - controlScale * deltaY2

                );

 

                targetPointList.Add(point2);

                targetPointList.Add(point3);

                targetPointList.Add(point4);

            }

 

            return targetPointList.ToArray();

        }

 

        #endregion

        #region 베지어 경로 구하기 - GetBezierPath(sourcePointArray)

 

        /// <summary>

        /// 베지어 경로 구하기

        /// </summary>

        /// <param name="sourcePointArray">소스 포인트 배열</param>

        /// <returns>베이저 경로</returns>

        private Path GetBezierPath(Point[] sourcePointArray)

        {

            Path path = new Path();

 

            PathGeometry pathGeometry = new PathGeometry();

 

            path.Data = pathGeometry;

 

            PathFigure pathFigure = new PathFigure();

 

            pathGeometry.Figures.Add(pathFigure);

 

            pathFigure.StartPoint = sourcePointArray[0];

 

            PathSegmentCollection pathSegmentCollection = new PathSegmentCollection();

 

            pathFigure.Segments = pathSegmentCollection;

 

            PointCollection pointCollection = new PointCollection(sourcePointArray.Length - 1);

 

            for(int i = 1; i < sourcePointArray.Length; i++)

            {

                pointCollection.Add(sourcePointArray[i]);

            }

 

            PolyBezierSegment polyBezierSegment = new PolyBezierSegment();

 

            polyBezierSegment.Points = pointCollection;

 

            pathSegmentCollection.Add(polyBezierSegment);

 

            return path;

        }

 

        #endregion

        #region 곡선 경로 구하기 - GetCurvePath(sourcePointArray, tension)

 

        /// <summary>

        /// 곡선 경로 구하기

        /// </summary>

        /// <param name="sourcePointArray">소스 포인트 배열</param>

        /// <param name="tension">텐션</param>

        /// <returns>곡선 경로</returns>

        private Path GetCurvePath(Point[] sourcePointArray, double tension)

        {

            if(sourcePointArray.Length < 2)

            {

                return null;

            }

 

            Point[] targetPointArray = GetCurvePointArray(sourcePointArray, tension);

 

            return GetBezierPath(targetPointArray.ToArray());

        }

 

        #endregion

        #region 곡선 그리기 - DrawCurve(tension)

 

        /// <summary>

        /// 곡선 그리기

        /// </summary>

        /// <param name="tension">텐션</param>

        private void DrawCurve(double tension)

        {

            this.canvas.Children.Clear();

 

            if(this.pointList.Count > 1)

            {

                Path path = GetCurvePath(this.pointList.ToArray(), tension);

 

                path.Stroke          = Brushes.LightBlue;

                path.StrokeThickness = 5;

 

                if(this.isDrawing)

                {

                    path.StrokeDashArray.Add(2);

                    path.StrokeDashArray.Add(2);

                }

                

                this.canvas.Children.Add(path);

            }

 

            foreach(Point point in pointList)

            {

                Rectangle rectangle = new Rectangle();

 

                rectangle.Width           = 6;

                rectangle.Height          = 6;

                rectangle.Stroke          = Brushes.Black;

                rectangle.StrokeThickness = 1;

                rectangle.Fill            = Brushes.White;

 

                Canvas.SetLeft(rectangle, point.X - 3);

                Canvas.SetTop (rectangle, point.Y - 3);

 

                this.canvas.Children.Add(rectangle);

            }

        }

 

        #endregion

    }

}

 

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

Posted by 사용자 icodebroker

댓글을 달아 주세요