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

■ 다각형 확장하기

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


TestProject.zip


MainForm.cs

 

 

using System;

using System.Collections.Generic;

using System.Drawing;

using System.Drawing.Drawing2D;

using System.Windows;

using System.Windows.Forms;

 

namespace TestProject

{

    /// <summary>

    /// 메인 폼

    /// </summary>

    public partial class MainForm : Form

    {

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

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

 

        #region Field

 

        /// <summary>

        /// 포인트 리스트

        /// </summary>

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

 

        /// <summary>

        /// 현재 포인트

        /// </summary>

        private PointF currentPoint;

 

        /// <summary>

        /// 그리기 여부

        /// </summary>

        private bool isDrawing = false;

 

        /// <summary>

        /// 확대 크기

        /// </summary>

        private int enlargeSize = 0;

 

        #endregion

 

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

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

 

        #region 생성자 - MainForm()

 

        /// <summary>

        /// 생성자

        /// </summary>

        public MainForm()

        {

            InitializeComponent();

 

            #region 이벤트를 설정한다.

 

            this.newMenuItem.Click            += newMenuItem_Click;

            this.canvasPictureBox.Paint       += canvasPictureBox_Paint;

            this.canvasPictureBox.MouseDown   += canvasPictureBox_MouseDown;

            this.canvasPictureBox.MouseMove   += canvasPictureBox_MouseMove;

            this.enlargeSizeHScrollBar.Scroll += enlargeSizeHScrollBar_Scroll;

 

            #endregion

        }

 

        #endregion

 

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

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

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

 

        #region 새로 그리기 메뉴 항목 클릭시 처리하기 - newMenuItem_Click(sender, e)

 

        /// <summary>

        /// 새로 그리기 메뉴 항목 클릭시 처리하기

        /// </summary>

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

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

        private void newMenuItem_Click(object sender, EventArgs e)

        {

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

 

            this.canvasPictureBox.Refresh();

 

            this.enlargeSizeHScrollBar.Value = 0;

        }

 

        #endregion

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

 

        /// <summary>

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

        /// </summary>

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

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

        private void canvasPictureBox_MouseDown(object sender, MouseEventArgs e)

        {

            if(e.Button == MouseButtons.Right)

            {

                if(this.isDrawing)

                {

                    this.isDrawing = false;

 

                    if(IsPolygonOrientedClockwise(this.pointList))

                    {

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

 

                        for(int i = this.pointList.Count - 1; i >= 0; i--)

                        {

                            pointList.Add(this.pointList[i]);

                        }

 

                        this.pointList = pointList;

                    }

                }

            }

            else

            {

                if(!this.isDrawing)

                {

                    this.isDrawing = true;

 

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

                }

 

                this.currentPoint = e.Location;

 

                this.pointList.Add(this.currentPoint);

            }

 

            this.canvasPictureBox.Refresh();

        }

 

        #endregion

        #region 캔버스 픽처 박스 마우스 이동시 처리하기 - canvasPictureBox_MouseMove(sender, e)

 

        /// <summary>

        /// 캔버스 픽처 박스 마우스 이동시 처리하기

        /// </summary>

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

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

        private void canvasPictureBox_MouseMove(object sender, MouseEventArgs e)

        {

            if(!this.isDrawing)

            {

                return;

            }

 

            this.currentPoint = e.Location;

 

            this.canvasPictureBox.Refresh();

        }

 

        #endregion

        #region 캔버스 픽처 박스 페인트시 처리하기 - canvasPictureBox_Paint(sender, e)

 

        /// <summary>

        /// 캔버스 픽처 박스 페인트시 처리하기

        /// </summary>

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

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

        private void canvasPictureBox_Paint(object sender, PaintEventArgs e)

        {

            e.Graphics.Clear(this.canvasPictureBox.BackColor);

 

            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

 

            if(this.isDrawing)

            {

                if(this.pointList.Count >= 2)

                {

                    e.Graphics.DrawLines(Pens.Red, this.pointList.ToArray());

                }

 

                e.Graphics.DrawLine(Pens.Pink, this.pointList[this.pointList.Count - 1], this.currentPoint);

            }

            else

            {

                if(this.pointList.Count >= 3)

                {

                    e.Graphics.DrawPolygon(Pens.Red, this.pointList.ToArray());

 

                    if(this.enlargeSize > 0)

                    {

                        DrawEnlargedPolygon(e.Graphics);

                    }

                }

            }

        }

 

