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

■ 이미지 나선 그리기

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


TestProject.zip



MainForm.cs

 

 

using System;

using System.Collections.Generic;

using System.Drawing;

using System.Drawing.Drawing2D;

using System.Drawing.Imaging;

using System.IO;

using System.Windows.Forms;

 

namespace TestProject

{

    /// <summary>

    /// 메인 폼

    /// </summary>

    public partial class MainForm : Form

    {

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

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

 

        #region Field

 

        /// <summary>

        /// 소스 비트맵

        /// </summary>

        private Bitmap sourceBitmap = Properties.Resources.cat;

 

        /// <summary>

        /// 배경 이미지

        /// </summary>

        private Bitmap backgroundImage = null;

 

        /// <summary>

        /// 배경 색상

        /// </summary>

        private Color backgroundColor = Color.White;

 

        #endregion

 

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

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

 

        #region 생성자 - MainForm()

 

        /// <summary>

        /// 생성자

        /// </summary>

        public MainForm()

        {

            InitializeComponent();

 

            this.openMenuItem.Click            += openMenuItem_Click;

            this.backgroundColorMenuItem.Click += backgroundColorMenuItem_Click;

            this.backgroundImageMenuItem.Click += backgroundImageMenuItem_Click;

            this.saveAsMenuItem.Click          += saveAsMenuItem_Click;

            this.exitMenuItem.Click            += exitMenuItem_Click;

            this.drawButton.Click              += drawButton_Click;

        }

 

        #endregion

 

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

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

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

 

        #region 파일 열기 메뉴 항목 클릭시 처리하기 - openMenuItem_Click(sender, e)

 

        /// <summary>

        /// 파일 열기 메뉴 항목 클릭시 처리하기

        /// </summary>

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

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

        private void openMenuItem_Click(object sender, EventArgs e)

        {

            if(this.openFileDialog.ShowDialog() == DialogResult.OK)

            {

                this.sourceBitmap = GetBitmap(this.openFileDialog.FileName);

            }

        }

 

        #endregion

        #region 배경 색상 메뉴 항목 클릭시 처리하기 - backgroundColorMenuItem_Click(sender, e)

 

        /// <summary>

        /// 배경 색상 메뉴 항목 클릭시 처리하기

        /// </summary>

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

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

        private void backgroundColorMenuItem_Click(object sender, EventArgs e)

        {

            this.colorDialog.Color = this.backgroundColor;

 

            if(this.colorDialog.ShowDialog() == DialogResult.OK)

            {

                this.backgroundColor = this.colorDialog.Color;

 

                Bitmap bitmap = new Bitmap(16, 16);

 

                using(Graphics graphics = Graphics.FromImage(bitmap))

                {

                    graphics.Clear(backgroundColor);

 

                    graphics.DrawRectangle(Pens.Black, 0, 0, 15, 15);

                }

 

                this.backgroundColorMenuItem.Image = bitmap;

 

                this.backgroundImage = null;

            }

        }

 

        #endregion

        #region 배경 이미지 메뉴 항목 클릭시 처리하기 - backgroundImageMenuItem_Click(sender, e)

 

        /// <summary>

        /// 배경 이미지 메뉴 항목 클릭시 처리하기

        /// </summary>

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

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

        private void backgroundImageMenuItem_Click(object sender, EventArgs e)

        {

            if(this.openFileDialog.ShowDialog() == DialogResult.OK)

            {

                this.backgroundImage = GetBitmap(this.openFileDialog.FileName);

 

                this.witdhTextBox.Text  = this.backgroundImage.Width.ToString();

                this.heightTextBox.Text = this.backgroundImage.Height.ToString();

            }

        }

 

        #endregion

        #region 다른 이름으로 저장 메뉴 항목 클릭시 처리하기 - saveAsMenuItem_Click(sender, e)

 

        /// <summary>

        /// 다른 이름으로 저장 메뉴 항목 클릭시 처리하기

        /// </summary>

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

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

        private void saveAsMenuItem_Click(object sender, EventArgs e)

        {

            if(this.saveFileDialog.ShowDialog() == DialogResult.OK)

            {

                try

                {

                    SaveImage(this.resultPictureBox.Image, this.saveFileDialog.FileName);

                }

                catch(Exception exception)

                {

                    MessageBox.Show(exception.Message);

                }

            }

        }

 

        #endregion

        #region 종료 메뉴 항목 클릭시 처리하기 - exitMenuItem_Click(sender, e)

 

        /// <summary>

        /// 종료 메뉴 항목 클릭시 처리하기

        /// </summary>

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

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

        private void exitMenuItem_Click(object sender, EventArgs e)

        {

            Close();

        }

 

        #endregion

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

 

