2016年5月6日金曜日

C# GraphicsのTransform

C#のGraphicsは表示位置やスケール、角度をTransformにMatrixで渡せば自由に設定できる。以前は自前でTransformを作っていたけど、Graphicsに標準で入ってるメソッドを使用する。
それとStackにTransformの状態を積むことにより、状態を復元できるようにする。



とりあえず試しに時計でも作ってみた。もっとも普通の見やすい時計って文字は正立だし、矢印だって三角関数とか使えば良いのであんまりTransformのメリットは無いけど。

TranslateTransformで移動、ScaleTransformでスケーリング、RotateTransformで回転、ResetTransformで初期化、という感じ。あとは任意の位置でTransformをStackに詰めばその場所を復元することができる。うまく使わないと頭がごちゃごちゃするので注意すること。



using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace GraphicsStack
{
    public partial class Form1 : Form
    {
        Bitmap bmp;
        Graphics gra;

        DateTime NowDate { get { return (DateTime.Now); } }
        //DateTime NowDate { get { return (new DateTime(2016, 5, 6, 10, 8, 37, 0)); } }

        public Form1()
        {
            InitializeComponent();

            bmp = new Bitmap(512, 512);
            gra = Graphics.FromImage(bmp);

            MaximumSize = MinimumSize = Size = Size + bmp.Size - pictureBox1.Size;
            pictureBox1.Image = bmp;

            timer1.Start();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            gra.ResetTransform();

            DateTime now = NowDate;

            using (StringFormat center = new StringFormat(StringFormat.GenericTypographic))
            using (Font font1 = new Font(DefaultFont.Name, 25))
            using (Font font2 = new Font(DefaultFont.Name, 18))
            using (Pen pen1 = new Pen(Color.Gray, 3))
            using (Pen pen2 = new Pen(Color.Black, 8))
            using (Pen pen3 = new Pen(Color.Red, 5))
            {
                center.Alignment = center.LineAlignment = StringAlignment.Center;
                pen2.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;

                Stack<System.Drawing.Drawing2D.Matrix> Stack = new Stack<System.Drawing.Drawing2D.Matrix>();
                Stack.Push(gra.Transform);

                gra.Clear(Color.LightGray);

                gra.TranslateTransform(256, 256);
                Stack.Push(gra.Transform);

                for (int i = 0; i < 60; i++)
                {
                    gra.RotateTransform(360 / 60);
                    gra.DrawLine(pen1, 0, -230, 0, -240);
                }

                for (int i = 1; i <= 12; i++)
                {
                    gra.RotateTransform(360 / 12);
                    gra.DrawLine(pen1, 0, -210, 0, -240);
                    gra.DrawString(i.ToString(), font1, pen1.Brush, 0, -190, center);
                    gra.DrawLine(pen1, -15, -170, 15, -170);
                }

                gra.Transform = Stack.Pop();

                Stack.Push(gra.Transform);
                gra.TranslateTransform(120, 0);
                gra.DrawRectangle(Pens.Black, -45, -15, 90, 30);
                gra.DrawString(
                    now.DayOfWeek.ToString().Substring(0, 3).ToUpper() +
                    now.Day.ToString(" 00"),
                    font2, pen1.Brush, 0, 0, center);
                gra.Transform = Stack.Pop();

                #regionfloat hour = now.Hour * 30 + now.Minute * 0.5f;
                float minute = now.Minute * 6;
             float second = now.Second * 6;
             float subsec = now.Millisecond / (1000 / 360f);

               Stack.Push(gra.Transform);
             gra.RotateTransform(hour);
             gra.DrawLine(pen2, 0, 50, 0, -150);
                gra.Transform = Stack.Pop();

              Stack.Push(gra.Transform);
             gra.RotateTransform(minute);
               gra.DrawLine(pen2, 0, 100, 0, -210);
               gra.Transform = Stack.Pop();

              Stack.Push(gra.Transform);
             gra.RotateTransform(second);
               gra.DrawLine(pen3, 0, 50, 0, -150);
                gra.FillEllipse(pen3.Brush, -10, -10, 20, 20);
             gra.Transform = Stack.Pop();

              Stack.Push(gra.Transform);
             gra.TranslateTransform(90, 90);
                gra.RotateTransform(subsec);
               gra.DrawLine(pen3, 0, 10, 0, -30);
             gra.FillEllipse(pen3.Brush, -10, -10, 20, 20);
             gra.Transform = Stack.Pop();
               #endregion

                gra.Transform = Stack.Pop();

#if DEBUG
                if (Stack.Count != 0) { throw new Exception("Stackが異常です"); }
#endif
          }

         pictureBox1.Refresh();
     }
  }
}

                float hour = now.Hour * 30 + now.Minute * 0.5f;
                float minute = now.Minute * 6;
                float second = now.Second * 6;
                float subsec = now.Millisecond / (1000 / 360f);

                Stack.Push(gra.Transform);
                gra.RotateTransform(hour);
                gra.DrawLine(pen2, 0, 50, 0, -150);
                gra.Transform = Stack.Pop();

                Stack.Push(gra.Transform);
                gra.RotateTransform(minute);
                gra.DrawLine(pen2, 0, 100, 0, -210);
                gra.Transform = Stack.Pop();

                Stack.Push(gra.Transform);
                gra.RotateTransform(second);
                gra.DrawLine(pen3, 0, 50, 0, -150);
                gra.FillEllipse(pen3.Brush, -10, -10, 20, 20);
                gra.Transform = Stack.Pop();

                Stack.Push(gra.Transform);
                gra.TranslateTransform(90, 90);
                gra.RotateTransform(subsec);
                gra.DrawLine(pen3, 0, 10, 0, -30);
                gra.FillEllipse(pen3.Brush, -10, -10, 20, 20);
                gra.Transform = Stack.Pop();
                #endregion

                gra.Transform = Stack.Pop();

#if DEBUG
                if (Stack.Count != 0) { throw new Exception("Stackが異常です"); }
#endif
            }

            pictureBox1.Refresh();
        }
    }
}

0 件のコメント:

コメントを投稿