        #endregion

        #region 크기 확대 수평 스크롤바 스크롤시 처리하기 - enlargeSizeHScrollBar_Scroll(sender, e)

 

        /// <summary>

        /// 크기 확대 수평 스크롤바 스크롤시 처리하기

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void enlargeSizeHScrollBar_Scroll(object sender, ScrollEventArgs e)

        {

            this.enlargeSizeLabel.Text = this.enlargeSizeHScrollBar.Value.ToString();

 

            this.enlargeSize = this.enlargeSizeHScrollBar.Value;

 

            this.canvasPictureBox.Refresh();

        }

 

        #endregion

 

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

 

        #region 부호 있는 다각형 영역 구하기 - GetSignedPolygonArea(pointList)

 

        /// <summary>

        /// 부호 있는 다각형 영역 구하기

        /// </summary>

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

        /// <returns>부호 있는 다각형 영역</returns>

        private float GetSignedPolygonArea(List<PointF> pointList)

        {

            int pointCount = pointList.Count;

 

            PointF[] pointArray = new PointF[pointCount + 1];

 

            pointList.CopyTo(pointArray, 0);

 

            pointArray[pointCount] = pointList[0];

 

            float area = 0;

 

            for(int i = 0; i < pointCount; i++)

            {

                area += (pointArray[i + 1].X - pointArray[i].X) * (pointArray[i + 1].Y + pointArray[i].Y) / 2;

            }

 

            return area;

        }

 

        #endregion

        #region 다각형 시계 방향 구성 여부 구하기 - IsPolygonOrientedClockwise(pointList)

 

        /// <summary>

        /// 다각형 시계 방향 구성 여부 구하기

        /// </summary>

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

        /// <returns>다각형 시계 방향 구성 여부</returns>

        public bool IsPolygonOrientedClockwise(List<PointF> pointList)

        {

            return (GetSignedPolygonArea(pointList) < 0);

        }

 

        #endregion

        #region 교차 포인트 찾기 - FindIntersectionPoint(point1, point2, point3, point4,

            lineIntersect, segmentIntersect, intersectionPoint, closePoint1, closePoint2)

 

        /// <summary>

        /// 교차 포인트 찾기

        /// </summary>

        /// <param name="point1">포인트 1</param>

        /// <param name="point2">포인트 2</param>

        /// <param name="point3">포인트 3</param>

        /// <param name="point4">포인트 4</param>

        /// <param name="lineIntersect">직선 교차 여부</param>

        /// <param name="segmentIntersect">세그먼트 교차 여부</param>

        /// <param name="intersectionPoint">교차 포인트</param>

        /// <param name="closePoint1">근접 포인트 1</param>

        /// <param name="closePoint2">근접 포인트 2</param>

        private void FindIntersectionPoint(PointF point1, PointF point2, PointF point3, PointF point4,

            out bool lineIntersect, out bool segmentIntersect, out PointF intersectionPoint, out PointF closePoint1, out PointF closePoint2)