            DrawSpiral();

 

            Cursor = Cursors.Default;

        }

 

        #endregion

 

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

 

        #region 비트맵 구하기 - GetBitmap(filePath)

 

        /// <summary>

        /// 비트맵 구하기

        /// </summary>

        /// <param name="filePath">파일 경로</param>

        /// <returns>비트맵</returns>

        private Bitmap GetBitmap(string filePath)

        {

            try

            {

                using(Bitmap bitmap = new Bitmap(filePath))

                {

                    return new Bitmap(bitmap);

                }

            }

            catch(Exception exception)

            {

                MessageBox.Show(exception.Message);

 

                return null;

            }

        }

 

        #endregion

        #region 이미지 저장하기 - SaveImage(image, filePath)

 

        /// <summary>

        /// 이미지 저장하기

        /// </summary>

        /// <param name="image">이미지</param>

        /// <param name="filePath">파일 경로</param>

        private void SaveImage(Image image, string filePath)

        {

            try

            {

                string extension = Path.GetExtension(filePath);

 

                switch(extension.ToLower())

                {

                    case ".bmp"  : image.Save(filePath, ImageFormat.Bmp ); break;

                    case ".exif" : image.Save(filePath, ImageFormat.Exif); break;

                    case ".gif"  : image.Save(filePath, ImageFormat.Gif ); break;

                    case ".jpg"  :

                    case ".jpeg" : image.Save(filePath, ImageFormat.Jpeg); break;

                    case ".png"  : image.Save(filePath, ImageFormat.Png ); break;

                    case ".tif"  :

                    case ".tiff" : image.Save(filePath, ImageFormat.Tiff); break;

                    default      : throw new NotSupportedException("Unknown file extension " + extension);

                }

            }

            catch(Exception exception)

            {

                MessageBox.Show(exception.Message);

            }

        }

 

        #endregion

        #region 매개 변수 검증하기 - ValidateParameter(width, height, scale, a, b, deltaTheta, spiralCount)

 

        /// <summary>

        /// 매개 변수 검증하기

        /// </summary>

        /// <param name="width">너비</param>

        /// <param name="height">높이</param>

        /// <param name="scale">스케일</param>

        /// <param name="a">A</param>

        /// <param name="b">B</param>

        /// <param name="deltaTheta">델타 세타</param>

        /// <param name="spiralCount">나선 수</param>

        /// <returns>검증 결과</returns>

        private bool ValidateParameter

        (

            out int   width,

            out int   height,

            out float scale,

            out float a,

            out float b,

            out float deltaTheta,

            out int   spiralCount

        )

        {

            width       = 0;

            height      = 0;

            scale       = 0;

            a           = 0;

            b           = 0;

            deltaTheta  = 0;

            spiralCount = 0;

 

            if(!int.TryParse(this.witdhTextBox.Text, out width) || (width <= 0))

            {

                MessageBox.Show("Please enter a width greater than zero.");

 

                this.witdhTextBox.Focus();

 

                return true;

            }

 

            if(!int.TryParse(this.heightTextBox.Text, out height) || (height <= 0))

            {

                MessageBox.Show("Please enter a height greater than zero.");

 

                this.heightTextBox.Focus();

 

                return true;

            }

 

            if(!float.TryParse(this.scaleTextBox.Text, out scale) || (Math.Abs(scale) <= 0.01))

            {

                MessageBox.Show("Please enter a non-zero scale.");

 

                this.scaleTextBox.Focus();

 

                return true;

            }

 

            if(this.sourceBitmap == null)

            {

                MessageBox.Show("Please select a base image.");

 

                return true;

            }

 

            if(!float.TryParse(this.aTextBox.Text, out a))

            {

                MessageBox.Show("Please enter parameter A.");

 

                return true;

            }

 

            if(!float.TryParse(this.bTextBox.Text, out b))

            {

                MessageBox.Show("Please enter parameter B.");

 

                return true;

            }

 

            if(!float.TryParse(this.deltaThetaTextBox.Text, out deltaTheta))

            {

                MessageBox.Show("Please enter parameter DTheta.");

 

                return true;

            }

 

            deltaTheta *= (float)(Math.PI / 180);

 

            if(!int.TryParse(this.spiralCountTextBox.Text, out spiralCount) || spiralCount <= 0)

            {

                MessageBox.Show("Please enter a number if spirals greater than zero.");

 

                return true;

            }

 

            return false;

        }

 

        #endregion

        #region 극 좌표를 직교 좌표로 변환하기 - ConvertPolarToCartesian(radius, theta, x, y)

 

        /// <summary>

        /// 극 좌표를 직교 좌표로 변환하기

        /// </summary>

        /// <param name="radius">반경</param>

