첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
본 블로그는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 블로그 콘텐츠 향상을 위해 쓰여집니다.

728x90
반응형
728x170

TestProject.zip
다운로드

▶ MainForm.cs

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace TestProject
{
    /// <summary>
    /// 메인 폼
    /// </summary>
    public partial class MainForm : Form
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - MainForm()

        /// <summary>
        /// 생성자
        /// </summary>
        public MainForm()
        {
            InitializeComponent();

            #region 이벤트를 설정한다.

            this.drawButton.Click += drawButton_Click;

            #endregion
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Private
        //////////////////////////////////////////////////////////////////////////////// Event

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

        /// <summary>
        /// 그리기 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void drawButton_Click(object sender, EventArgs e)
        {
            Cursor = Cursors.WaitCursor;

            try
            {
                float angleA       = (float)((double)this.anglaANumericUpDown.Value * Math.PI / 180.0);
                float lengthScaleA = float.Parse(this.lengthScaleATextBox.Text);
                float angleB       = (float)((double)this.angleBNumericUpDown.Value * Math.PI / 180.0);
                float lengthScaleB = float.Parse(this.lengthScaleBTextBox.Text);

                this.canvasPictureBox.Image = GetCurlyTreeFractalBitmap
                (
                    this.canvasPictureBox.ClientSize.Width,
                    this.canvasPictureBox.ClientSize.Height,
                    (int)this.levelNumericUpDown.Value,
                    angleA,
                    angleB,
                    lengthScaleA,
                    lengthScaleB
                );
            }
            catch
            {
            }

            Cursor = Cursors.Default;
        }

        #endregion

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

        #region 가지 테두리 사각형 찾기 - FindBranchBoundRectangle(x, y, direction, level, length, angleA, angleB,
            lengthScaleA, lengthScaleB, minimumX, maximumX, minimumY, maximumY)

        /// <summary>
        /// 가지 테두리 사각형 찾기
        /// </summary>
        /// <param name="x">X 좌표</param>
        /// <param name="y">Y 좌표</param>
        /// <param name="direction">방향</param>
        /// <param name="level">레벨</param>
        /// <param name="length">길이</param>
        /// <param name="angleA">각도 A</param>
        /// <param name="angleB">각도 B</param>
        /// <param name="lengthScaleA">길이 스케일 A</param>
        /// <param name="lengthScaleB">길이 스케일 B</param>
        /// <param name="minimumX">최소 X</param>
        /// <param name="maximumX">최대 X</param>
        /// <param name="minimumY">최소 Y</param>
        /// <param name="maximumY">최대 Y</param>
        private void FindBranchBoundRectangle(float x, float y, float direction, int level, float length, float angleA, float angleB,
            float lengthScaleA, float lengthScaleB, ref float minimumX, ref float maximumX, ref float minimumY, ref float maximumY)
        {
            if(length < 0.1)
            {
                return;
            }

            x += (float)(length * Math.Cos(direction));

            if(minimumX > x)
            {
                minimumX = x;
            }

            if(maximumX < x)
            {
                maximumX = x;
            }

            y += (float)(length * Math.Sin(direction));

            if(minimumY > y)
            {
                minimumY = y;
            }

            if(maximumY < y)
            {
                maximumY = y;
            }

            if(level > 0)
            {
                FindBranchBoundRectangle
                (
                    x,
                    y,
                    direction + angleA,
                    level - 1,
                    length * lengthScaleA,
                    angleA,
                    angleB,
                    lengthScaleA,
                    lengthScaleB,
                    ref minimumX,
                    ref maximumX,
                    ref minimumY,
                    ref maximumY
                );

                FindBranchBoundRectangle
                (
                    x,
                    y,
                    direction + angleB,
                    level - 1,
                    length * lengthScaleB,
                    angleA,
                    angleB,
                    lengthScaleA,
                    lengthScaleB,
                    ref minimumX,
                    ref maximumX,
                    ref minimumY,
                    ref maximumY
                );
            }
        }

        #endregion
        #region 변환 설정하기 - SetTransformation(graphics, worldRectangle, deviceRectangle, invertX, invertY)

        /// <summary>
        /// 변환 설정하기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        /// <param name="worldRectangle">세계 사각형</param>
        /// <param name="deviceRectangle">장치 사각형</param>
        /// <param name="invertX">X축 반전 여부</param>
        /// <param name="invertY">Y축 반전 여부</param>
        private void SetTransformation(Graphics graphics, RectangleF worldRectangle, RectangleF deviceRectangle, bool invertX, bool invertY)
        {
            PointF[] devicePointArray =
            {
                new PointF(deviceRectangle.Left , deviceRectangle.Top   ),
                new PointF(deviceRectangle.Right, deviceRectangle.Top   ),
                new PointF(deviceRectangle.Left , deviceRectangle.Bottom)
            };

            if(invertX)
            {
                devicePointArray[0].X = deviceRectangle.Right;
                devicePointArray[1].X = deviceRectangle.Left;
                devicePointArray[2].X = deviceRectangle.Right;
            }

            if(invertY)
            {
                devicePointArray[0].Y = deviceRectangle.Bottom;
                devicePointArray[1].Y = deviceRectangle.Bottom;
                devicePointArray[2].Y = deviceRectangle.Top;
            }

            graphics.Transform = new Matrix(worldRectangle, devicePointArray);
        }

        #endregion
        #region 왜곡 없이 변환 설정하기 - SetTransformationWithoutDisortion(graphics, worldRectangle, deviceRectangle, invertX, invertY)

        /// <summary>
        /// 왜곡 없이 변환 설정하기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        /// <param name="worldRectangle">세계 사각형</param>
        /// <param name="deviceRectangle">장치 사각형</param>
        /// <param name="invertX">X축 반전 여부</param>
        /// <param name="invertY">Y축 반전 여부</param>
        private void SetTransformationWithoutDisortion(Graphics graphics, RectangleF worldRectangle, RectangleF deviceRectangle, bool invertX, bool invertY)
        {
            float worldAspect  = worldRectangle.Width  / worldRectangle.Height;
            float deviceAspect = deviceRectangle.Width / deviceRectangle.Height;

            float worldCenterX = worldRectangle.X + worldRectangle.Width  / 2f;
            float worldCenterY = worldRectangle.Y + worldRectangle.Height / 2f;

            if(worldAspect > deviceAspect)
            {
                float worldHeight = worldRectangle.Width / deviceAspect;

                worldRectangle = new RectangleF
                (
                    worldRectangle.Left,
                    worldCenterY - worldHeight / 2f,
                    worldRectangle.Width,
                    worldHeight
                );
            }
            else
            {
                float worldWidth = deviceAspect * worldRectangle.Height;
 
                worldRectangle = new RectangleF
                (
                    worldCenterX - worldWidth / 2f,
                    worldRectangle.Top,
                    worldWidth,
                    worldRectangle.Height
                );
            }

            SetTransformation(graphics, worldRectangle, deviceRectangle, invertX, invertY);
        }

        #endregion
        #region 거리 구하기 - GetDistance(point1, point2)

        /// <summary>
        /// 거리 구하기
        /// </summary>
        /// <param name="point1">포인트 1</param>
        /// <param name="point2">포인트 2</param>
        /// <returns>거리</returns>
        private float GetDistance(PointF point1, PointF point2)
        {
            float dx = point1.X - point2.X;
            float dy = point1.Y - point2.Y;

            return (float)(Math.Sqrt(dx * dx + dy * dy));
        }

        #endregion
        #region 가지 그리기 - DrawBranch(graphics, pen, x, y, direction, level, length, angleA, angleB, lengthScaleA, lengthScaleB)

        /// <summary>
        /// 가지 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        /// <param name="pen">펜</param>
        /// <param name="x">X 좌표</param>
        /// <param name="y">Y 좌표</param>
        /// <param name="direction">방향</param>
        /// <param name="level">레벨</param>
        /// <param name="length">길이</param>
        /// <param name="angleA">각도 A</param>
        /// <param name="angleB">각도 B</param>
        /// <param name="lengthScaleA">길이 스케일 A</param>
        /// <param name="lengthScaleB">길이 스케일 B</param>
        private void DrawBranch(Graphics graphics, Pen pen, float x, float y, float direction, int level, float length,
            float angleA, float angleB, float lengthScaleA, float lengthScaleB)
        {
            if(length < 0.1)
            {
                return;
            }

            PointF point1 = new PointF(x, y);

            x += (float)(length * Math.Cos(direction));
            y += (float)(length * Math.Sin(direction));

            PointF point2 = new PointF(x, y);
            
            pen.Width = GetDistance(point1, point2) / 10f;

            graphics.DrawLine(pen, point1, point2);

            if(level > 0)
            {
                DrawBranch
                (
                    graphics,
                    pen,
                    x,
                    y,
                    direction + angleA,
                    level - 1,
                    length * lengthScaleA,
                    angleA,
                    angleB,
                    lengthScaleA,
                    lengthScaleB
                );

                DrawBranch
                (
                    graphics,
                    pen,
                    x,
                    y,
                    direction + angleB,
                    level - 1,
                    length * lengthScaleB,
                    angleA,
                    angleB,
                    lengthScaleA,
                    lengthScaleB
                );
            }
        }

        #endregion
        #region 곱슬 나무 프랙탈 비트맵 구하기 - GetCurlyTreeFractalBitmap(width, height, level, angleA, angleB, lengthScaleA, lengthScaleB)

        /// <summary>
        /// 곱슬 나무 프랙탈 비트맵 구하기
        /// </summary>
        /// <param name="width">너비</param>
        /// <param name="height">높이</param>
        /// <param name="level">레벨</param>
        /// <param name="angleA">각도 A</param>
        /// <param name="angleB">각도 B</param>
        /// <param name="lengthScaleA">길이 스케일 A</param>
        /// <param name="lengthScaleB">길이 스케일 B</param>
        /// <returns>곱슬 나무 프랙탈 비트맵</returns>
        private Bitmap GetCurlyTreeFractalBitmap(int width, int height, int level, float angleA, float angleB, float lengthScaleA, float lengthScaleB)
        {
            float minimumX = 0;
            float maximumX = 0;
            float minimumY = 0;
            float maximumY = 0;

            float startLength = height * 0.33f;

            const float START_THICKNES  = 10;
            const float START_DIRECTION = (float)(Math.PI * 0.5);

            FindBranchBoundRectangle
            (
                0,
                0,
                START_DIRECTION,
                level,
                startLength,
                angleA,
                angleB,
                lengthScaleA,
                lengthScaleB,
                ref minimumX,
                ref maximumX,
                ref minimumY,
                ref maximumY
            );

            Bitmap bitmap = new Bitmap(width, height);

            using(Graphics graphics = Graphics.FromImage(bitmap))
            {
                graphics.Clear(this.canvasPictureBox.BackColor);

                graphics.SmoothingMode = SmoothingMode.AntiAlias;

                minimumX -= START_THICKNES;
                maximumX += START_THICKNES;
                minimumY -= START_THICKNES;
                maximumY += START_THICKNES;

                RectangleF worldRectangle = new RectangleF
                (
                    minimumX,
                    minimumY,
                    maximumX - minimumX,
                    maximumY - minimumY
                );

                const int MARGIN = 4;

                RectangleF deviceRectangle = new RectangleF
                (
                    MARGIN,
                    MARGIN,
                    width  - 2 * MARGIN,
                    height - 2 * MARGIN
                );

                SetTransformationWithoutDisortion(graphics, worldRectangle, deviceRectangle, false, true);

                using(Pen pen = new Pen(Color.Green, 1))
                {
                    DrawBranch
                    (
                        graphics,
                        pen,
                        0,
                        0,
                        START_DIRECTION,
                        level,
                        startLength,
                        angleA,
                        angleB,
                        lengthScaleA,
                        lengthScaleB
                    );
                }
            }

            return bitmap;
        }

        #endregion
    }
}
728x90
반응형
그리드형
Posted by 사용자 icodebroker

댓글을 달아 주세요