        {

            float dx12 = point2.X - point1.X;

            float dy12 = point2.Y - point1.Y;

            float dx34 = point4.X - point3.X;

            float dy34 = point4.Y - point3.Y;

 

            float denominator = (dy12 * dx34 - dx12 * dy34);

 

            float t1 = ((point1.X - point3.X) * dy34 + (point3.Y - point1.Y) * dx34) / denominator;

 

            if(float.IsInfinity(t1))

            {

                lineIntersect    = false;

                segmentIntersect = false;

 

                intersectionPoint = new PointF(float.NaN, float.NaN);

 

                closePoint1 = new PointF(float.NaN, float.NaN);

                closePoint2 = new PointF(float.NaN, float.NaN);

 

                return;

            }

 

            lineIntersect = true;

 

            float t2 = ((point3.X - point1.X) * dy12 + (point1.Y - point3.Y) * dx12) / -denominator;

 

            intersectionPoint = new PointF(point1.X + dx12 * t1, point1.Y + dy12 * t1);

 

            segmentIntersect = ((t1 >= 0) && (t1 <= 1) && (t2 >= 0) && (t2 <= 1));

 

            if(t1 < 0)

            {

                t1 = 0;

            }

            else if(t1 > 1)

            {

                t1 = 1;

            }

 

            if(t2 < 0)

            {

                t2 = 0;

            }

            else if(t2 > 1)

            {

                t2 = 1;

            }

 

            closePoint1 = new PointF(point1.X + dx12 * t1, point1.Y + dy12 * t1);

            closePoint2 = new PointF(point3.X + dx34 * t2, point3.Y + dy34 * t2);

        }

 

        #endregion

        #region 확장 다각형 포인트 리스트 구하기 - GetEnlargedPolygonPointList(sourcePointList, offset)

 

        /// <summary>

        /// 확장 다각형 포인트 리스트 구하기

        /// </summary>

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

        /// <param name="offset">오프셋</param>

        /// <returns>확장 다각형 포인트 리스트</returns>

        private List<PointF> GetEnlargedPolygonPointList(List<PointF> sourcePointList, float offset)

        {

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

 

            int pointCount = sourcePointList.Count;

 

            for(int j = 0; j < pointCount; j++)

            {

                int i = (j - 1);

 

                if(i < 0)

                {

                    i += pointCount;

                }

 

                int k = (j + 1) % pointCount;

 

                Vector v1 = new Vector

                (

                    sourcePointList[j].X - sourcePointList[i].X,

                    sourcePointList[j].Y - sourcePointList[i].Y

                );

 

                v1.Normalize();

 

                v1 *= offset;

 

                Vector n1 = new Vector(-v1.Y, v1.X);

 

                PointF pij1 = new PointF

                (

                    (float)(sourcePointList[i].X + n1.X),

                    (float)(sourcePointList[i].Y + n1.Y)

                );

 

                PointF pij2 = new PointF

                (

                    (float)(sourcePointList[j].X + n1.X),

                    (float)(sourcePointList[j].Y + n1.Y)

                );

 

                Vector v2 = new Vector

                (

                    sourcePointList[k].X - sourcePointList[j].X,

                    sourcePointList[k].Y - sourcePointList[j].Y

                );

 

                v2.Normalize();

 

                v2 *= offset;

 

                Vector n2 = new Vector(-v2.Y, v2.X);

 

                PointF pjk1 = new PointF

                (

                    (float)(sourcePointList[j].X + n2.X),

                    (float)(sourcePointList[j].Y + n2.Y)

                );

 

                PointF pjk2 = new PointF

                (

                    (float)(sourcePointList[k].X + n2.X),

                    (float)(sourcePointList[k].Y + n2.Y)

                );

 

                bool lineIntersect;

                bool segmentIntersect;

 

                PointF intersectionPoint;

                PointF closePoint1;

                PointF closePoint2;

 

                FindIntersectionPoint

                (

                    pij1,

                    pij2,

                    pjk1,

                    pjk2,

                    out lineIntersect,

                    out segmentIntersect,

                    out intersectionPoint,

                    out closePoint1,

                    out closePoint2

                );

 

                targetPointList.Add(intersectionPoint);

            }

 

            return targetPointList;

        }

 

        #endregion

        #region 확장 다각형 그리기 - DrawEnlargedPolygon(graphics)

 

        /// <summary>

        /// 확장 다각형 그리기

        /// </summary>

        /// <param name="graphics">그래픽스</param>

        private void DrawEnlargedPolygon(Graphics graphics)

        {

            List<PointF> pointList = GetEnlargedPolygonPointList(this.pointList, this.enlargeSizeHScrollBar.Value);

 

            graphics.DrawPolygon(Pens.Green, pointList.ToArray());

        }

 

        #endregion

    }

}

 

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

Posted by 사용자 icodebroker

댓글을 달아 주세요