        /// <param name="theta">세타</param>

        /// <param name="x">X</param>

        /// <param name="y">Y</param>

        private void ConvertPolarToCartesian(float radius, float theta, out float x, out float y)

        {

            x = (float)(radius * Math.Cos(theta));

            y = (float)(radius * Math.Sin(theta));

        }

 

        #endregion

        #region 나선 포인트 리스트 구하기 - GetSpiralPointList(centerPoint, a, b, offsetAngle, deltaTheta, maximumRadius)

 

        /// <summary>

        /// 나선 포인트 리스트 구하기

        /// </summary>

        /// <param name="centerPoint">중심점</param>

        /// <param name="a">A</param>

        /// <param name="b">B</param>

        /// <param name="offsetAngle">오프셋 각도</param>

        /// <param name="deltaTheta">델타 세타</param>

        /// <param name="maximumRadius">최대 반경</param>

        /// <returns>나선 포인트 리스트</returns>

        private List<PointF> GetSpiralPointList

        (

            PointF centerPoint,

            float  a,

            float  b,

            float  offsetAngle,

            float  deltaTheta,

            float  maximumRadius

        )

        {

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

 

            float minimumTheta = (float)(Math.Log(0.1 / a) / b);

 

            for(float theta = minimumTheta; ; theta += deltaTheta)

            {

                float radius = (float)(a * Math.Exp(b * theta));

 

                float x, y;

 

                ConvertPolarToCartesian(radius, theta + offsetAngle, out x, out y);

 

                x += centerPoint.X;

                y += centerPoint.Y;

 

                pointList.Add(new PointF((float)x, (float)y));

 

                if(radius > maximumRadius)

                {

                    break;

                }

            }

 

            return pointList;

        }

 

        #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 deltaX = point1.X - point2.X;

            float deltaY = point1.Y - point2.Y;

 

            return (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY);

        }

 

        #endregion

        #region 비트맵 밝기 조정하기 - AdjustBitmapBrightness(sourceImage, brightness)

 

        /// <summary>

        /// 비트맵 밝기 조정하기

        /// </summary>

        /// <param name="sourceImage">소스 이미지</param>

        /// <param name="brightness">밝기</param>

        /// <returns>비트맵</returns>

        private Bitmap AdjustBitmapBrightness(Image sourceImage, float brightness)

        {

            ColorMatrix colorMatrix = new ColorMatrix

            (

                new float[][]

                {

                    new float[] {brightness, 0         , 0         , 0, 0},

                    new float[] {0         , brightness, 0         , 0, 0},

                    new float[] {0         , 0         , brightness, 0, 0},

                    new float[] {0         , 0         , 0         , 1, 0},

                    new float[] {0         , 0         , 0         , 0, 1}

                }

            );

 

            ImageAttributes imageAttributes = new ImageAttributes();

 

            imageAttributes.SetColorMatrix(colorMatrix);

 

            Point[] pointArray =

            {

                new Point(0                , 0                 ),

                new Point(sourceImage.Width, 0                 ),

                new Point(0                , sourceImage.Height)

            };

 

            Rectangle rectangle = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);

 

            Bitmap targetBitmap = new Bitmap(sourceImage.Width, sourceImage.Height);

 

            using(Graphics graphics = Graphics.FromImage(targetBitmap))

            {

                graphics.DrawImage(sourceImage, pointArray, rectangle, GraphicsUnit.Pixel, imageAttributes);

            }

 

            return targetBitmap;

        }

 

        #endregion

        #region 이미지 그리기 - DrawImageAt(graphics, centerPoint, point, radius, imageScale)

 

        /// <summary>

        /// 이미지 그리기

        /// </summary>

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

        /// <param name="centerPoint">중심점</param>

        /// <param name="point">포인트</param>

        /// <param name="radius">반경</param>

        /// <param name="imageScale">이미지 스케일</param>

        private void DrawImageAt(Graphics graphics, PointF centerPoint, PointF point, float radius, float imageScale)

        {

            float deltaX = point.X - centerPoint.X;

            float deltaY = point.Y - centerPoint.Y;

 

            float angle = (float)(Math.Atan2(deltaY, deltaX) * 180 / Math.PI) + 90;

 

            float scaleX = radius / this.sourceBitmap.Width * imageScale;

            float scaleY = radius / this.sourceBitmap.Height * imageScale;

            float scale  = Math.Min(scaleX, scaleY);

 

            GraphicsState state = graphics.Save();

 

            graphics.ResetTransform();

            graphics.ScaleTransform(scale, scale);

            graphics.RotateTransform(angle, MatrixOrder.Append);

            graphics.TranslateTransform(point.X, point.Y, MatrixOrder.Append);

 

            PointF[] targetPointArray =

            {

                new PointF(-this.sourceBitmap.Width / 2, -this.sourceBitmap.Height / 2),

                new PointF( this.sourceBitmap.Width / 2, -this.sourceBitmap.Height / 2),

                new PointF(-this.sourceBitmap.Width / 2,  this.sourceBitmap.Height / 2)

            };

 

            RectangleF sourceRectangle = new RectangleF(0, 0, this.sourceBitmap.Width, this.sourceBitmap.Height);

 

            if(this.darkenCheckBox.Checked)

            {

                float darkenRadius = Math.Min(centerPoint.X, centerPoint.Y) / 3f;

                float darkenScale  = radius / darkenRadius + 0.2f;

 

                if(darkenScale > 1)

                {

                    darkenScale = 1;

                }

 

                Bitmap bitmap = AdjustBitmapBrightness(this.sourceBitmap, darkenScale);

 

                graphics.DrawImage(bitmap, targetPointArray, sourceRectangle, GraphicsUnit.Pixel);

            }

            else

            {

                graphics.DrawImage(sourceBitmap, targetPointArray, sourceRectangle, GraphicsUnit.Pixel);

            }

 

            graphics.Restore(state);

        }

 

        #endregion

        #region 나선 이미지 그리기 - DrawSpiralImage(graphics, bitmap, width, height, scale, a, b, deltaTheta, spiralCount)

 

        /// <summary>

        /// 나선 이미지 그리기

        /// </summary>

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

        /// <param name="bitmap">비트맵</param>

        /// <param name="width">너비</param>

        /// <param name="height">높이</param>

        /// <param name="scale">스케일</param>

        /// <param name="a">A</param>

        /// <param name="b">B</param>

        /// <param name="deltaTheta">델타 세타</param>

        /// <param name="spiralCount">나선 수</param>

        private void DrawSpiralImage

        (

            Graphics graphics,

            Bitmap   bitmap,

            int      width,

            int      height,

            float    scale,

            float    a,

            float    b,

            float    deltaTheta,

            int      spiralCount

        )

        {

            PointF centerPoint = new PointF(width / 2, height / 2);

 

            float maximumRadius = (float)Math.Sqrt(centerPoint.X * centerPoint.X + centerPoint.Y * centerPoint.Y) + this.sourceBitmap.Height;

 

            float startAngle = 0;

            float deltaAngle = (float)(2 * Math.PI / spiralCount);

 

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

            {

                List<PointF> pointList = GetSpiralPointList(centerPoint, a, b, startAngle, deltaTheta, maximumRadius);

 

                foreach(PointF point in pointList)

                {

                    float distance = GetDistance(centerPoint, point);

 

                    float radius = distance / 2;

 

                    DrawImageAt(graphics, centerPoint, point, radius, scale);

                }

 

                startAngle += deltaAngle;

            }

        }

 

        #endregion

        #region 나선 그리기 - DrawSpiral()

 

        /// <summary>

        /// 나선 그리기

        /// </summary>

        private void DrawSpiral()

        {

            int   width;

            int   height;

            int   spiralCount;

            float scale;

            float a;

            float b;

            float deltaTheta;

 

            if(ValidateParameter(out width, out height, out scale, out a, out b, out deltaTheta, out spiralCount))

            {

                return;

            }

 

            this.resultPictureBox.ClientSize = new Size(width, height);

 

            int clientWidth  = Math.Min(this.containerPanel.Left + this.resultPictureBox.Width + this.containerPanel.Left, 800);

            int clientHeight = Math.Min(this.containerPanel.Top  + this.resultPictureBox.Width + this.containerPanel.Left, 800);

 

            if(clientWidth < ClientSize.Width)

            {

                clientWidth = ClientSize.Width;

            }

 

            if(clientHeight < ClientSize.Height)

            {

                clientHeight = ClientSize.Height;

            }

 

            ClientSize = new Size(clientWidth, clientHeight);

 

            Bitmap bitmap = new Bitmap(width, height);

 

            using(Graphics graphics = Graphics.FromImage(bitmap))

            {

                graphics.SmoothingMode = SmoothingMode.AntiAlias;

 

                if(backgroundImage != null)

                {

                    using(Brush brush = new TextureBrush(backgroundImage))

                    {

                        graphics.FillRectangle(brush, 0, 0, width, height);

                    }

                }

                else

                {

                    graphics.Clear(this.backgroundColor);

                }

 

                DrawSpiralImage(graphics, sourceBitmap, width, height, scale, a, b, deltaTheta, spiralCount);

            }

 

            this.resultPictureBox.Image = bitmap;

        }

 

        #endregion

    }

}

 

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

Posted by 사용자 icodebroker

댓글을 달아 주세요