C#

2015年9月 2日 (水)

C#:万華鏡丸形 - Kaleidoscope Round

案外簡単にできそうな気がしたので作ってみた。

K11

元画像は
http://freedesignfile.com/32981-winter-snowflake-backgrounds-art-design-vector-05/
から。

K33

元画像は
http://all-free-download.com/free-vector/vector-background/starstudded_christmas_ball_background_vector_157399.html
から。

作り方

  1. サイズ480×480の Picture Box を8枚重ねて置く。
  2. Graphic Path を使ってそれぞれから中心角45度の扇形を残す。
  3. サイズ480×480の画像を1枚用意して読み込む(bmp1)。
  4. それを左右反転したbmp2を作る(RotateFlipType.RotateNoneFlipX)。
  5. 8つの扇形にbmp1とbmp2を交互に読み込む。
  6. 奇数番目の画像が全て同じになるように回転。偶数番目も同様。
  7. Timerを使って動かす。

変幻自在というか、非常に美しく感動的。

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace kaleidoscope2
{
    public partial class Form1 : Form
    {
        string imagePath;
        Bitmap bmp1;
        Bitmap bmp2;
        int dd = 0;
        bool moving = false;

        public Form1()
        {
            InitializeComponent();

        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.SetStyle(ControlStyles.ResizeRedraw, true);
            this.SetStyle(ControlStyles.DoubleBuffer, true);
            this.SetStyle(ControlStyles.UserPaint, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);

            this.ClientSize = new Size(480,520);
            this.splitContainer1.Size = new Size(640, 480);
            this.splitContainer1.SplitterDistance = 480;
            this.splitContainer1.Panel1.Margin = new Padding(0, 0, 0, 0);
            this.splitContainer1.Panel1.BackColor = Color.Black;

            //扇形
            System.Drawing.Drawing2D.GraphicsPath path1 = new System.Drawing.Drawing2D.GraphicsPath();
            path1.AddPie(new Rectangle(0, 0, 480, 480), 0, 45);
            System.Drawing.Drawing2D.GraphicsPath path2 = new System.Drawing.Drawing2D.GraphicsPath();
            path2.AddPie(new Rectangle(0, 0, 480, 480), 45, 45);
            System.Drawing.Drawing2D.GraphicsPath path3 = new System.Drawing.Drawing2D.GraphicsPath();
            path3.AddPie(new Rectangle(0, 0, 480, 480), 90, 45);
            System.Drawing.Drawing2D.GraphicsPath path4 = new System.Drawing.Drawing2D.GraphicsPath();
            path4.AddPie(new Rectangle(0, 0, 480, 480), 135, 45);
            System.Drawing.Drawing2D.GraphicsPath path5 = new System.Drawing.Drawing2D.GraphicsPath();
            path5.AddPie(new Rectangle(0, 0, 480, 480), 180, 45);
            System.Drawing.Drawing2D.GraphicsPath path6 = new System.Drawing.Drawing2D.GraphicsPath();
            path6.AddPie(new Rectangle(0, 0, 480, 480), 225, 45);
            System.Drawing.Drawing2D.GraphicsPath path7 = new System.Drawing.Drawing2D.GraphicsPath();
            path7.AddPie(new Rectangle(0, 0, 480, 480), 270, 45);
            System.Drawing.Drawing2D.GraphicsPath path8 = new System.Drawing.Drawing2D.GraphicsPath();
            path8.AddPie(new Rectangle(0, 0, 480, 480), 315, 45);

            this.pictureBox1.Region = new Region(path1);
            this.pictureBox2.Region = new Region(path2);
            this.pictureBox3.Region = new Region(path3);
            this.pictureBox4.Region = new Region(path4);
            this.pictureBox5.Region = new Region(path5);
            this.pictureBox6.Region = new Region(path6);
            this.pictureBox7.Region = new Region(path7);
            this.pictureBox8.Region = new Region(path8);
            
            //画像読み込み
            imagePath = @"C:\snow1.jpg";
            imageSet();
            timer1.Interval = 100;
        }

        //画像を8つの扇形に配置
        private void imageSet()
        {
            bmp1 = (System.Drawing.Bitmap)System.Drawing.Image.FromFile(imagePath);
            bmp2 = (System.Drawing.Bitmap)System.Drawing.Image.FromFile(imagePath);
            bmp2.RotateFlip(RotateFlipType.RotateNoneFlipX); //左右反転

            dd = 0;

            pictureBox1.Invalidate();
            pictureBox2.Invalidate();
            pictureBox3.Invalidate();
            pictureBox4.Invalidate();
            pictureBox5.Invalidate();
            pictureBox6.Invalidate();
            pictureBox7.Invalidate();
            pictureBox8.Invalidate();

        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            dd += 4; //4度ずつ回転

            pictureBox1.Invalidate();
            pictureBox2.Invalidate();
            pictureBox3.Invalidate();
            pictureBox4.Invalidate();
            pictureBox5.Invalidate();
            pictureBox6.Invalidate();
            pictureBox7.Invalidate();
            pictureBox8.Invalidate();

        }

        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.ResetTransform();
            int deg = 0 + dd;
            double adeg = Math.PI * (45 + deg) / 180;
            g.TranslateTransform((float)(240 - 338.4 * Math.Cos(adeg)), -(float)(338.4 * Math.Sin(adeg) - 240));
            g.RotateTransform(deg);

            g.DrawImage(bmp1, 0, 0);
        }

        private void pictureBox2_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.ResetTransform();
            int deg = -90 - dd;
            double adeg = Math.PI * (45 + deg) / 180;
            g.TranslateTransform((float)(240 - 338.4 * Math.Cos(adeg)), -(float)(338.4 * Math.Sin(adeg) - 240));
            g.RotateTransform(deg);

            g.DrawImage(bmp2, 0, 0);
        }

        private void pictureBox3_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.ResetTransform();
            int deg = 90 + dd;
            double adeg = Math.PI * (45 + deg) / 180;
            g.TranslateTransform((float)(240 - 338.4 * Math.Cos(adeg)), -(float)(338.4 * Math.Sin(adeg) - 240));
            g.RotateTransform(deg);

            g.DrawImage(bmp1, 0, 0);
        }

        private void pictureBox4_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.ResetTransform();
            int deg = 0 - dd;
            double adeg = Math.PI * (45 + deg) / 180;
            g.TranslateTransform((float)(240 - 338.4 * Math.Cos(adeg)), -(float)(338.4 * Math.Sin(adeg) - 240));
            g.RotateTransform(deg);

            g.DrawImage(bmp2, 0, 0);
        }

        private void pictureBox5_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.ResetTransform();
            int deg = 180 + dd;
            double adeg = Math.PI * (45 + deg) / 180;
            g.TranslateTransform((float)(240 - 338.4 * Math.Cos(adeg)), -(float)(338.4 * Math.Sin(adeg) - 240));
            g.RotateTransform(deg);

            g.DrawImage(bmp1, 0, 0);
        }

        private void pictureBox6_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.ResetTransform();
            int deg = 90 - dd;
            double adeg = Math.PI * (45 + deg) / 180;
            g.TranslateTransform((float)(240 - 338.4 * Math.Cos(adeg)), -(float)(338.4 * Math.Sin(adeg) - 240));
            g.RotateTransform(deg);

            g.DrawImage(bmp2, 0, 0);

        }

        private void pictureBox7_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.ResetTransform();
            int deg = 270 + dd;
            double adeg = Math.PI * (45 + deg) / 180;
            g.TranslateTransform((float)(240 - 338.4 * Math.Cos(adeg)), -(float)(338.4 * Math.Sin(adeg) - 240));
            g.RotateTransform(deg);

            g.DrawImage(bmp1, 0, 0);

        }

        private void pictureBox8_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.ResetTransform();
            int deg = 180 - dd;
            double adeg = Math.PI * (45 + deg) / 180;
            g.TranslateTransform((float)(240 - 338.4 * Math.Cos(adeg)), -(float)(338.4 * Math.Sin(adeg) - 240));
            g.RotateTransform(deg);

            g.DrawImage(bmp2, 0, 0);

        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (moving == false)
            {
                timer1.Start();
                button1.Text = "Stop";
            }
            else
            {
                timer1.Stop();
                button1.Text = "start";
            }
            moving = !moving;
        }

        private void radioButton1_CheckedChanged(object sender, EventArgs e)
        {
            imagePath = @"C:\snow1.jpg";
            imageSet();
        }

        private void radioButton2_CheckedChanged(object sender, EventArgs e)
        {
            imagePath = @"C:\snow2.jpg";
            imageSet();
        }

        private void radioButton3_CheckedChanged(object sender, EventArgs e)
        {
            imagePath = @"C:\Christmas2.jpg";
            imageSet();

        }

        private void radioButton4_CheckedChanged(object sender, EventArgs e)
        {
            imagePath = @"C:\Christmas.jpg";
            imageSet();
        }
    }
}

 

 

2015年2月20日 (金)

C#:WebBrowser (IE) のキャッシュを選択的に削除する

C#:超小型 Radiko プレーヤー/その2でブラウザのキャッシュのうち"radiko"を含むものを削除する部分があった。これは音量などの設定を後に残さず、初期化するためであった。

それが、うまくいかなくなった。Windowsか.NETのアップデートのためだろう。ネットでもあちこちで質問されているが、さすがに解決していないようだ。

DeleteCache()内のwhileループがいつまでもfalseにならないことが原因らしい。そこで応急処置として、

1 エラーが出るところをtry-catchでごまかす
2 意味がなさげなDeleteUrlCacheGroupをやめる。
3 一定回数ループしたら強制的に切り上げる

こととした。

ループ回数は、数百程度だとスキャンが不十分で全部削除しきれない。ちょっとIEを使っただけでも数千個のキャッシュを飲まされるので、最低でも8000くらいにしておいたほうがいいだろう。

        [STAThread]
        public void DeleteCache()
        {
            string cURL = "";
            const int CACHEGROUP_SEARCH_ALL = 0x0;
            const int ERROR_NO_MORE_ITEMS = 259;
            long groupId = 0;

            int cacheEntryInfoBufferSizeInitial = 0;
            int cacheEntryInfoBufferSize = 0;
            IntPtr cacheEntryInfoBuffer = IntPtr.Zero;
            INTERNET_CACHE_ENTRY_INFOA internetCacheEntry;
            IntPtr enumHandle = IntPtr.Zero;
            bool returnValue = false;

            enumHandle = FindFirstUrlCacheGroup(0, CACHEGROUP_SEARCH_ALL, IntPtr.Zero, 0, ref groupId, IntPtr.Zero);
            if (enumHandle != IntPtr.Zero && ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error())
                return;

            //ここにあったDeleteUrlCacheGroupのループをやめる

            enumHandle = FindFirstUrlCacheEntry(null, IntPtr.Zero, ref cacheEntryInfoBufferSizeInitial);
            if (enumHandle != IntPtr.Zero && ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error())
                return;

            cacheEntryInfoBufferSize = cacheEntryInfoBufferSizeInitial;
            cacheEntryInfoBuffer = Marshal.AllocHGlobal(cacheEntryInfoBufferSize);
            enumHandle = FindFirstUrlCacheEntry(null, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSizeInitial);

            int roop = 0; //ループ回数を設定する
            while (roop < 8000)
            {
                ++roop;
                internetCacheEntry = (INTERNET_CACHE_ENTRY_INFOA)Marshal.PtrToStructure(cacheEntryInfoBuffer, typeof(INTERNET_CACHE_ENTRY_INFOA));
                try
                {
                    cURL += Marshal.PtrToStringAnsi(internetCacheEntry.lpszSourceUrlName);
                }
                catch
                {
                    cURL += "";
                }
                cacheEntryInfoBufferSizeInitial = cacheEntryInfoBufferSize;
                if (cURL.Contains("radiko")) //URLにradikoが含まれるもののみ削除
                {
                    try
                    {
                        returnValue = DeleteUrlCacheEntry(internetCacheEntry.lpszSourceUrlName);
                    }
                    catch {
                        returnValue = true;
                    }

                }
                else
                {
                    returnValue = false;
                }
                if (!returnValue)
                {
                    returnValue = FindNextUrlCacheEntry(enumHandle, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSizeInitial);
                }
                if (!returnValue && ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error())
                {
                    break;
                }
                if (!returnValue && cacheEntryInfoBufferSizeInitial > cacheEntryInfoBufferSize)
                {
                    cacheEntryInfoBufferSize = cacheEntryInfoBufferSizeInitial;
                    cacheEntryInfoBuffer = Marshal.ReAllocHGlobal(cacheEntryInfoBuffer, (IntPtr)cacheEntryInfoBufferSize);
                    returnValue = FindNextUrlCacheEntry(enumHandle, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSizeInitial);
                }
            }
            Marshal.FreeHGlobal(cacheEntryInfoBuffer);
        }

もとのコードでは、キャッシュの削除をプログラム起動時に行っていたが、フォームを閉じるときに行うよう変更してインターネットオプションから確認したところ、以上のコードでradikoを含むキャッシュだけを見事に削除できた。

確認できたらキャッシュの削除を起動時に行うように戻したほうがいいだろう。なぜならIEなどで別途Radikoを開くことがあると、その設定がキャッシュに保存されてしまうからだ。初期化はプログラム起動時に行ったほうがいい。

あと、button1_Clickでラジオを停止させるとき、ブラウザのRefreshは要らなくなったようだ。

        private void button1_Click(object sender, EventArgs e)
        {
            this.webBrowser1.Navigate(@"http://radiko.jp/");
//            this.webBrowser1.Refresh(); ← カット
            this.label1.Text = dtitle;
        }

Visual Studio Community 2013に移行してみたが、とりあえずこれで動くようになった。

選局にやや時間がかかるものの、小さくて使いやすく、非常に満足している。

 

 

 

2013年2月 2日 (土)

C#:超小型 Radiko プレーヤー/その2

エリア判定を自動的に行い、放送局一覧を取得できるようにした。

  1. HttpWebRequestを用いて、エリアコードを取得する。
  2. それに基づき、放送局一覧のxmlを取得する。
  3. xmlにより、選局ボタンを作成・配置する。
  4. これで一応全国で使えるはず。
  5. 停止ボタンが効かないことがあったので改良した。
  6. Radikoサイトの音量はデフォルトで最大に設定されているが、IEでRadikoにアクセスしたことがあると、そのときの音量調整がインターネットキャッシュに保存されていて、それが最大音量になってしまうため、マスターボリュームで音量が上がらなくなる。そこで、
    http://support.microsoft.com/kb/326201/ja
    を参考にして、Radikoのインターネットキャッシュのみ起動時に削除する。
  7. ボタンに放送局名を表示するときに文字数が多すぎて収まらないことがあるので、
    http://wiki.dobon.net/index.php?free%2FkanaxsCSharp
    を用い、全角カタカナを半角カタカナに変換する。
  8. さらに文字が収まらないときは、「ラジオ」の文字を削除する。
  9. これで超小型化を実現した。
  10. 起動時のウインドウタイトルに、エリア名を表示した。

Tokyo

エリアコードをいじることで他の道府県の放送局を取得できるか試した。

大阪。

Osaka

北海道。

Hokkaido2

再生時のエリア判定自体はクリアできないので、東京からローカル局を聴くことはできないが、放送大学やNIKKEI第一は広範囲で聴ける模様。

以下がForm1.cs。Designer.csは前回と同じ。それにしても、遊んでいた割にはコードがずいぶん長くなってしまった。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using CoreAudioApi;
using System.Text.RegularExpressions;
using Microsoft.VisualBasic;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private MMDevice device;
        ToolTip tt = new ToolTip();
        Point p0 = new Point();
        int w;
        string mes = "Ice Potato Radiko Player";
        string dtitle;

        public Form1()
        {
            InitializeComponent();
            //音量調節
            MMDeviceEnumerator DevEnum = new MMDeviceEnumerator();
            device = DevEnum.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);
            this.Master.Value = (int)(device.AudioEndpointVolume.MasterVolumeLevelScalar * 100);
            device.AudioEndpointVolume.OnVolumeNotification += new AudioEndpointVolumeNotificationDelegate(AudioEndpointVolume_OnVolumeNotification);
            //モニターの右上に表示
            w = System.Windows.Forms.Screen.GetBounds(this).Width;
            //ツールチップ
            tt.SetToolTip(this.flowLayoutPanel1, "ダブルクリックで閉じます");
            tt.SetToolTip(this.label1, "ダブルクリックで閉じます");
            tt.SetToolTip(this.button2, "閉じる");
            this.MouseEnter += new EventHandler(Form1_MouseEnter);
            //radikoキャッシュ削除
            this.DeleteCache();
            //選局ボタン
            this.buttonSet();
        }

        void Form1_MouseEnter(object sender, EventArgs e)
        {
            this.Master.Focus();
        }

        private void buttonSet()
        {
            System.Net.HttpWebRequest webreq = (System.Net.HttpWebRequest)System.Net.WebRequest.Create("http://radiko.jp/area");
            System.Net.HttpWebResponse webres = (System.Net.HttpWebResponse)webreq.GetResponse();
            System.Text.Encoding enc = System.Text.Encoding.GetEncoding("utf-8");
            System.IO.Stream st = webres.GetResponseStream();
            System.IO.StreamReader sr = new System.IO.StreamReader(st, enc);
            string res = sr.ReadToEnd();
            sr.Close();
            //エリアコードを取得
            System.Text.RegularExpressions.MatchCollection mc = System.Text.RegularExpressions.Regex.Matches(res, @"JP\d+");
            //xmlを読む
            System.Net.HttpWebRequest xmlsrc = (System.Net.HttpWebRequest)System.Net.WebRequest.Create("http://radiko.jp/v2/station/list/" + mc[0].Value + ".xml");
            //他県xml読込確認用
            //System.Net.HttpWebRequest xmlsrc = (System.Net.HttpWebRequest)System.Net.WebRequest.Create("http://radiko.jp/v2/station/list/" + "JP1" + ".xml");
            System.Net.HttpWebResponse xmlres = (System.Net.HttpWebResponse)xmlsrc.GetResponse();
            System.Text.Encoding enc2 = System.Text.Encoding.GetEncoding("utf-8");
            System.IO.Stream st2 = xmlres.GetResponseStream();
            System.IO.StreamReader sr2 = new System.IO.StreamReader(st2, enc2);
            string xml = sr2.ReadToEnd();
            sr2.Close();
            //放送局ID
            MatchCollection stids = System.Text.RegularExpressions.Regex.Matches(xml, @"<id>.+?</id>");
            //放送局名
            MatchCollection stnames = System.Text.RegularExpressions.Regex.Matches(xml, @"<name>.+?</name>");
            //エリア名
            MatchCollection areas = System.Text.RegularExpressions.Regex.Matches(xml, "area_name=\".+? JAPAN\"");
            string area = areas[0].Value.Replace("area_name=\"", "");
            area = area.Replace(" JAPAN\"", "");
            dtitle = mes + " (" + area + ")";

            string names = "";
            for (int i = 0; i < stnames.Count; ++i)
            {
                names += stnames[i].Value + ", ";
            }

            string[] ID = new string[stids.Count];
            for(int i = 0; i < stids.Count; ++i)
            {
                string id = stids[i].Value.Replace("<id>", "");
                id = id.Replace("</id>", "");
                ID[i] = id;
            }

            string[] Name = new string[stnames.Count];
            for (int i = 0; i < stnames.Count; ++i)
            {
                string name = stnames[i].Value.Replace("<name>", "");
                name = name.Replace("</name>", "");
                name = name.Replace(" ", "");
                name = name.Replace("&apos;", "'");
                name = name.Replace("&amp;", "&");
                System.Text.RegularExpressions.Regex r = new System.Text.RegularExpressions.Regex("(.+?)");
                name = r.Replace(name, "");
                name = ToHankakuKana(name);
                if (name.Length > 9)
                {
                    name = name.Replace("ラジオ", "");
                }

                Name[i] = name;
            }

            Button[] bt = new Button[ID.Length];
            tt.InitialDelay = 500;
            tt.ReshowDelay = 500;
            tt.AutoPopDelay = 10000;
            tt.ShowAlways = true;

            int tuneArea = 0; //選局領域の高さ
            int btwidth = 60; //ボタンの幅
            int btheight = 16;//ボタンの高さ
            int btcol = 4; //一行のボタンの数
            for (int i = 0; i < ID.Length; ++i)
            {
                bt[i] = new Button();
                bt[i].Text = Name[i];
                bt[i].Name = ID[i];
                tt.SetToolTip(bt[i], bt[i].Text);
                bt[i].Font = new System.Drawing.Font("", 6);
                bt[i].Size = new Size(btwidth, btheight);
                bt[i].Margin = new Padding(0);
                bt[i].Padding = new Padding(0);
                bt[i].BackColor = SystemColors.Control;
                this.flowLayoutPanel1.Controls.Add(bt[i]);
                bt[i].Click += new EventHandler(BT_CLICK);
                if (i % btcol == 0)
                {
                    tuneArea += btheight;
                }
            }
            //ウインドウのサイズ等を設定
            this.flowLayoutPanel1.Size = new Size(btwidth*btcol, tuneArea);
            this.webBrowser1.Size = this.flowLayoutPanel1.Size;
            this.webBrowser1.Visible = false;
            this.button1.Location = new Point(5, label1.Height + tuneArea + 2);
            this.ClientSize = new Size(this.flowLayoutPanel1.Width, label1.Height + tuneArea + 25);
            this.Location = new Point(w - this.Width - 10, 5);
            this.button2.Location = new Point(this.Width - 12, 0);
            this.Master.Location = new Point(25, label1.Height + tuneArea);
            this.Master.Width = this.ClientSize.Width - 25;
            this.label1.Text = dtitle;
        }

        void BT_CLICK(object sender, EventArgs e)
        {
            this.mes = ((Button)sender).Text;
            this.label1.Text = "選局中";
            this.timer1.Start();
            this.webBrowser1.Navigate(@"http://radiko.jp/#" + ((Button)sender).Name);
            this.webBrowser1.Refresh();
            this.Master.Focus();
        }

        void AudioEndpointVolume_OnVolumeNotification(AudioVolumeNotificationData data)
        {
            if (this.InvokeRequired)
            {
                object[] Params = new object[1];
                Params[0] = data;
                this.Invoke(new AudioEndpointVolumeNotificationDelegate(AudioEndpointVolume_OnVolumeNotification), Params);
            }
            else
            {
                Master.Value = (int)(data.MasterVolume * 100);
            }
        }

        private void Master_Scroll(object sender, EventArgs e)
        {
            device.AudioEndpointVolume.MasterVolumeLevelScalar = ((float)Master.Value / 100.0f);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.webBrowser1.Navigate(@"http://radiko.jp/");
            this.webBrowser1.Refresh();
            this.label1.Text = dtitle;
        }

        private void flowLayoutPanel1_DoubleClick(object sender, EventArgs e)
        {
            this.Close();
        }

        private void label1_MouseDown(object sender, MouseEventArgs e)
        {
            p0 = new Point(e.X, e.Y);
        }

        private void label1_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                this.Left += e.X - p0.X;
                this.Top += e.Y - p0.Y;
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            this.label1.Text = mes;
            timer1.Stop();
        }

        public static string ToHankakuKana(string str)
        {
            if (str == null || str.Length == 0)
            {
                return str;
            }

            char[] cs = new char[str.Length * 2];
            int len = 0;

            int f = str.Length;

            for (int i = 0; i < f; i++)
            {
                char c = str[i];
                // 、(0x3001) ~ ー(0x30FC)
                if ('、' <= c && c <= 'ー')
                {
                    char m = ConvertToHankakuKanaChar(c);
                    if (m != '\0')
                    {
                        cs[len++] = m;
                    }
                    // カ(0x30AB) ~ ド(0x30C9)
                    else if ('カ' <= c && c <= 'ド')
                    {
                        cs[len++] = ConvertToHankakuKanaChar((char)(c - 1));
                        cs[len++] = '゙';
                    }
                    // ハ(0x30CF) ~ ポ(0x30DD)
                    else if ('ハ' <= c && c <= 'ポ')
                    {
                        int mod3 = c % 3;
                        cs[len++] = ConvertToHankakuKanaChar((char)(c - mod3));
                        cs[len++] = (mod3 == 1 ? '゙' : '゚');
                    }
                    // ヴ(0x30F4)
                    else if (c == 'ヴ')
                    {
                        cs[len++] = 'ウ';
                        cs[len++] = '゙';
                    }
                    // ヷ(0x30F7)
                    else if (c == 'ヷ')
                    {
                        cs[len++] = 'ワ';
                        cs[len++] = '゙';
                    }
                    // ヺ(0x30FA)
                    else if (c == 'ヺ')
                    {
                        cs[len++] = 'ヲ';
                        cs[len++] = '゙';
                    }
                    else
                    {
                        cs[len++] = c;
                    }
                }
                else
                {
                    cs[len++] = c;
                }
            }

            return new string(cs, 0, len);
        }

        private static char ConvertToHankakuKanaChar(char zenkakuChar)
        {
            switch (zenkakuChar)
            {
                case 'ァ':
                    return 'ァ';
                case 'ィ':
                    return 'ィ';
                case 'ゥ':
                    return 'ゥ';
                case 'ェ':
                    return 'ェ';
                case 'ォ':
                    return 'ォ';
                case 'ー':
                    return 'ー';
                case 'ア':
                    return 'ア';
                case 'イ':
                    return 'イ';
                case 'ウ':
                    return 'ウ';
                case 'エ':
                    return 'エ';
                case 'オ':
                    return 'オ';
                case 'カ':
                    return 'カ';
                case 'キ':
                    return 'キ';
                case 'ク':
                    return 'ク';
                case 'ケ':
                    return 'ケ';
                case 'コ':
                    return 'コ';
                case 'サ':
                    return 'サ';
                case 'シ':
                    return 'シ';
                case 'ス':
                    return 'ス';
                case 'セ':
                    return 'セ';
                case 'ソ':
                    return 'ソ';
                case 'タ':
                    return 'タ';
                case 'チ':
                    return 'チ';
                case 'ツ':
                    return 'ツ';
                case 'テ':
                    return 'テ';
                case 'ト':
                    return 'ト';
                case 'ナ':
                    return 'ナ';
                case 'ニ':
                    return 'ニ';
                case 'ヌ':
                    return 'ヌ';
                case 'ネ':
                    return 'ネ';
                case 'ノ':
                    return 'ノ';
                case 'ハ':
                    return 'ハ';
                case 'ヒ':
                    return 'ヒ';
                case 'フ':
                    return 'フ';
                case 'ヘ':
                    return 'ヘ';
                case 'ホ':
                    return 'ホ';
                case 'マ':
                    return 'マ';
                case 'ミ':
                    return 'ミ';
                case 'ム':
                    return 'ム';
                case 'メ':
                    return 'メ';
                case 'モ':
                    return 'モ';
                case 'ヤ':
                    return 'ヤ';
                case 'ユ':
                    return 'ユ';
                case 'ヨ':
                    return 'ヨ';
                case 'ラ':
                    return 'ラ';
                case 'リ':
                    return 'リ';
                case 'ル':
                    return 'ル';
                case 'レ':
                    return 'レ';
                case 'ロ':
                    return 'ロ';
                case 'ワ':
                    return 'ワ';
                case 'ヲ':
                    return 'ヲ';
                case 'ン':
                    return 'ン';
                case 'ッ':
                    return 'ッ';

                //ャュョ を追加
                case 'ャ':
                    return 'ャ';
                case 'ュ':
                    return 'ュ';
                case 'ョ':
                    return 'ョ';

                // 、。「」・ を追加
                case '、':
                    return '、';
                case '。':
                    return '。';
                case '「':
                    return '「';
                case '」':
                    return '」';
                case '・':
                    return '・';

                //゛゜ を追加
                case '゛':
                    return '゙';
                case '゜':
                    return '゚';

                //U+3099とU+309Aの濁点と半濁点を追加
                case '\u3099':
                    return '゙';
                case '\u309A':
                    return '゚';

                default:
                    return '\0';
            }
        }

        [StructLayout(LayoutKind.Explicit, Size = 80)]
        public struct INTERNET_CACHE_ENTRY_INFOA
        {
            [FieldOffset(0)]
            public uint dwStructSize;
            [FieldOffset(4)]
            public IntPtr lpszSourceUrlName;
            [FieldOffset(8)]
            public IntPtr lpszLocalFileName;
            [FieldOffset(12)]
            public uint CacheEntryType;
            [FieldOffset(16)]
            public uint dwUseCount;
            [FieldOffset(20)]
            public uint dwHitRate;
            [FieldOffset(24)]
            public uint dwSizeLow;
            [FieldOffset(28)]
            public uint dwSizeHigh;
            [FieldOffset(32)]
            public System.Runtime.InteropServices.ComTypes.FILETIME LastModifiedTime;
            [FieldOffset(40)]
            public System.Runtime.InteropServices.ComTypes.FILETIME ExpireTime;
            [FieldOffset(48)]
            public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;
            [FieldOffset(56)]
            public System.Runtime.InteropServices.ComTypes.FILETIME LastSyncTime;
            [FieldOffset(64)]
            public IntPtr lpHeaderInfo;
            [FieldOffset(68)]
            public uint dwHeaderInfoSize;
            [FieldOffset(72)]
            public IntPtr lpszFileExtension;
            [FieldOffset(76)]
            public uint dwReserved;
            [FieldOffset(76)]
            public uint dwExemptDelta;
        }

        [DllImport(@"wininet",
            SetLastError = true,
            CharSet = CharSet.Auto,
            EntryPoint = "FindFirstUrlCacheGroup",
            CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr FindFirstUrlCacheGroup(
            int dwFlags,
            int dwFilter,
            IntPtr lpSearchCondition,
            int dwSearchCondition,
            ref long lpGroupId,
            IntPtr lpReserved);

        [DllImport(@"wininet",
            SetLastError = true,
            CharSet = CharSet.Auto,
            EntryPoint = "FindNextUrlCacheGroup",
            CallingConvention = CallingConvention.StdCall)]
        public static extern bool FindNextUrlCacheGroup(
            IntPtr hFind,
            ref long lpGroupId,
            IntPtr lpReserved);

        [DllImport(@"wininet",
            SetLastError = true,
            CharSet = CharSet.Auto,
            EntryPoint = "DeleteUrlCacheGroup",
            CallingConvention = CallingConvention.StdCall)]
        public static extern bool DeleteUrlCacheGroup(
            long GroupId,
            int dwFlags,
            IntPtr lpReserved);

        [DllImport(@"wininet",
            SetLastError = true,
            CharSet = CharSet.Auto,
            EntryPoint = "FindFirstUrlCacheEntryA",
            CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr FindFirstUrlCacheEntry(
            [MarshalAs(UnmanagedType.LPTStr)] string lpszUrlSearchPattern,
            IntPtr lpFirstCacheEntryInfo,
            ref int lpdwFirstCacheEntryInfoBufferSize);

        [DllImport(@"wininet",
            SetLastError = true,
            CharSet = CharSet.Auto,
            EntryPoint = "FindNextUrlCacheEntryA",
            CallingConvention = CallingConvention.StdCall)]
        public static extern bool FindNextUrlCacheEntry(
            IntPtr hFind,
            IntPtr lpNextCacheEntryInfo,
            ref int lpdwNextCacheEntryInfoBufferSize);

        [DllImport(@"wininet",
            SetLastError = true,
            CharSet = CharSet.Auto,
            EntryPoint = "DeleteUrlCacheEntryA",
            CallingConvention = CallingConvention.StdCall)]
        public static extern bool DeleteUrlCacheEntry(
            IntPtr lpszUrlName);

        [STAThread]
        public void DeleteCache()
        {
            string cURL = "";
            const int CACHEGROUP_SEARCH_ALL = 0x0;
            const int CACHEGROUP_FLAG_FLUSHURL_ONDELETE = 0x2;
            const int ERROR_FILE_NOT_FOUND = 0x2;
            const int ERROR_NO_MORE_ITEMS = 259;
            long groupId = 0;

            int cacheEntryInfoBufferSizeInitial = 0;
            int cacheEntryInfoBufferSize = 0;
            IntPtr cacheEntryInfoBuffer = IntPtr.Zero;
            INTERNET_CACHE_ENTRY_INFOA internetCacheEntry;
            IntPtr enumHandle = IntPtr.Zero;
            bool returnValue = false;

            enumHandle = FindFirstUrlCacheGroup(0, CACHEGROUP_SEARCH_ALL, IntPtr.Zero, 0, ref groupId, IntPtr.Zero);
            if (enumHandle != IntPtr.Zero && ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error())
                return;

            while (true)
            {
                returnValue = DeleteUrlCacheGroup(groupId, CACHEGROUP_FLAG_FLUSHURL_ONDELETE, IntPtr.Zero);
                if (!returnValue && ERROR_FILE_NOT_FOUND == Marshal.GetLastWin32Error())
                {
                    returnValue = FindNextUrlCacheGroup(enumHandle, ref groupId, IntPtr.Zero);
                }

                if (!returnValue && (ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error() || ERROR_FILE_NOT_FOUND == Marshal.GetLastWin32Error()))
                    break;
            }

            enumHandle = FindFirstUrlCacheEntry(null, IntPtr.Zero, ref cacheEntryInfoBufferSizeInitial);
            if (enumHandle != IntPtr.Zero && ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error())
                return;

            cacheEntryInfoBufferSize = cacheEntryInfoBufferSizeInitial;
            cacheEntryInfoBuffer = Marshal.AllocHGlobal(cacheEntryInfoBufferSize);
            enumHandle = FindFirstUrlCacheEntry(null, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSizeInitial);

            while (true)
            {
                internetCacheEntry = (INTERNET_CACHE_ENTRY_INFOA)Marshal.PtrToStructure(cacheEntryInfoBuffer, typeof(INTERNET_CACHE_ENTRY_INFOA));
                cURL += Marshal.PtrToStringAnsi(internetCacheEntry.lpszSourceUrlName);
                cacheEntryInfoBufferSizeInitial = cacheEntryInfoBufferSize;
                if (cURL.Contains("radiko")) //URLにradikoが含まれるもののみ削除
                {
                    returnValue = DeleteUrlCacheEntry(internetCacheEntry.lpszSourceUrlName);
                }
                else
                {
                    returnValue = false;
                }
                if (!returnValue)
                {
                    returnValue = FindNextUrlCacheEntry(enumHandle, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSizeInitial);
                }
                if (!returnValue && ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error())
                {
                    break;
                }
                if (!returnValue && cacheEntryInfoBufferSizeInitial > cacheEntryInfoBufferSize)
                {
                    cacheEntryInfoBufferSize = cacheEntryInfoBufferSizeInitial;
                    cacheEntryInfoBuffer = Marshal.ReAllocHGlobal(cacheEntryInfoBuffer, (IntPtr)cacheEntryInfoBufferSize);
                    returnValue = FindNextUrlCacheEntry(enumHandle, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSizeInitial);
                }
            }
            Marshal.FreeHGlobal(cacheEntryInfoBuffer);
        }
    }
}

 

 

2013年1月24日 (木)

C#:ウインドウ分割のできるPDFビューア/その2

  1. 上下分割に加え、左右分割もできるようにした。
  2. PDF以外のファイルをドラッグ&ドロップしたときにエラーが出る問題を修正。

Ipv

演習書の問題を左に、巻末解答を右に表示したりできて便利。Program.csは前回の記事と同じ。

Form1.cs

using System;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
using IWshRuntimeLibrary;
using Microsoft.Win32;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        string temp = Path.GetTempPath() + @"start.pdf";
        bool spdh = false;
        bool spdv = false;
        private SaveWindowPosition swp = null;
        tpanel tpanel1 = new tpanel();

        public Form1(string[] init) //アイコンへのD&Dを受け付ける
        {
            InitializeComponent();
            this.Text = "Ice Potato PDF Viewer";
            this.Size = new Size(400, 600);
            this.splitContainer1.SplitterDistance = this.ClientSize.Height;
            this.Load += new EventHandler(Form1_Load);
            this.FormClosing += new FormClosingEventHandler(Form1_FormClosing);
            this.splitContainer1.SplitterWidth = 5;
            this.axAcroPDF1.setShowToolbar(false);
            this.axAcroPDF2.setShowToolbar(false);

            //ショートカットの設定
            string shortcutPath = System.IO.Path.Combine(Environment.GetFolderPath(System.Environment.SpecialFolder.DesktopDirectory), @"PDF Viewer.lnk");
            string targetPath = Application.ExecutablePath;
            IWshShell_Class shell = new IWshRuntimeLibrary.IWshShell_Class();
            IWshRuntimeLibrary.IWshShortcut shortcut =
                (IWshRuntimeLibrary.IWshShortcut)shell.CreateShortcut(shortcutPath);
            shortcut.TargetPath = targetPath;
            shortcut.Description = "Ice Potato PDF Viewer";
            shortcut.IconLocation = Application.ExecutablePath + ",0";
            shortcut.Save();
            System.Runtime.InteropServices.Marshal.ReleaseComObject(shortcut);

            //リソースのスタートページを一時フォルダに保存
            PdfReader r = new PdfReader(Properties.Resources.start);
            int pageNum = r.NumberOfPages;
            Document document = new Document();
            PdfWriter writer =
            PdfWriter.GetInstance(document, new FileStream(temp, FileMode.Create));
            writer.Open();
            document.Open();
            PdfContentByte cb = writer.DirectContent;
            int i = 0;
            while (i < pageNum)
            {
                document.NewPage();
                PdfImportedPage page1 = writer.GetImportedPage(r, ++i);
                cb.AddTemplate(page1, 1f, 0, 0, 1f, 0, 0);
            }
            document.Close();
            r.Close();
            writer.Close();
            if (init.Length > 0 && (init[0].EndsWith(".pdf") || init[0].EndsWith(".PDF")))
            {
                this.axAcroPDF1.src = init[0];
                this.axAcroPDF2.src = init[0];
                nameset();
            }
            else
            {
                this.axAcroPDF1.src = temp;
                this.axAcroPDF2.src = temp;
                this.Text = "Welcome - Ice Potato PDF Viewer";
            }
        }

        void Form1_Load(object sender, EventArgs e)
        {
            //ウインドウ復元
            swp = new SaveWindowPosition(this);
            swp.Load();
        }

        void Form1_DragEnter(object sender, DragEventArgs e)
        {            
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                e.Effect = DragDropEffects.Copy;
                //複数ファイルがドラッグされたときのためにまとめて取得
                string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
                //最初のファイルのみビューアに表示
                if (files[0].EndsWith(".pdf") || files[0].EndsWith(".PDF"))
                {
                    this.axAcroPDF1.src = files[0];
                    this.axAcroPDF2.src = files[0];
                    nameset();
                }
                else
                {
                    return;
                }
            }
        }

        private void nameset()
        {
            string fname = this.axAcroPDF1.src;
            string[] fn = fname.Split('\\');
            this.Text = fn[fn.Length - 1] + " - Ice Potato PDF Viewer";
        }

        void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            //ウインドウを閉じるときにスタートページ削除
            System.IO.File.Delete(temp);
            swp.Save();
        }

        private void splitButton_Click(object sender, EventArgs e)
        {
            this.splitContainer1.Orientation = Orientation.Horizontal;
            //splitボタンを押すごとに交互に表示を切り替える
            if (spdh == false)
            {
                this.splitContainer1.SplitterDistance = (int)(this.ClientSize.Height / 2);
            }
            else
            {
                this.splitContainer1.SplitterDistance = this.ClientSize.Height;
            }
            spdv = false;
            spdh = !spdh;
        }

        private void splitButton_Click2(object sender, EventArgs e)
        {
            this.splitContainer1.Orientation = Orientation.Vertical;
            //splitボタンを押すごとに交互に表示を切り替える
            if (spdv == false)
            {
                this.splitContainer1.SplitterDistance = (int)(this.ClientSize.Width / 2);
            }
            else
            {
                this.splitContainer1.SplitterDistance = this.ClientSize.Width;
            }
            spdh = false;
            spdv = !spdv;
        }

        private void openButton_Click(object sender, EventArgs e)
        {
            //ファイルを開くダイアログ
            OpenFileDialog fd = new OpenFileDialog();
            fd.Filter = "PDF(*.pdf)|*.pdf";
            if (fd.ShowDialog() == DialogResult.OK)
            {
                this.axAcroPDF1.src = fd.FileName;
                this.axAcroPDF2.src = fd.FileName;
                nameset();
            }
        }

        //ウインドウ位置の保存
        public class SaveWindowPosition
        {
            public SaveWindowPosition(Form form)
            {
                this.form = form;
                userReg = Application.UserAppDataRegistry;
            }

            private Form form = null;
            private RegistryKey userReg = null;

            public void Save()
            {
                userReg.SetValue("WindowState", (int)(form.WindowState));
                if (form.WindowState == FormWindowState.Normal)
                {
                    userReg.SetValue("X", form.Location.X);
                    userReg.SetValue("Y", form.Location.Y);
                    userReg.SetValue("Height", form.Height);
                    userReg.SetValue("Width", form.Width);
                }
            }

            public void Load()
            {
                try
                {
                    int x = (int)(userReg.GetValue("X"));
                    int y = (int)(userReg.GetValue("Y"));
                    form.Location = new Point(x, y);

                    int height = (int)userReg.GetValue("Height");
                    int width = (int)userReg.GetValue("Width");
                    form.Size = new Size(width, height);

                    form.WindowState = (FormWindowState)userReg.GetValue("WindowState2");
                }
                catch
                {
                    return;
                }
            }
        }

    }
}

Designer.cs

namespace WindowsFormsApplication1
{
    partial class Form1
    {
        /// <summary>
        /// 必要なデザイナー変数です。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 使用中のリソースをすべてクリーンアップします。
        /// </summary>
        /// <param name="disposing">マネージ リソースが破棄される場合 true、破棄されない場合は false です。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows フォーム デザイナーで生成されたコード

        /// <summary>
        /// デザイナー サポートに必要なメソッドです。このメソッドの内容を
        /// コード エディターで変更しないでください。
        /// </summary>
        private void InitializeComponent()
        {
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
            this.splitContainer1 = new System.Windows.Forms.SplitContainer();
            this.axAcroPDF1 = new AxAcroPDFLib.AxAcroPDF();
            this.axAcroPDF2 = new AxAcroPDFLib.AxAcroPDF();
            this.toolStrip1 = new System.Windows.Forms.ToolStrip();
            this.openButton = new System.Windows.Forms.ToolStripButton();
            this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
            this.splitButtonH = new System.Windows.Forms.ToolStripButton();
            this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
            this.splitButtonV = new System.Windows.Forms.ToolStripButton();
            this.toolStripContainer1 = new System.Windows.Forms.ToolStripContainer();
            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
            this.splitContainer1.Panel1.SuspendLayout();
            this.splitContainer1.Panel2.SuspendLayout();
            this.splitContainer1.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.axAcroPDF1)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.axAcroPDF2)).BeginInit();
            this.toolStrip1.SuspendLayout();
            this.toolStripContainer1.ContentPanel.SuspendLayout();
            this.toolStripContainer1.TopToolStripPanel.SuspendLayout();
            this.toolStripContainer1.SuspendLayout();
            this.SuspendLayout();
            //
            // splitContainer1
            //
            this.splitContainer1.AllowDrop = true;
            this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.splitContainer1.Location = new System.Drawing.Point(0, 0);
            this.splitContainer1.Name = "splitContainer1";
            this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal;
            //
            // splitContainer1.Panel1
            //
            this.splitContainer1.Panel1.Controls.Add(this.axAcroPDF1);
            //
            // splitContainer1.Panel2
            //
            this.splitContainer1.Panel2.Controls.Add(this.axAcroPDF2);
            this.splitContainer1.Panel2MinSize = 0;
            this.splitContainer1.Size = new System.Drawing.Size(282, 238);
            this.splitContainer1.SplitterDistance = 83;
            this.splitContainer1.TabIndex = 0;
            this.splitContainer1.DragOver += new System.Windows.Forms.DragEventHandler(this.Form1_DragEnter);
            //
            // axAcroPDF1
            //
            this.axAcroPDF1.AllowDrop = true;
            this.axAcroPDF1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.axAcroPDF1.Enabled = true;
            this.axAcroPDF1.Location = new System.Drawing.Point(0, 0);
            this.axAcroPDF1.Name = "axAcroPDF1";
            this.axAcroPDF1.OcxState = ((System.Windows.Forms.AxHost.State)(resources.GetObject("axAcroPDF1.OcxState")));
            this.axAcroPDF1.Size = new System.Drawing.Size(282, 83);
            this.axAcroPDF1.TabIndex = 0;
            //
            // axAcroPDF2
            //
            this.axAcroPDF2.AllowDrop = true;
            this.axAcroPDF2.Dock = System.Windows.Forms.DockStyle.Fill;
            this.axAcroPDF2.Enabled = true;
            this.axAcroPDF2.Location = new System.Drawing.Point(0, 0);
            this.axAcroPDF2.Name = "axAcroPDF2";
            this.axAcroPDF2.OcxState = ((System.Windows.Forms.AxHost.State)(resources.GetObject("axAcroPDF2.OcxState")));
            this.axAcroPDF2.Size = new System.Drawing.Size(282, 151);
            this.axAcroPDF2.TabIndex = 0;
            //
            // toolStrip1
            //
            this.toolStrip1.BackColor = System.Drawing.SystemColors.Control;
            this.toolStrip1.Dock = System.Windows.Forms.DockStyle.None;
            this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.openButton,
            this.toolStripSeparator1,
            this.splitButtonH,
            this.toolStripSeparator2,
            this.splitButtonV});
            this.toolStrip1.Location = new System.Drawing.Point(3, 0);
            this.toolStrip1.Name = "toolStrip1";
            this.toolStrip1.Size = new System.Drawing.Size(172, 25);
            this.toolStrip1.TabIndex = 1;
            this.toolStrip1.Text = "toolStrip1";
            //
            // openButton
            //
            this.openButton.AutoSize = false;
            this.openButton.BackColor = System.Drawing.SystemColors.ActiveCaption;
            this.openButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
            this.openButton.ForeColor = System.Drawing.SystemColors.HighlightText;
            this.openButton.Image = ((System.Drawing.Image)(resources.GetObject("openButton.Image")));
            this.openButton.ImageTransparentColor = System.Drawing.Color.Magenta;
            this.openButton.Name = "openButton";
            this.openButton.Size = new System.Drawing.Size(35, 18);
            this.openButton.Text = "Open";
            this.openButton.ToolTipText = "ファイルを開きます";
            this.openButton.Click += new System.EventHandler(this.openButton_Click);
            //
            // toolStripSeparator1
            //
            this.toolStripSeparator1.Name = "toolStripSeparator1";
            this.toolStripSeparator1.Size = new System.Drawing.Size(6, 25);
            //
            // splitButtonH
            //
            this.splitButtonH.AutoSize = false;
            this.splitButtonH.BackColor = System.Drawing.SystemColors.ActiveCaption;
            this.splitButtonH.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
            this.splitButtonH.ForeColor = System.Drawing.SystemColors.HighlightText;
            this.splitButtonH.Image = ((System.Drawing.Image)(resources.GetObject("splitButtonH.Image")));
            this.splitButtonH.ImageTransparentColor = System.Drawing.Color.Magenta;
            this.splitButtonH.Name = "splitButtonH";
            this.splitButtonH.Size = new System.Drawing.Size(41, 18);
            this.splitButtonH.Text = "split━";
            this.splitButtonH.ToolTipText = "上下に分割します";
            this.splitButtonH.Click += new System.EventHandler(this.splitButton_Click);
            //
            // toolStripSeparator2
            //
            this.toolStripSeparator2.Name = "toolStripSeparator2";
            this.toolStripSeparator2.Size = new System.Drawing.Size(6, 25);
            //
            // splitButtonV
            //
            this.splitButtonV.AutoSize = false;
            this.splitButtonV.BackColor = System.Drawing.SystemColors.ActiveCaption;
            this.splitButtonV.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
            this.splitButtonV.ForeColor = System.Drawing.SystemColors.HighlightText;
            this.splitButtonV.Image = ((System.Drawing.Image)(resources.GetObject("splitButtonV.Image")));
            this.splitButtonV.ImageTransparentColor = System.Drawing.Color.Magenta;
            this.splitButtonV.Name = "splitButtonV";
            this.splitButtonV.Size = new System.Drawing.Size(41, 18);
            this.splitButtonV.Text = "split┃";
            this.splitButtonV.ToolTipText = "左右に分割します";
            this.splitButtonV.Click += new System.EventHandler(this.splitButton_Click2);
            //
            // toolStripContainer1
            //
            //
            // toolStripContainer1.ContentPanel
            //
            this.toolStripContainer1.ContentPanel.Controls.Add(this.splitContainer1);
            this.toolStripContainer1.ContentPanel.Size = new System.Drawing.Size(282, 238);
            this.toolStripContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.toolStripContainer1.Location = new System.Drawing.Point(0, 0);
            this.toolStripContainer1.Name = "toolStripContainer1";
            this.toolStripContainer1.Size = new System.Drawing.Size(282, 263);
            this.toolStripContainer1.TabIndex = 2;
            this.toolStripContainer1.Text = "toolStripContainer1";
            //
            // toolStripContainer1.TopToolStripPanel
            //
            this.toolStripContainer1.TopToolStripPanel.Controls.Add(this.toolStrip1);
            //
            // Form1
            //
            this.AllowDrop = true;
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(282, 263);
            this.Controls.Add(this.toolStripContainer1);
            this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
            this.Name = "Form1";
            this.Text = "Form1";
            this.DragEnter += new System.Windows.Forms.DragEventHandler(this.Form1_DragEnter);
            this.splitContainer1.Panel1.ResumeLayout(false);
            this.splitContainer1.Panel2.ResumeLayout(false);
            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
            this.splitContainer1.ResumeLayout(false);
            ((System.ComponentModel.ISupportInitialize)(this.axAcroPDF1)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.axAcroPDF2)).EndInit();
            this.toolStrip1.ResumeLayout(false);
            this.toolStrip1.PerformLayout();
            this.toolStripContainer1.ContentPanel.ResumeLayout(false);
            this.toolStripContainer1.TopToolStripPanel.ResumeLayout(false);
            this.toolStripContainer1.TopToolStripPanel.PerformLayout();
            this.toolStripContainer1.ResumeLayout(false);
            this.toolStripContainer1.PerformLayout();
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.SplitContainer splitContainer1;
        private AxAcroPDFLib.AxAcroPDF axAcroPDF1;
        private AxAcroPDFLib.AxAcroPDF axAcroPDF2;
        private System.Windows.Forms.ToolStrip toolStrip1;
        private System.Windows.Forms.ToolStripButton openButton;
        private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
        private System.Windows.Forms.ToolStripButton splitButtonV;
        private System.Windows.Forms.ToolStripContainer toolStripContainer1;
        private System.Windows.Forms.ToolStripButton splitButtonH;
        private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
    }
}

 

 

2013年1月 2日 (水)

C#:超小型 Radiko プレーヤー

選局ボタンを押すだけで聴けるお手軽で場所をとらない Radiko プレーヤーを作ってみた。

  1. WebBrowserコントロールでRadikoサイトを読み込むだけの安易な設計。
  2. 小型化のためウインドウ枠もなくす。
  3. 余白をドラッグするとウインドウ移動、余白をダブルクリックで閉じる。
  4. 音量調節はCoreAudioApi名前空間を読み込み、マスターボリュームをコントロールする。これまた安易な設計。
  5. 初期表示はモニターの右上に。
  6. とりあえず東京エリアの局だけ。
  7. 局リストを変えればたぶん他地域でも聴ける。
  8. ボタンの字が小さいので、カーソルを当てるとツールチップで局名を表示。
  9. 録音機能等はRadikoに負担になるらしいので付けない。

使ってみるととても便利で感動。音量調節がCoreAudioApiで簡単にできるのも嬉しい。

Radio

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using CoreAudioApi;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private MMDevice device;
        ToolTip tt = new ToolTip();
        Point p0 = new Point();
        int w;

        public Form1()
        {
            InitializeComponent();
            //音量調節
            MMDeviceEnumerator DevEnum = new MMDeviceEnumerator();
            device = DevEnum.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);
            this.Master.Value = (int)(device.AudioEndpointVolume.MasterVolumeLevelScalar * 100);
            device.AudioEndpointVolume.OnVolumeNotification += new AudioEndpointVolumeNotificationDelegate(AudioEndpointVolume_OnVolumeNotification);
            //モニターの右上に表示
            w = System.Windows.Forms.Screen.GetBounds(this).Width;
            this.Location = new Point(w - 210, 5);
            //選局ボタン
            this.buttonSet();
            //ツールチップ
            tt.SetToolTip(this.flowLayoutPanel1, "ダブルクリックで閉じます");
            tt.SetToolTip(this.label1, "ダブルクリックで閉じます");
            tt.SetToolTip(this.button2, "閉じる");
        }

        private void buttonSet()
        {
            string[] ID = new string[] { "FMT", "FMJ", "INT", "JORF", "BAYFM78", "NACK5", "YFM",
                "HOUSOU-DAIGAKU", "QRR", "RN1", "RN2", "TBS", "LFR" };
            string[] Name = new string[] { "TokyoFM","J-WAVE","InterFM","ラジオ日本","BayFM","NACK5","FMヨコハマ",
                "放送大学","文化放送","NIKKEI第1","NIKKEI第2","TBSラジオ","ニッポン放送"};
            Button[] bt = new Button[ID.Length];
            tt.InitialDelay = 500;
            tt.ReshowDelay = 500;
            tt.AutoPopDelay = 10000;
            tt.ShowAlways = true;

            for (int i = 0; i < ID.Length; ++i)
            {
                bt[i] = new Button();
                bt[i].Text = Name[i];
                bt[i].Name = ID[i];
                tt.SetToolTip(bt[i], bt[i].Text);
                bt[i].Font = new System.Drawing.Font("", 6);
                bt[i].Size = new Size(50, 16);
                bt[i].Margin = new Padding(0);
                bt[i].Padding = new Padding(0);
                this.flowLayoutPanel1.Controls.Add(bt[i]);
                bt[i].Click += new EventHandler(BT_CLICK);
            }

            int tuneArea = ID.Length * 16 / 4 + (ID.Length % 4) * 16;
            this.flowLayoutPanel1.Size = new Size(200, tuneArea);
            this.webBrowser1.Height = tuneArea;
            this.webBrowser1.Visible = false;
            this.Master.Location = new Point(25, label1.Height + tuneArea);
            this.button1.Location = new Point(5, label1.Height + tuneArea + 2);

            this.ClientSize = new Size(200, label1.Height + tuneArea + 25);
        }

        void BT_CLICK(object sender, EventArgs e)
        {
            this.webBrowser1.Navigate(@"http://radiko.jp/#" + ((Button)sender).Name);
            this.webBrowser1.Refresh();
        }

        void AudioEndpointVolume_OnVolumeNotification(AudioVolumeNotificationData data)
        {
            if (this.InvokeRequired)
            {
                object[] Params = new object[1];
                Params[0] = data;
                this.Invoke(new AudioEndpointVolumeNotificationDelegate(AudioEndpointVolume_OnVolumeNotification), Params);
            }
            else
            {
                Master.Value = (int)(data.MasterVolume * 100);
            }
        }

        private void Master_Scroll(object sender, EventArgs e)
        {
            device.AudioEndpointVolume.MasterVolumeLevelScalar = ((float)Master.Value / 100.0f);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.webBrowser1.Navigate(@"about:blank");
        }

        private void flowLayoutPanel1_DoubleClick(object sender, EventArgs e)
        {
            this.Close();
        }

        private void label1_MouseDown(object sender, MouseEventArgs e)
        {
            p0 = new Point(e.X, e.Y);
        }

        private void label1_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                this.Left += e.X - p0.X;
                this.Top += e.Y - p0.Y;
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            this.Close();
        }

    }
}

Dsigner.cs

namespace WindowsFormsApplication1
{
    partial class Form1
    {
        /// <summary>
        /// 必要なデザイナー変数です。
        /// </summary>

        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 使用中のリソースをすべてクリーンアップします。
        /// </summary>
        /// <param name="disposing">マネージ リソースが破棄される場合 true、破棄されない場合は false です。</param>

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows フォーム デザイナーで生成されたコード

        /// <summary>
        /// デザイナー サポートに必要なメソッドです。このメソッドの内容を
        /// コード エディターで変更しないでください。
        /// </summary>

        private void InitializeComponent()
        {
            this.Master = new System.Windows.Forms.TrackBar();
            this.webBrowser1 = new System.Windows.Forms.WebBrowser();
            this.button1 = new System.Windows.Forms.Button();
            this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
            this.label1 = new System.Windows.Forms.Label();
            this.button2 = new System.Windows.Forms.Button();
            ((System.ComponentModel.ISupportInitialize)(this.Master)).BeginInit();
            this.label1.SuspendLayout();
            this.SuspendLayout();
            //
            // Master
            //

            this.Master.AutoSize = false;
            this.Master.LargeChange = 20;
            this.Master.Location = new System.Drawing.Point(25, 175);
            this.Master.Margin = new System.Windows.Forms.Padding(0);
            this.Master.Maximum = 100;
            this.Master.Name = "Master";
            this.Master.Size = new System.Drawing.Size(175, 20);
            this.Master.SmallChange = 5;
            this.Master.TabIndex = 0;
            this.Master.TickFrequency = 10;
            this.Master.TickStyle = System.Windows.Forms.TickStyle.None;
            this.Master.Scroll += new System.EventHandler(this.Master_Scroll);
            //
            // webBrowser1
            //

            this.webBrowser1.Location = new System.Drawing.Point(0, 0);
            this.webBrowser1.MinimumSize = new System.Drawing.Size(20, 20);
            this.webBrowser1.Name = "webBrowser1";
            this.webBrowser1.ScrollBarsEnabled = false;
            this.webBrowser1.Size = new System.Drawing.Size(200, 175);
            this.webBrowser1.TabIndex = 1;
            //
            // button1
            //

            this.button1.BackColor = System.Drawing.Color.Blue;
            this.button1.Font = new System.Drawing.Font("MS UI Gothic", 7.5F);
            this.button1.ForeColor = System.Drawing.Color.Blue;
            this.button1.Location = new System.Drawing.Point(3, 178);
            this.button1.Margin = new System.Windows.Forms.Padding(0);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(16, 16);
            this.button1.TabIndex = 2;
            this.button1.UseVisualStyleBackColor = false;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            //
            // flowLayoutPanel1
            //

            this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 12);
            this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0);
            this.flowLayoutPanel1.Name = "flowLayoutPanel1";
            this.flowLayoutPanel1.Size = new System.Drawing.Size(200, 118);
            this.flowLayoutPanel1.TabIndex = 3;
            this.flowLayoutPanel1.DoubleClick += new System.EventHandler(this.flowLayoutPanel1_DoubleClick);
            this.flowLayoutPanel1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.label1_MouseDown);
            this.flowLayoutPanel1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.label1_MouseMove);
            //
            // label1
            //

            this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
            | System.Windows.Forms.AnchorStyles.Right)));
            this.label1.BackColor = System.Drawing.Color.Blue;
            this.label1.Controls.Add(this.button2);
            this.label1.ForeColor = System.Drawing.SystemColors.ButtonHighlight;
            this.label1.Location = new System.Drawing.Point(0, 0);
            this.label1.Margin = new System.Windows.Forms.Padding(0);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(200, 12);
            this.label1.TabIndex = 0;
            this.label1.Text = "Ice Potato Radiko Player";
            this.label1.DoubleClick += new System.EventHandler(this.flowLayoutPanel1_DoubleClick);
            this.label1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.label1_MouseDown);
            this.label1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.label1_MouseMove);
            //
            // button2
            //

            this.button2.BackColor = System.Drawing.Color.Red;
            this.button2.Location = new System.Drawing.Point(188, 0);
            this.button2.Name = "button2";
            this.button2.Size = new System.Drawing.Size(12, 12);
            this.button2.TabIndex = 4;
            this.button2.Text = "button2";
            this.button2.UseVisualStyleBackColor = false;
            this.button2.Click += new System.EventHandler(this.button2_Click);
            //
            // Form1
            //

            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(200, 200);
            this.Controls.Add(this.flowLayoutPanel1);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.Master);
            this.Controls.Add(this.button1);
            this.Controls.Add(this.webBrowser1);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
            this.Name = "Form1";
            this.Text = "Ice Potato Radiko Player";
            ((System.ComponentModel.ISupportInitialize)(this.Master)).EndInit();
            this.label1.ResumeLayout(false);
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.TrackBar Master;
        private System.Windows.Forms.WebBrowser webBrowser1;
        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Button button2;
    }
}

 

 

2012年12月31日 (月)

C#:ウインドウ分割のできるPDFビューア

ウインドウ分割ができると、目次と本文を同時に表示させたりと便利。しかし無料のAdobe Readerはウインドウ分割ができない。そこで手間をかけずに、axAcroPDFコントロールを用いてウインドウ分割できるPDFビューアを作った。

  1. Splitボタンを押すとウインドウが二つに分割される。
  2. 画面の分割にはSplitContainerを用いる。これで境界線を移動できる。
  3. ウインドウへのDrag & Dropを受け付ける。
  4. axAcroPDFはそのままではDrag & Dropを受け付けないようなので、フォームのDragEnterイベントを利用する。
  5. 起動時にスタートページを表示する。axAcroPDFはリソースファイルを読めないっぽいので、一度保存してからファイルパスを指定して開く。
  6. デスクトップにショートカットを作る。
  7. ショートカットへのDrag & Dropも受け付ける。
  8. さらにOpenボタンでファイルを開くダイアログも表示。
  9. ウインドウ上部にファイル名を表示。
  10. axAcroPDFを設置するコードは面倒なので、Designer.csに任せる。

お手軽に作ったが、けっこう使える。

▼起動時

Pv4

▼ファイルを読み込みSplitボタンを押したとき

Pv3

▼デスクトップにアイコン

Icon

Program.cs

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

namespace WindowsFormsApplication1
{
    static class Program
    {
        /// <summary>
        /// アプリケーションのメイン エントリ ポイントです。
        /// </summary>

        [STAThread]
        static void Main(string[] filelist) //アイコンへのD&Dのファイル名を取得
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1(filelist));//ファイル名をForm1に渡す
        }
    }
}

Form1.cs

using System;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
using IWshRuntimeLibrary;
using Microsoft.Win32;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        string temp = Path.GetTempPath() + @"start.pdf";
        bool spd = true;
        private SaveWindowPosition swp = null;

        public Form1(string[] init) //アイコンへのD&Dを受け付ける
        {
            InitializeComponent();
            this.Text = "Ice Potato PDF Viewer";
            this.Size = new Size(400, 600);
            this.splitContainer1.SplitterDistance = this.ClientSize.Height;
            this.Load += new EventHandler(Form1_Load);
            this.FormClosing += new FormClosingEventHandler(Form1_FormClosing);
            this.DragEnter += new DragEventHandler(Form1_DragEnter);
            this.splitContainer1.SplitterWidth = 5;

            //ショートカットの設定
            string shortcutPath = System.IO.Path.Combine(Environment.GetFolderPath(System.Environment.SpecialFolder.DesktopDirectory), @"PDF Viewer.lnk");
            string targetPath = Application.ExecutablePath;
            IWshShell_Class shell = new IWshRuntimeLibrary.IWshShell_Class();
            IWshRuntimeLibrary.IWshShortcut shortcut =
                (IWshRuntimeLibrary.IWshShortcut)shell.CreateShortcut(shortcutPath);
            shortcut.TargetPath = targetPath;
            shortcut.Description = "Ice Potato PDF Viewer";
            shortcut.IconLocation = Application.ExecutablePath + ",0";
            shortcut.Save();
            System.Runtime.InteropServices.Marshal.ReleaseComObject(shortcut);

            //リソースのスタートページを一時フォルダに保存
            PdfReader r = new PdfReader(Properties.Resources.start);
            int pageNum = r.NumberOfPages;
            Document document = new Document();
            PdfWriter writer =
            PdfWriter.GetInstance(document, new FileStream(temp, FileMode.Create));
            writer.Open();
            document.Open();
            PdfContentByte cb = writer.DirectContent;
            int i = 0;
            while (i < pageNum)
            {
                document.NewPage();
                PdfImportedPage page1 = writer.GetImportedPage(r, ++i);
                cb.AddTemplate(page1, 1f, 0, 0, 1f, 0, 0);
            }
            document.Close();
            r.Close();
            writer.Close();
            if (init.Length > 0)
            {
                this.axAcroPDF1.src = init[0];
                this.axAcroPDF2.src = init[0];
                nameset();
            }
            else
            {
                this.axAcroPDF1.src = temp;
                this.axAcroPDF2.src = temp;
                this.Text = "Welcome - Ice Potato PDF Viewer";
            }
            this.axAcroPDF1.setShowToolbar(false);
            this.axAcroPDF2.setShowToolbar(false);
        }

        void Form1_Load(object sender, EventArgs e)
        {
            //ウインドウ復元
            swp = new SaveWindowPosition(this);
            swp.Load();
        }

        void Form1_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                //複数ファイルがドラッグされたときのためにまとめて取得
                string[] ddata = (string[])e.Data.GetData(DataFormats.FileDrop);
                foreach (string f in ddata)
                {
                    if (!System.IO.File.Exists(f))
                    {
                        //ファイルでないときは処理をやめる
                        return;
                    }
                }
                e.Effect = DragDropEffects.Copy;
                string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
                //最初のファイルのみビューアに表示
                this.axAcroPDF1.src = files[0];
                this.axAcroPDF2.src = files[0];
                nameset();
            }
        }

        private void nameset()
        {
            string fname = this.axAcroPDF1.src;
            string[] fn = fname.Split('\\');
            this.Text = fn[fn.Length - 1] + " - Ice Potato PDF Viewer";
        }

        void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            //ウインドウを閉じるときにスタートページ削除
            System.IO.File.Delete(temp);
            swp.Save();
        }

        private void splitButton_Click(object sender, EventArgs e)
        {
            //splitボタンを押すごとに交互に表示を切り替える
            if (spd)
            {
                this.splitContainer1.SplitterDistance = (int)(this.ClientSize.Height / 2);
            }
            else
            {
                this.splitContainer1.SplitterDistance = this.ClientSize.Height;
            }
            spd = !spd;
        }

        private void openButton_Click(object sender, EventArgs e)
        {
            //ファイルを開くダイアログ
            OpenFileDialog fd = new OpenFileDialog();
            fd.Filter = "PDF(*.pdf)|*.pdf";
            if (fd.ShowDialog() == DialogResult.OK)
            {
                this.axAcroPDF1.src = fd.FileName;
                this.axAcroPDF2.src = fd.FileName;
                nameset();
            }
        }

        //ウインドウ位置の保存
        public class SaveWindowPosition
        {
            public SaveWindowPosition(Form form)
            {
                this.form = form;
                userReg = Application.UserAppDataRegistry;
            }

            private Form form = null;
            private RegistryKey userReg = null;

            public void Save()
            {
                userReg.SetValue("WindowState", (int)(form.WindowState));
                if (form.WindowState == FormWindowState.Normal)
                {
                    userReg.SetValue("X", form.Location.X);
                    userReg.SetValue("Y", form.Location.Y);
                    userReg.SetValue("Height", form.Height);
                    userReg.SetValue("Width", form.Width);
                }
            }

            public void Load()
            {
                try
                {
                    int x = (int)(userReg.GetValue("X"));
                    int y = (int)(userReg.GetValue("Y"));
                    form.Location = new Point(x, y);

                    int height = (int)userReg.GetValue("Height");
                    int width = (int)userReg.GetValue("Width");
                    form.Size = new Size(width, height);

                    form.WindowState = (FormWindowState)userReg.GetValue("WindowState2");
                }
                catch
                {
                    return;
                }
            }
        }
    }
}

Designer.cs

namespace WindowsFormsApplication1
{
    partial class Form1
    {
        /// <summary>
        /// 必要なデザイナー変数です。
        /// </summary>

        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 使用中のリソースをすべてクリーンアップします。
        /// </summary>
        /// <param name="disposing">マネージ リソースが破棄される場合 true、破棄されない場合は false です。</param>

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows フォーム デザイナーで生成されたコード

        /// <summary>
        /// デザイナー サポートに必要なメソッドです。このメソッドの内容を
        /// コード エディターで変更しないでください。
        /// </summary>

        private void InitializeComponent()
        {
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
            this.splitContainer1 = new System.Windows.Forms.SplitContainer();
            this.axAcroPDF1 = new AxAcroPDFLib.AxAcroPDF();
            this.axAcroPDF2 = new AxAcroPDFLib.AxAcroPDF();
            this.toolStrip1 = new System.Windows.Forms.ToolStrip();
            this.openButton = new System.Windows.Forms.ToolStripButton();
            this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
            this.splitButton = new System.Windows.Forms.ToolStripButton();
            this.toolStripContainer1 = new System.Windows.Forms.ToolStripContainer();
            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
            this.splitContainer1.Panel1.SuspendLayout();
            this.splitContainer1.Panel2.SuspendLayout();
            this.splitContainer1.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.axAcroPDF1)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.axAcroPDF2)).BeginInit();
            this.toolStrip1.SuspendLayout();
            this.toolStripContainer1.ContentPanel.SuspendLayout();
            this.toolStripContainer1.TopToolStripPanel.SuspendLayout();
            this.toolStripContainer1.SuspendLayout();
            this.SuspendLayout();
            //
            // splitContainer1
            //

            this.splitContainer1.AllowDrop = true;
            this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.splitContainer1.Location = new System.Drawing.Point(0, 0);
            this.splitContainer1.Name = "splitContainer1";
            this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal;
            //
            // splitContainer1.Panel1
            //

            this.splitContainer1.Panel1.Controls.Add(this.axAcroPDF1);
            //
            // splitContainer1.Panel2
            //

            this.splitContainer1.Panel2.Controls.Add(this.axAcroPDF2);
            this.splitContainer1.Panel2MinSize = 0;
            this.splitContainer1.Size = new System.Drawing.Size(282, 238);
            this.splitContainer1.SplitterDistance = 83;
            this.splitContainer1.TabIndex = 0;
            //
            // axAcroPDF1
            //

            this.axAcroPDF1.AllowDrop = true;
            this.axAcroPDF1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.axAcroPDF1.Enabled = true;
            this.axAcroPDF1.Location = new System.Drawing.Point(0, 0);
            this.axAcroPDF1.Name = "axAcroPDF1";
            this.axAcroPDF1.OcxState = ((System.Windows.Forms.AxHost.State)(resources.GetObject("axAcroPDF1.OcxState")));
            this.axAcroPDF1.Size = new System.Drawing.Size(282, 83);
            this.axAcroPDF1.TabIndex = 0;
            //
            // axAcroPDF2
            //

            this.axAcroPDF2.AllowDrop = true;
            this.axAcroPDF2.Dock = System.Windows.Forms.DockStyle.Fill;
            this.axAcroPDF2.Enabled = true;
            this.axAcroPDF2.Location = new System.Drawing.Point(0, 0);
            this.axAcroPDF2.Name = "axAcroPDF2";
            this.axAcroPDF2.OcxState = ((System.Windows.Forms.AxHost.State)(resources.GetObject("axAcroPDF2.OcxState")));
            this.axAcroPDF2.Size = new System.Drawing.Size(282, 151);
            this.axAcroPDF2.TabIndex = 0;
            //
            // toolStrip1
            //

            this.toolStrip1.BackColor = System.Drawing.SystemColors.Control;
            this.toolStrip1.Dock = System.Windows.Forms.DockStyle.None;
            this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.openButton,
            this.toolStripSeparator1,
            this.splitButton});
            this.toolStrip1.Location = new System.Drawing.Point(3, 0);
            this.toolStrip1.Name = "toolStrip1";
            this.toolStrip1.Size = new System.Drawing.Size(84, 25);
            this.toolStrip1.TabIndex = 1;
            this.toolStrip1.Text = "toolStrip1";
            //
            // openButton
            //

            this.openButton.AutoSize = false;
            this.openButton.BackColor = System.Drawing.SystemColors.ActiveCaption;
            this.openButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
            this.openButton.ForeColor = System.Drawing.SystemColors.HighlightText;
            this.openButton.Image = ((System.Drawing.Image)(resources.GetObject("openButton.Image")));
            this.openButton.ImageTransparentColor = System.Drawing.Color.Magenta;
            this.openButton.Name = "openButton";
            this.openButton.Size = new System.Drawing.Size(35, 18);
            this.openButton.Text = "Open";
            this.openButton.Click += new System.EventHandler(this.openButton_Click);
            //
            // toolStripSeparator1
            //

            this.toolStripSeparator1.Name = "toolStripSeparator1";
            this.toolStripSeparator1.Size = new System.Drawing.Size(6, 25);
            //
            // splitButton
            //

            this.splitButton.AutoSize = false;
            this.splitButton.BackColor = System.Drawing.SystemColors.ActiveCaption;
            this.splitButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
            this.splitButton.ForeColor = System.Drawing.SystemColors.HighlightText;
            this.splitButton.Image = ((System.Drawing.Image)(resources.GetObject("splitButton.Image")));
            this.splitButton.ImageTransparentColor = System.Drawing.Color.Magenta;
            this.splitButton.Name = "splitButton";
            this.splitButton.Size = new System.Drawing.Size(31, 18);
            this.splitButton.Text = "split";
            this.splitButton.Click += new System.EventHandler(this.splitButton_Click);
            //
            // toolStripContainer1
            //
            //
            // toolStripContainer1.ContentPanel
            //

            this.toolStripContainer1.ContentPanel.Controls.Add(this.splitContainer1);
            this.toolStripContainer1.ContentPanel.Size = new System.Drawing.Size(282, 238);
            this.toolStripContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.toolStripContainer1.Location = new System.Drawing.Point(0, 0);
            this.toolStripContainer1.Name = "toolStripContainer1";
            this.toolStripContainer1.Size = new System.Drawing.Size(282, 263);
            this.toolStripContainer1.TabIndex = 2;
            this.toolStripContainer1.Text = "toolStripContainer1";
            //
            // toolStripContainer1.TopToolStripPanel
            //

            this.toolStripContainer1.TopToolStripPanel.Controls.Add(this.toolStrip1);
            //
            // Form1
            //

            this.AllowDrop = true;
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(282, 263);
            this.Controls.Add(this.toolStripContainer1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.splitContainer1.Panel1.ResumeLayout(false);
            this.splitContainer1.Panel2.ResumeLayout(false);
            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
            this.splitContainer1.ResumeLayout(false);
            ((System.ComponentModel.ISupportInitialize)(this.axAcroPDF1)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.axAcroPDF2)).EndInit();
            this.toolStrip1.ResumeLayout(false);
            this.toolStrip1.PerformLayout();
            this.toolStripContainer1.ContentPanel.ResumeLayout(false);
            this.toolStripContainer1.TopToolStripPanel.ResumeLayout(false);
            this.toolStripContainer1.TopToolStripPanel.PerformLayout();
            this.toolStripContainer1.ResumeLayout(false);
            this.toolStripContainer1.PerformLayout();
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.SplitContainer splitContainer1;
        private AxAcroPDFLib.AxAcroPDF axAcroPDF1;
        private AxAcroPDFLib.AxAcroPDF axAcroPDF2;
        private System.Windows.Forms.ToolStrip toolStrip1;
        private System.Windows.Forms.ToolStripButton openButton;
        private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
        private System.Windows.Forms.ToolStripButton splitButton;
        private System.Windows.Forms.ToolStripContainer toolStripContainer1;
    }
}

 

 

2012年12月28日 (金)

C#:3次元グラフを作る

図形と配列の扱いに慣れてきたので、面グラフを作ってみようと思う。

  1. ファイルから数値データを読み込み、3次元プロットする。
  2. 前回の立体回転の画面にグラフ描画機能を追加する。
  3. Graphボタンを押すとグラフモードに切り替わる。
  4. あとは立体回転と全く同様に、マウスドラッグやトラックバーで回転させることができる。
  5. 描画が複雑になるため、新たにグラフ専用の描画クラスを用意する。
  6. 閉じた立体と異なり、角度によっては面の裏側が見えるので、隠面処理を法線ベクトルと重心の座標の両面から行う。
  7. 立体回転と同じく、右方向から光が当たっている設定。
  8. ただしグラフ面の裏側はグレーで塗る。

凹凸のあるグラフでも、以下のように何とか表示できた。クリーム色の面は座標平面のつもり。

Graph1

Graph4_2

Graph2

Graph3

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

namespace CubicRotate1
{

    public class Form1 : Form//Designer.cs 削除
    {
        private System.ComponentModel.IContainer components = null;
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        PictureBox pb = new PictureBox();
        TrackBar t1 = new TrackBar();
        TrackBar t2 = new TrackBar();
        Label l1 = new Label();
        Label l2 = new Label();
        Label l3 = new Label();
        double rxz = 0;
        double ryz = 0;
        int axz = 0;
        int ayz = 0;
        int axz0 = 0;
        int ayz0 = 0;
        Point mp;
        int ck = 0;
        string[] Cubes = { "正四面体", "立方体", "正八面体", "立方体カット", "六角錐", "複合" };
        double Smin = 40000;
        int axzm = 0;
        int ayzm = 0;
        Button gbutton = new Button();
        double[][][] P;
        double[][][] P0;

        public Form1()
        {
            this.components = new System.ComponentModel.Container();
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.Text = "CubicRotate1";
            this.ClientSize = new Size(300, 300);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;
            this.Controls.Add(pb);
            pb.SetBounds(0, 0, 200, 200);
            pb.BackColor = Color.White;
            this.Controls.Add(t1);
            t1.AutoSize = false;
            t1.SetBounds(0, 200, 200, 20);
            t1.TickStyle = TickStyle.None;
            t1.Minimum = 0;
            t1.Maximum = 360;
            t1.Value = axz + 180;
            this.Controls.Add(t2);
            t2.AutoSize = false;
            t2.Orientation = Orientation.Vertical;
            t2.SetBounds(200, 0, 20, 200);
            t2.TickStyle = TickStyle.None;
            t2.Minimum = 0;
            t2.Maximum = 180;
            t2.Value = ayz + 90;
            this.Controls.Add(l1);
            this.Controls.Add(l2);
            this.Controls.Add(l3);
            l1.SetBounds(70, 220, 60, 20);
            l1.TextAlign = ContentAlignment.MiddleCenter;
            l1.Text = "Axz: " + axz.ToString();
            l2.SetBounds(220, 90, 60, 20);
            l2.TextAlign = ContentAlignment.MiddleCenter;
            l2.Text = "Ayz: " + ayz.ToString();
            l3.SetBounds(220, 140, 80, 60);
            l3.TextAlign = ContentAlignment.TopLeft;

            gbutton.SetBounds(220, 0, 80, 60);
            gbutton.Text = "Graph";
            this.Controls.Add(gbutton);

            pb.MouseDown += new MouseEventHandler(pb_MouseDown);
            pb.MouseMove += new MouseEventHandler(pb_MouseMove);

            t1.MouseDown += new MouseEventHandler(t_MouseDown);
            t2.MouseDown += new MouseEventHandler(t_MouseDown);
            t1.ValueChanged += new EventHandler(t_ValueChanged);
            t2.ValueChanged += new EventHandler(t_ValueChanged);

            GroupBox gb = new GroupBox();
            gb.SetBounds(10, 240, 280, 55);
            this.Controls.Add(gb);

            RadioButton[] rd = new RadioButton[Cubes.Length];

            for (int i = 0; i < Cubes.Length; ++i)
            {
                rd[i] = new RadioButton();
                if (i < 3)
                {
                    rd[i].SetBounds(10 + i * 90, 10, 85, 20);
                }
                else
                {
                    rd[i].SetBounds(10 + (i - 3) * 90, 30, 85, 20);
                }
                rd[i].Text = Cubes[i];
                rd[i].CheckedChanged += new EventHandler(rd_CheckedChanged);

            }
            rd[0].Checked = true;
            gb.Controls.AddRange(rd);

            gbutton.Click += new EventHandler(gbutton_Click);

            //座標平面
            P0 = new double[3][][];
            P0[0] = new double[3][];
            P0[1] = new double[3][];
            P0[2] = new double[3][];
            P0[0][0] = new double[3] { 40, 160, -60 };
            P0[0][1] = new double[3] { 100, 160, -60 };
            P0[0][2] = new double[3] { 160, 160, -60 };
            P0[1][0] = new double[3] { 40, 160, 0 };
            P0[1][1] = new double[3] { 100, 160, 0 };
            P0[1][2] = new double[3] { 160, 160, 0 };
            P0[2][0] = new double[3] { 40, 160, 60 };
            P0[2][1] = new double[3] { 100, 160, 60 };
            P0[2][2] = new double[3] { 160, 160, 60 };

            if (ck != 99) { Rotate(); } else { Rotate2(); }
            setFocus();
        }

        void gbutton_Click(object sender, EventArgs e)
        {
            ck = 99;
            P = getTable();
            Rotate2();
            setFocus();
        }

        private double[][][] getTable()
        {
            System.IO.StreamReader r = (new System.IO.StreamReader(@"C:\Data.txt", System.Text.Encoding.Default));
            string src = "";
            while (r.Peek() >= 0)
            {
                string line = r.ReadLine();
                if (line.Length > 2) { src += line + "\n"; }
            }
            r.Close();

            //末尾の改行を除去
            src = src.TrimEnd('\n');
            string[] lines = src.Split('\n');
            double[][][] data = new double[lines.Length][][];
            for (int i = 0; i < lines.Length; ++i)
            {
                string[] ele = lines[i].Split('\t');
                data[i] = new double[ele.Length][];
                int xrange = ele.Length - 1;
                int yrange = lines.Length - 1;
                for (int j = 0; j < ele.Length; ++j)
                {
                    data[i][j] = new double[3];
                    data[i][j][0] = j * (120 / xrange) + 40;
                    data[i][j][1] = 160 - Double.Parse(ele[j]) * 7;
                    data[i][j][2] = i * (120 / yrange) - 60;//データシートの縦方向を奥行き方向に
                }
            }
            return data;
        }

        private void Rotate2()
        {
            int[][][] Q = new int[P.Length][][];
            int[][][] Q0 = new int[P0.Length][][];
            double temp0, temp1, temp2;

            for (int i = 0; i < P.Length; ++i)
            {
                Q[i] = new int[P[i].Length][];
                for(int j = 0; j < P[i].Length; ++j)
                {
                    Q[i][j] = new int[3];
                    //y軸のまわりに回転
                    temp0 = (P[i][j][0] - 100) * Math.Cos(rxz) - P[i][j][2] * Math.Sin(rxz) + 100;
                    temp1 = P[i][j][1];
                    temp2 = (P[i][j][0] - 100) * Math.Sin(rxz) + P[i][j][2] * Math.Cos(rxz);
                    //x軸のまわりに回転
                    Q[i][j][0] = (int)temp0;
                    Q[i][j][1] = (int)((temp1 - 100) * Math.Cos(ryz) - temp2 * Math.Sin(ryz)) + 100;
                    Q[i][j][2] = (int)((temp1 - 100) * Math.Sin(ryz) + temp2 * Math.Cos(ryz));
                }
            }

            for (int i = 0; i < P0.Length; ++i)
            {
                Q0[i] = new int[P0.Length][];
                for (int j = 0; j < P0[i].Length; ++j)
                {
                    Q0[i][j] = new int[3];
                    //y軸のまわりに回転
                    temp0 = (P0[i][j][0] - 100) * Math.Cos(rxz) - P0[i][j][2] * Math.Sin(rxz) + 100;
                    temp1 = P0[i][j][1];
                    temp2 = (P0[i][j][0] - 100) * Math.Sin(rxz) + P0[i][j][2] * Math.Cos(rxz);
                    //x軸のまわりに回転
                    Q0[i][j][0] = (int)temp0;
                    Q0[i][j][1] = (int)((temp1 - 100) * Math.Cos(ryz) - temp2 * Math.Sin(ryz)) + 100;
                    Q0[i][j][2] = (int)((temp1 - 100) * Math.Sin(ryz) + temp2 * Math.Cos(ryz));
                }
            }

            //面の取得
            int[][][][] F = new int[Q.Length - 1][][][];
            int[][][][] F0 = new int[Q0.Length - 1][][][];
            int[][] G = new int[Q.Length - 1][];
            for (int i = 0; i < Q.Length - 1; ++i)
            {
                F[i] = new int[Q[i].Length - 1][][];
                G[i] = new int[F[i].Length];
                for (int j = 0; j < Q[i].Length - 1; ++j)
                {
                    F[i][j] = new int[4][];
                    F[i][j][0] = new int[3] { Q[i][j][0], Q[i][j][1], Q[i][j][2] };
                    F[i][j][1] = new int[3] { Q[i+1][j][0], Q[i+1][j][1], Q[i+1][j][2] };
                    F[i][j][2] = new int[3] { Q[i+1][j+1][0], Q[i+1][j+1][1], Q[i+1][j+1][2] };
                    F[i][j][3] = new int[3] { Q[i][j+1][0], Q[i][j+1][1], Q[i][j+1][2] };
                    G[i][j] = (Q[i][j][2] + Q[i + 1][j][2] + Q[i + 1][j + 1][2] + Q[i][j + 1][2]) / 4;
                }
            }

            int[][] pair = new int[G.Length * G[0].Length][];
            int[] gs = new int[G.Length * G[0].Length];
            int m = 0;
            for (int i = 0; i < G.Length; ++i)
            {
                for (int j = 0; j < G[0].Length; ++j)
                {
                    pair[m] = new int[2] { i, j };
                    gs[m] = G[i][j];
                    ++m;
                }
            }
            Array.Sort(gs, pair);


            for (int i = 0; i < Q0.Length - 1; ++i)
            {
                F0[i] = new int[Q0[i].Length - 1][][];
                for (int j = 0; j < Q0[i].Length - 1; ++j)
                {
                    F0[i][j] = new int[4][];
                    F0[i][j][0] = new int[3] { Q0[i][j][0], Q0[i][j][1], Q0[i][j][2] };
                    F0[i][j][1] = new int[3] { Q0[i + 1][j][0], Q0[i + 1][j][1], Q0[i + 1][j][2] };
                    F0[i][j][2] = new int[3] { Q0[i + 1][j + 1][0], Q0[i + 1][j + 1][1], Q0[i + 1][j + 1][2] };
                    F0[i][j][3] = new int[3] { Q0[i][j + 1][0], Q0[i][j + 1][1], Q0[i][j + 1][2] };
                }
            }

            //法線ベクトル
            double[][] Nx = new double[F.Length][];
            int[][] Nz = new int[F.Length][];
            for (int i = 0; i < F.Length; ++i)
            {
                int[] vec1 = new int[3];
                int[] vec2 = new int[3];
                int[] cr = new int[3];

                if (F[i].Length > 0)
                {
                    Nx[i] = new double[F[i].Length];
                    Nz[i] = new int[F[i].Length];
                    for (int j = 0; j < F[i].Length; ++j)
                    {
                        for (int k = 0; k < 3; ++k)
                        {
                            vec1[k] = F[i][j][1][k] - F[i][j][0][k];
                            vec2[k] = F[i][j][2][k] - F[i][j][1][k];

                        }
                        for (int k = 0; k < 3; ++k)
                        {
                            cr[k] = vec2[(k + 1) % 3] * vec1[(k + 2) % 3] - vec2[(k + 2) % 3] * vec1[(k + 1) % 3];
                        }
                        //法線ベクトルの大きさ
                        double len = Math.Sqrt(cr[0] * cr[0] + cr[1] * cr[1] + cr[2] * cr[2]);
                        //x成分を規格化
                        Nx[i][j] = cr[0] / len;
                        Nz[i][j] = cr[2];
                    }
                }
            }

            Bitmap sheet = new Bitmap(pb.Width, pb.Height);
            Graphics g = Graphics.FromImage(sheet);
            for (int i = 0; i < F0.Length; ++i)
            {
                for (int j = 0; j < F0[i].Length; ++j)
                {
                    if (F[i][j].Length > 0 )
                    {
                        Point[] p = new Point[F0[i][j].Length];
                        for (int l = 0; l < F0[i][j].Length; ++l)
                        {
                            //x,y座標のみ抽出
                            p[l] = new Point(F0[i][j][l][0], F0[i][j][l][1]);
                        }

                        //描画
                        Brush br = Brushes.Bisque;
                        g.FillPolygon(br, p);
                        g.DrawPolygon(Pens.Black, p);
                    }
                }
            }

            for (int r = 0; r < pair.Length; ++r)
            {
                int i = pair[r][0];
                int j = pair[r][1];
                Point[] p = new Point[F[i][j].Length];
                for(int l = 0; l < F[i][j].Length;++l)
                {
                    p[l] = new Point(F[i][j][l][0], F[i][j][l][1]);
                }
                Brush br;
                if (Nz[i][j] < 0) { br = Brushes.Gray; }
                else if (0.7 < Nx[i][j] && Nx[i][j] <= 1) { br = Brushes.LightCyan; }
                else if (0.3 < Nx[i][j] && Nx[i][j] <= 0.7) { br = Brushes.PaleTurquoise; }
                else if (-0.5 < Nx[i][j] && Nx[i][j] <= 0.3) { br = Brushes.Turquoise; }
                else { br = Brushes.DarkCyan; }
                g.FillPolygon(br, p);
                g.DrawPolygon(Pens.Black, p);
            }

            g.Dispose();
            pb.Image = sheet;
        }

        void rd_CheckedChanged(object sender, EventArgs e)
        {
            RadioButton rbutton = sender as RadioButton;
            for (int i = 0; i < Cubes.Length; ++i)
            {
                if (rbutton.Text == Cubes[i])
                {
                    ck = i;
                }
            }
            Smin = 40000;
            Rotate();
            setFocus();
        }

        void setFocus()
        {
            pb.MouseMove -= new MouseEventHandler(pb_MouseMove);
            t1.ValueChanged += new EventHandler(t_ValueChanged);
            t2.ValueChanged += new EventHandler(t_ValueChanged);
            t1.Focus();
            axz0 = axz;
            ayz0 = ayz;
        }

        void t_MouseDown(object sender, MouseEventArgs e)
        {
            pb.MouseMove -= new MouseEventHandler(pb_MouseMove);
            t1.ValueChanged += new EventHandler(t_ValueChanged);
            t2.ValueChanged += new EventHandler(t_ValueChanged);
            axz0 = axz;
            ayz0 = ayz;
        }

        void t_ValueChanged(object sender, EventArgs e)
        {
            axz = t1.Value - 180;
            ayz = t2.Value - 90;

            if (axz > 180) { axz = 180; }
            else if (axz < -180) { axz = -180; }

            if (ayz > 90) { ayz = 90; }
            else if (ayz < -90) { ayz = -90; }

            l1.Text = "Axz: " + axz.ToString();
            l2.Text = "Ayz: " + ayz.ToString();
            rxz = (-1) * axz * Math.PI / 180;
            ryz = ayz * Math.PI / 180;
            if (ck != 99) { Rotate(); } else { Rotate2(); }
        }

        void pb_MouseDown(object sender, MouseEventArgs e)
        {
            t1.ValueChanged -= new EventHandler(t_ValueChanged);
            t2.ValueChanged -= new EventHandler(t_ValueChanged);
            pb.MouseMove += new MouseEventHandler(pb_MouseMove);
            mp = new Point(e.X, e.Y);
            axz0 = axz;
            ayz0 = ayz;
        }

        void pb_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                axz = axz0 + e.X - mp.X;
                ayz = ayz0 + mp.Y - e.Y;

                if (axz >= 180) { axz = 180; }
                else if (axz <= -180) { axz = -180; }

                if (ayz >= 90) { ayz = 90; }
                else if (ayz <= -90) { ayz = -90; }

                t1.Value = axz + 180;
                t2.Value = ayz + 90;
                l1.Text = "Axz: " + axz.ToString();
                l2.Text = "Ayz: " + ayz.ToString();
                rxz = (-1) * axz * Math.PI / 180;
                ryz = ayz * Math.PI / 180;
                if (ck != 99) { Rotate(); } else { Rotate2(); }
            }
        }

        private void Rotate()
        {
            //初期の頂点
            int[] P1 = new int[3];
            int[] P2 = new int[3];
            int[] P3 = new int[3];
            int[] P4 = new int[3];
            int[] P5 = new int[3];
            int[] P6 = new int[3];
            int[] P7 = new int[3];
            int[] P8 = new int[3];
            int[] P9 = new int[3];
            int[] P10 = new int[3];
            //移動後の頂点
            int[] Q1 = new int[3];
            int[] Q2 = new int[3];
            int[] Q3 = new int[3];
            int[] Q4 = new int[3];
            int[] Q5 = new int[3];
            int[] Q6 = new int[3];
            int[] Q7 = new int[3];
            int[] Q8 = new int[3];
            int[] Q9 = new int[3];
            int[] Q10 = new int[3];
            //面
            int[][] F1 = new int[][] { };
            int[][] F2 = new int[][] { };
            int[][] F3 = new int[][] { };
            int[][] F4 = new int[][] { };
            int[][] F5 = new int[][] { };
            int[][] F6 = new int[][] { };
            int[][] F7 = new int[][] { };
            int[][] F8 = new int[][] { };
            int[][] F9 = new int[][] { };
            int[][] F10 = new int[][] { };
            int[][] F11 = new int[][] { };
            int[][] F12 = new int[][] { };

            //点の割り当て
            switch (ck)
            {
                case 0://正四面体
                    P1 = new int[] { 100, 10, 0 };
                    P2 = new int[] { 100, 130, (int)(60 * Math.Sqrt(2)) };
                    P3 = new int[] { 100 + (int)(30 * Math.Sqrt(6)), 130, -(int)(30 * Math.Sqrt(2)) };
                    P4 = new int[] { 100 - (int)(30 * Math.Sqrt(6)), 130, -(int)(30 * Math.Sqrt(2)) };
                    break;
                case 1://立方体
                    P1 = new int[] { 150, 150, 50 };
                    P2 = new int[] { 50, 150, 50 };
                    P3 = new int[] { 50, 50, 50 };
                    P4 = new int[] { 150, 50, 50 };
                    P5 = new int[] { 150, 150, -50 };
                    P6 = new int[] { 50, 150, -50 };
                    P7 = new int[] { 50, 50, -50 };
                    P8 = new int[] { 150, 50, -50 };
                    break;
                case 2://正八面体
                    P1 = new int[] { 190, 100, 0 };
                    P2 = new int[] { 100, 190, 0 };
                    P3 = new int[] { 10, 100, 0 };
                    P4 = new int[] { 100, 10, 0 };
                    P5 = new int[] { 100, 100, 90 };
                    P6 = new int[] { 100, 100, -90 };
                    break;
                case 3://立方体カット
                    P1 = new int[] { 160, 40, -60 };
                    P2 = new int[] { 160, 40, 20 };
                    P3 = new int[] { 80, 40, -60 };
                    P4 = new int[] { 160, 70, 60 };
                    P5 = new int[] { 40, 160, 60 };
                    P6 = new int[] { 40, 70, -60 };
                    P7 = new int[] { 160, 160, -60 };
                    P8 = new int[] { 160, 160, 60 };
                    P9 = new int[] { 40, 160, -60 };
                    break;
                case 4://六角錐
                    P1 = new int[] { 100, 20, 0 };
                    P2 = new int[] { 100, 160, -60 };
                    P3 = new int[] { 100 - (int)(30 * Math.Sqrt(3)), 160, -30 };
                    P4 = new int[] { 100 - (int)(30 * Math.Sqrt(3)), 160, 30 };
                    P5 = new int[] { 100, 160, 60 };
                    P6 = new int[] { 100 + (int)(30 * Math.Sqrt(3)), 160, 30 };
                    P7 = new int[] { 100 + (int)(30 * Math.Sqrt(3)), 160, -30 };
                    break;
                case 5://複合
                    P1 = new int[] { 100, 10, 0 };
                    P2 = new int[] { 100, 130, (int)(60 * Math.Sqrt(2)) };
                    P3 = new int[] { 100 + (int)(30 * Math.Sqrt(6)), 130, -(int)(30 * Math.Sqrt(2)) };
                    P4 = new int[] { 100 - (int)(30 * Math.Sqrt(6)), 130, -(int)(30 * Math.Sqrt(2)) };
                    P5 = new int[] { 100, 190, 0 };
                    P6 = new int[] { 100, 70, -(int)(60 * Math.Sqrt(2)) };
                    P7 = new int[] { 100 + (int)(30 * Math.Sqrt(6)), 70, (int)(30 * Math.Sqrt(2)) };
                    P8 = new int[] { 100 - (int)(30 * Math.Sqrt(6)), 70, (int)(30 * Math.Sqrt(2)) };
                    break;
                default:
                    P1 = new int[] { 190, 100, 0 };
                    P2 = new int[] { 100, 190, 0 };
                    P3 = new int[] { 10, 100, 0 };
                    P4 = new int[] { 100, 10, 0 };
                    P5 = new int[] { 100, 100, 90 };
                    P6 = new int[] { 100, 100, -90 };
                    break;
            }

            int[][] Ps = { P1, P2, P3, P4, P5, P6, P7, P8, P9, P10 };
            int[][] Qs = { Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10 };
            double temp0, temp1, temp2;

            //変換後の頂点の座標
            for (int i = 0; i < Ps.Length; ++i)
            {
                if (Ps[i].Length > 0)
                {
                    //y軸のまわりに回転
                    temp0 = (Ps[i][0] - 100) * Math.Cos(rxz) - Ps[i][2] * Math.Sin(rxz) + 100;
                    temp1 = Ps[i][1];
                    temp2 = (Ps[i][0] - 100) * Math.Sin(rxz) + Ps[i][2] * Math.Cos(rxz);

                    //x軸のまわりに回転
                    Qs[i][0] = (int)temp0;
                    Qs[i][1] = (int)((temp1 - 100) * Math.Cos(ryz) - temp2 * Math.Sin(ryz)) + 100;
                    Qs[i][2] = (int)((temp1 - 100) * Math.Sin(ryz) + temp2 * Math.Cos(ryz));
                }
            }

            //面の割り当て
            switch (ck)
            {
                case 0://正四面体
                    F1 = new int[][] { Q1, Q2, Q3 };
                    F2 = new int[][] { Q1, Q3, Q4 };
                    F3 = new int[][] { Q1, Q4, Q2 };
                    F4 = new int[][] { Q2, Q4, Q3 };
                    break;
                case 1://立方体
                    F1 = new int[][] { Q1, Q4, Q3, Q2 };
                    F2 = new int[][] { Q5, Q6, Q7, Q8 };
                    F3 = new int[][] { Q1, Q2, Q6, Q5 };
                    F4 = new int[][] { Q2, Q3, Q7, Q6 };
                    F5 = new int[][] { Q3, Q4, Q8, Q7 };
                    F6 = new int[][] { Q4, Q1, Q5, Q8 };
                    break;
                case 2://正八面体
                    F1 = new int[][] { Q5, Q2, Q1 };
                    F2 = new int[][] { Q5, Q3, Q2 };
                    F3 = new int[][] { Q5, Q4, Q3 };
                    F4 = new int[][] { Q5, Q1, Q4 };
                    F5 = new int[][] { Q6, Q1, Q2 };
                    F6 = new int[][] { Q6, Q2, Q3 };
                    F7 = new int[][] { Q6, Q3, Q4 };
                    F8 = new int[][] { Q6, Q4, Q1 };
                    break;
                case 3://立方体カット
                    F1 = new int[][] { Q1, Q3, Q2 };
                    F2 = new int[][] { Q2, Q3, Q6, Q5, Q4 };
                    F3 = new int[][] { Q1, Q2, Q4, Q8, Q7 };
                    F4 = new int[][] { Q1, Q7, Q9, Q6, Q3 };
                    F5 = new int[][] { Q4, Q5, Q8 };
                    F6 = new int[][] { Q6, Q9, Q5 };
                    F7 = new int[][] { Q5, Q9, Q7, Q8 };
                    break;
                case 4://六角錐
                    F1 = new int[][] { Q1, Q2, Q3 };
                    F2 = new int[][] { Q1, Q3, Q4 };
                    F3 = new int[][] { Q1, Q4, Q5 };
                    F4 = new int[][] { Q1, Q5, Q6 };
                    F5 = new int[][] { Q1, Q6, Q7 };
                    F6 = new int[][] { Q1, Q7, Q2 };
                    F7 = new int[][] { Q2, Q7, Q6, Q5, Q4, Q3 };
                    break;
                case 5://複合
                    F1 = new int[][] { Q6, Q4, Q1 };
                    F2 = new int[][] { Q6, Q3, Q4 };
                    F3 = new int[][] { Q6, Q1, Q3 };
                    F4 = new int[][] { Q8, Q4, Q2 };
                    F5 = new int[][] { Q8, Q2, Q1 };
                    F6 = new int[][] { Q8, Q1, Q4 };
                    F7 = new int[][] { Q5, Q2, Q4 };
                    F8 = new int[][] { Q5, Q4, Q3 };
                    F9 = new int[][] { Q5, Q3, Q2 };
                    F10 = new int[][] { Q7, Q1, Q2 };
                    F11 = new int[][] { Q7, Q2, Q3 };
                    F12 = new int[][] { Q7, Q3, Q1 };
                    break;
                default:
                    F1 = new int[][] { Q5, Q2, Q1 };
                    F2 = new int[][] { Q5, Q3, Q2 };
                    F3 = new int[][] { Q5, Q4, Q3 };
                    F4 = new int[][] { Q5, Q1, Q4 };
                    F5 = new int[][] { Q6, Q1, Q2 };
                    F6 = new int[][] { Q6, Q2, Q3 };
                    F7 = new int[][] { Q6, Q3, Q4 };
                    F8 = new int[][] { Q6, Q4, Q1 };
                    break;
            }

            int[][][] Fs = { F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12 };

            //法線ベクトル
            double[] Nx = new double[Fs.Length];
            int[] Nz = new int[Fs.Length];
            for (int i = 0; i < Fs.Length; ++i)
            {
                int[] vec1 = new int[3];
                int[] vec2 = new int[3];
                int[] cr = new int[3];

                if (Fs[i].Length > 0)
                {
                    for (int j = 0; j < 3; ++j)
                    {
                        vec1[j] = Fs[i][1][j] - Fs[i][0][j];
                        vec2[j] = Fs[i][2][j] - Fs[i][1][j];
                    }
                    for (int j = 0; j < 3; ++j)
                    {
                        cr[j] = vec2[(j + 1) % 3] * vec1[(j + 2) % 3] - vec2[(j + 2) % 3] * vec1[(j + 1) % 3];
                    }
                    //法線ベクトルの大きさ
                    double len = Math.Sqrt(cr[0] * cr[0] + cr[1] * cr[1] + cr[2] * cr[2]);
                    //x成分を規格化
                    Nx[i] = cr[0] / len;
                    Nz[i] = cr[2];
                }
            }

            Bitmap sheet = new Bitmap(pb.Width, pb.Height);
            Graphics g = Graphics.FromImage(sheet);
            double S = 0;
            for (int i = 0; i < Fs.Length; ++i)
            {
                //法線ベクトルが手前に向いている面のみ処理
                if (Fs[i].Length > 0 && Nz[i] >= 0)
                {
                    Point[] P = new Point[Fs[i].Length];
                    for (int j = 0; j < Fs[i].Length; ++j)
                    {
                        //x,y座標のみ抽出
                        P[j] = new Point(Fs[i][j][0], Fs[i][j][1]);
                        //面積
                        S += Fs[i][j][0] * Fs[i][(j + 1) % Fs[i].Length][1]
                            - Fs[i][(j + 1) % Fs[i].Length][0] * Fs[i][j][1];
                    }

                    //描画
                    Brush br;
                    if (0.7 < Nx[i] && Nx[i] <= 1) { br = Brushes.LightCyan; }
                    else if (0.3 < Nx[i] && Nx[i] <= 0.7) { br = Brushes.PaleTurquoise; }
                    else if (-0.5 < Nx[i] && Nx[i] <= 0.3) { br = Brushes.Turquoise; }
                    else { br = Brushes.DarkCyan; }
                    g.FillPolygon(br, P);
                    g.DrawPolygon(Pens.Black, P);
                }
            }
            g.Dispose();
            pb.Image = sheet;
            S = Math.Abs(S) * 0.5;
            if (S <= Smin) { Smin = S; axzm = axz; ayzm = ayz; }
            l3.Text = "S: " + S.ToString("0.") + "\nSmin: " + Smin.ToString("0.")
                + "\nAxzm: " + axzm.ToString() + "\nAyzm: " + ayzm.ToString();
        }

    }
}

 

 

2012年12月27日 (木)

C#:立体を回転させて表示/その4

今回は、立体の正射影の面積を計算して自動表示させてみた。

  1. 立体を転がすたびにそのときの正射影の面積を表示
  2. それまでの面積の最小値を記憶しておき、その面積と回転角も表示
  3. ついでに初期表示でトラックバーにフォーカスし、すぐにカーソルキーで回転させられるようにした
  4. これまでラベルがAyzとするところをAxyと間違えて表示していたので修正した

面積は公式 (1/2) * |Σ(x[i]y[i+1] - x[i+1]y[i])| でシンプルに計算。

どんなときに正射影の面積が最小になるかわかって面白い。

以下は正四面体の例。

Sが面積、Sminがこれまでの最小値、Axzm、Ayzmはそれぞれ最小面積のときの回転角。

Cubic31_2

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

namespace WindowsFormsApplication1
{
   
    public class Form1 : Form//Designer.cs 削除
    {
        private System.ComponentModel.IContainer components = null;
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        PictureBox pb = new PictureBox();
        TrackBar t1 = new TrackBar();
        TrackBar t2 = new TrackBar();
        Label l1 = new Label();
        Label l2 = new Label();
        Label l3 = new Label();
        double rxz = 0;
        double ryz = 0;
        int axz = 0;
        int ayz = 0;
        int axz0 = 0;
        int ayz0 = 0;
        Point mp;
        int ck = 0;
        string[] Cubes = { "正四面体", "立方体", "正八面体", "立方体カット", "六角錐", "複合" };
        double Smin = 40000;
        int axzm = 0;
        int ayzm = 0;

        public Form1()
        {
            this.components = new System.ComponentModel.Container();
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.Text = "Cubic";
            this.ClientSize = new Size(300, 300);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;
            this.Controls.Add(pb);
            pb.SetBounds(0, 0, 200, 200);
            pb.BackColor = Color.White;
            this.Controls.Add(t1);
            t1.AutoSize = false;
            t1.SetBounds(0, 200, 200, 20);
            t1.TickStyle = TickStyle.None;
            t1.Minimum = 0;
            t1.Maximum = 360;
            t1.Value = axz + 180;
            this.Controls.Add(t2);
            t2.AutoSize = false;
            t2.Orientation = Orientation.Vertical;
            t2.SetBounds(200, 0, 20, 200);
            t2.TickStyle = TickStyle.None;
            t2.Minimum = 0;
            t2.Maximum = 180;
            t2.Value = ayz + 90;
            this.Controls.Add(l1);
            this.Controls.Add(l2);
            this.Controls.Add(l3);
            l1.SetBounds(70, 220, 60, 20);
            l1.TextAlign = ContentAlignment.MiddleCenter;
            l1.Text = "Axz: " + axz.ToString();
            l2.SetBounds(220, 90, 60, 20);
            l2.TextAlign = ContentAlignment.MiddleCenter;
            l2.Text = "Ayz: " + ayz.ToString();
            l3.SetBounds(220, 140, 80, 60);
            l3.TextAlign = ContentAlignment.TopLeft;
            
            pb.MouseDown += new MouseEventHandler(pb_MouseDown);
            pb.MouseMove += new MouseEventHandler(pb_MouseMove);

            t1.MouseDown += new MouseEventHandler(t_MouseDown);
            t2.MouseDown += new MouseEventHandler(t_MouseDown);
            t1.ValueChanged += new EventHandler(t_ValueChanged);
            t2.ValueChanged += new EventHandler(t_ValueChanged);

            GroupBox gb = new GroupBox();
            gb.SetBounds(10, 240, 280, 55);
            this.Controls.Add(gb);

            RadioButton[] rd = new RadioButton[Cubes.Length];

            for (int i = 0; i < Cubes.Length; ++i)
            {
                rd[i] = new RadioButton();
                if (i < 3)
                {
                    rd[i].SetBounds(10 + i * 90, 10, 85, 20);
                }
                else
                {
                    rd[i].SetBounds(10 + (i-3) * 90, 30, 85, 20);
                }
                rd[i].Text = Cubes[i];
                rd[i].CheckedChanged += new EventHandler(rd_CheckedChanged);

            }
            rd[0].Checked = true;
            gb.Controls.AddRange(rd);
            Rotate();
            setFocus();
        }

        void rd_CheckedChanged(object sender, EventArgs e)
        {
            RadioButton rbutton = sender as RadioButton;
            for (int i = 0; i < Cubes.Length; ++i)
            {
                if (rbutton.Text == Cubes[i])
                {
                    ck = i;
                }
            }
            Smin = 40000;
            Rotate();
            setFocus();
        }

        void setFocus()
        {
            pb.MouseMove -= new MouseEventHandler(pb_MouseMove);
            t1.ValueChanged += new EventHandler(t_ValueChanged);
            t2.ValueChanged += new EventHandler(t_ValueChanged);
            t1.Focus();
            axz0 = axz;
            ayz0 = ayz;
        }

        void t_MouseDown(object sender, MouseEventArgs e)
        {
            pb.MouseMove -= new MouseEventHandler(pb_MouseMove);
            t1.ValueChanged += new EventHandler(t_ValueChanged);
            t2.ValueChanged += new EventHandler(t_ValueChanged);
            axz0 = axz;
            ayz0 = ayz;
        }

        void t_ValueChanged(object sender, EventArgs e)
        {
            axz = t1.Value - 180;
            ayz = t2.Value - 90;

            if (axz > 180) { axz = 180; }
            else if (axz < -180) { axz = -180; }

            if (ayz > 90) { ayz = 90; }
            else if (ayz < -90) { ayz = -90; }

            l1.Text = "Axz: " + axz.ToString();
            l2.Text = "Ayz: " + ayz.ToString();
            rxz = (-1) * axz * Math.PI / 180;
            ryz = ayz * Math.PI / 180;
            Rotate();
        }

        void pb_MouseDown(object sender, MouseEventArgs e)
        {
            t1.ValueChanged -= new EventHandler(t_ValueChanged);
            t2.ValueChanged -= new EventHandler(t_ValueChanged);
            pb.MouseMove += new MouseEventHandler(pb_MouseMove);
            mp = new Point(e.X, e.Y);
            axz0 = axz;
            ayz0 = ayz;
        }

        void pb_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                axz = axz0 + e.X - mp.X;
                ayz = ayz0 + mp.Y - e.Y;
               
                if (axz >= 180) { axz = 180; }
                else if (axz <= -180) { axz = -180; }

                if (ayz >= 90) { ayz = 90; }
                else if (ayz <= -90) { ayz = -90; }

                t1.Value = axz + 180;
                t2.Value = ayz + 90;
                l1.Text = "Axz: " + axz.ToString();
                l2.Text = "Ayz: " + ayz.ToString();
                rxz = (-1) * axz * Math.PI / 180;
                ryz = ayz * Math.PI / 180;
                Rotate();
            }
        }

        private void Rotate()
        {
            //初期の頂点
            int[] P1 = new int[3];
            int[] P2 = new int[3];
            int[] P3 = new int[3];
            int[] P4 = new int[3];
            int[] P5 = new int[3];
            int[] P6 = new int[3];
            int[] P7 = new int[3];
            int[] P8 = new int[3];
            int[] P9 = new int[3];
            int[] P10 = new int[3];
            //移動後の頂点
            int[] Q1 = new int[3];
            int[] Q2 = new int[3];
            int[] Q3 = new int[3];
            int[] Q4 = new int[3];
            int[] Q5 = new int[3];
            int[] Q6 = new int[3];
            int[] Q7 = new int[3];
            int[] Q8 = new int[3];
            int[] Q9 = new int[3];
            int[] Q10 = new int[3];
            //面
            int[][] F1 = new int[][] { };
            int[][] F2 = new int[][] { };
            int[][] F3 = new int[][] { };
            int[][] F4 = new int[][] { };
            int[][] F5 = new int[][] { };
            int[][] F6 = new int[][] { };
            int[][] F7 = new int[][] { };
            int[][] F8 = new int[][] { };
            int[][] F9 = new int[][] { };
            int[][] F10 = new int[][] { };
            int[][] F11 = new int[][] { };
            int[][] F12 = new int[][] { };

            //点の割り当て
            switch (ck)
            {
                case 0://正四面体
                    P1 = new int[] { 100, 10, 0 };
                    P2 = new int[] { 100, 130, (int)(60*Math.Sqrt(2)) };
                    P3 = new int[] { 100 + (int)(30*Math.Sqrt(6)), 130, -(int)(30 * Math.Sqrt(2)) };
                    P4 = new int[] { 100 - (int)(30*Math.Sqrt(6)), 130, -(int)(30 * Math.Sqrt(2)) };
                    break;
                case 1://立方体
                    P1 = new int[] { 150, 150, 50 };
                    P2 = new int[] { 50, 150, 50 };
                    P3 = new int[] { 50, 50, 50 };
                    P4 = new int[] { 150, 50, 50 };
                    P5 = new int[] { 150, 150, -50 };
                    P6 = new int[] { 50, 150, -50 };
                    P7 = new int[] { 50, 50, -50 };
                    P8 = new int[] { 150, 50, -50 };
                    break;
                case 2://正八面体
                    P1 = new int[] { 190, 100, 0 };
                    P2 = new int[] { 100, 190, 0 };
                    P3 = new int[] { 10, 100, 0 };
                    P4 = new int[] { 100, 10, 0 };
                    P5 = new int[] { 100, 100, 90 };
                    P6 = new int[] { 100, 100, -90 };
                    break;
                case 3://立方体カット
                    P1 = new int[] { 160, 40, -60 };
                    P2 = new int[] { 160, 40, 20 };
                    P3 = new int[] { 80, 40, -60 };
                    P4 = new int[] { 160, 70, 60 };
                    P5 = new int[] { 40, 160, 60 };
                    P6 = new int[] { 40, 70, -60 };
                    P7 = new int[] { 160, 160, -60 };
                    P8 = new int[] { 160, 160, 60 };
                    P9 = new int[] { 40, 160, -60 };
                    break;
                case 4://六角錐
                    P1 = new int[] { 100, 20, 0 };
                    P2 = new int[] { 100, 160, -60 };
                    P3 = new int[] { 100-(int)(30*Math.Sqrt(3)), 160, -30 };
                    P4 = new int[] { 100-(int)(30 * Math.Sqrt(3)), 160, 30 };
                    P5 = new int[] { 100, 160, 60 };
                    P6 = new int[] { 100+(int)(30*Math.Sqrt(3)), 160, 30 };
                    P7 = new int[] { 100+(int)(30 * Math.Sqrt(3)), 160, -30 };
                    break;
                case 5://複合
                    P1 = new int[] { 100, 10, 0 };
                    P2 = new int[] { 100, 130, (int)(60*Math.Sqrt(2)) };
                    P3 = new int[] { 100 + (int)(30*Math.Sqrt(6)), 130, -(int)(30 * Math.Sqrt(2)) };
                    P4 = new int[] { 100 - (int)(30*Math.Sqrt(6)), 130, -(int)(30 * Math.Sqrt(2)) };
                    P5 = new int[] { 100, 190, 0 };
                    P6 = new int[] { 100, 70, -(int)(60*Math.Sqrt(2)) };
                    P7 = new int[] { 100 + (int)(30*Math.Sqrt(6)), 70, (int)(30 * Math.Sqrt(2)) };
                    P8 = new int[] { 100 - (int)(30*Math.Sqrt(6)), 70, (int)(30 * Math.Sqrt(2)) };
                    break;
                default:
                    P1 = new int[] { 190, 100, 0 };
                    P2 = new int[] { 100, 190, 0 };
                    P3 = new int[] { 10, 100, 0 };
                    P4 = new int[] { 100, 10, 0 };
                    P5 = new int[] { 100, 100, 90 };
                    P6 = new int[] { 100, 100, -90 };
                    break;
            }

            int[][] Ps = { P1, P2, P3, P4, P5, P6, P7, P8, P9, P10 };
            int[][] Qs = { Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10 };
            double temp0, temp1, temp2;

            //変換後の頂点の座標
            for (int i = 0; i < Ps.Length; ++i)
            {
                if (Ps[i].Length > 0)
                {
                    //y軸のまわりに回転
                    temp0 = (Ps[i][0] - 100) * Math.Cos(rxz) - Ps[i][2] * Math.Sin(rxz) + 100;
                    temp1 = Ps[i][1];
                    temp2 = (Ps[i][0] - 100) * Math.Sin(rxz) + Ps[i][2] * Math.Cos(rxz);

                    //x軸のまわりに回転
                    Qs[i][0] = (int)temp0;
                    Qs[i][1] = (int)((temp1 - 100) * Math.Cos(ryz) - temp2 * Math.Sin(ryz)) + 100;
                    Qs[i][2] = (int)((temp1 - 100) * Math.Sin(ryz) + temp2 * Math.Cos(ryz));
                }
            }

            //面の割り当て
            switch (ck)
            {
                case 0://正四面体
                    F1 = new int[][] { Q1, Q2, Q3 };
                    F2 = new int[][] { Q1, Q3, Q4 };
                    F3 = new int[][] { Q1, Q4, Q2 };
                    F4 = new int[][] { Q2, Q4, Q3 };
                    break;
                case 1://立方体
                    F1 = new int[][] { Q1, Q4, Q3, Q2 };
                    F2 = new int[][] { Q5, Q6, Q7, Q8 };
                    F3 = new int[][] { Q1, Q2, Q6, Q5 };
                    F4 = new int[][] { Q2, Q3, Q7, Q6 };
                    F5 = new int[][] { Q3, Q4, Q8, Q7 };
                    F6 = new int[][] { Q4, Q1, Q5, Q8 };
                    break;
                case 2://正八面体
                    F1 = new int[][] { Q5, Q2, Q1 };
                    F2 = new int[][] { Q5, Q3, Q2 };
                    F3 = new int[][] { Q5, Q4, Q3 };
                    F4 = new int[][] { Q5, Q1, Q4 };
                    F5 = new int[][] { Q6, Q1, Q2 };
                    F6 = new int[][] { Q6, Q2, Q3 };
                    F7 = new int[][] { Q6, Q3, Q4 };
                    F8 = new int[][] { Q6, Q4, Q1 };
                    break;
                case 3://立方体カット
                    F1 = new int[][] { Q1, Q3, Q2 };
                    F2 = new int[][] { Q2, Q3, Q6, Q5, Q4 };
                    F3 = new int[][] { Q1, Q2, Q4, Q8, Q7 };
                    F4 = new int[][] { Q1, Q7, Q9, Q6, Q3 };
                    F5 = new int[][] { Q4, Q5, Q8 };
                    F6 = new int[][] { Q6, Q9, Q5 };
                    F7 = new int[][] { Q5, Q9, Q7, Q8 };
                    break;
                case 4://六角錐
                    F1 = new int[][] { Q1, Q2, Q3 };
                    F2 = new int[][] { Q1, Q3, Q4 };
                    F3 = new int[][] { Q1, Q4, Q5 };
                    F4 = new int[][] { Q1, Q5, Q6 };
                    F5 = new int[][] { Q1, Q6, Q7 };
                    F6 = new int[][] { Q1, Q7, Q2 };
                    F7 = new int[][] { Q2, Q7, Q6, Q5, Q4, Q3 };
                    break;
                case 5://複合
                    F1 = new int[][] { Q6, Q4, Q1 };
                    F2 = new int[][] { Q6, Q3, Q4 };
                    F3 = new int[][] { Q6, Q1, Q3 };
                    F4 = new int[][] { Q8, Q4, Q2 };
                    F5 = new int[][] { Q8, Q2, Q1 };
                    F6 = new int[][] { Q8, Q1, Q4 };
                    F7 = new int[][] { Q5, Q2, Q4 };
                    F8 = new int[][] { Q5, Q4, Q3 };
                    F9 = new int[][] { Q5, Q3, Q2 };
                    F10 = new int[][] { Q7, Q1, Q2 };
                    F11 = new int[][] { Q7, Q2, Q3 };
                    F12 = new int[][] { Q7, Q3, Q1 };
                    break;
                default:
                    F1 = new int[][] { Q5, Q2, Q1 };
                    F2 = new int[][] { Q5, Q3, Q2 };
                    F3 = new int[][] { Q5, Q4, Q3 };
                    F4 = new int[][] { Q5, Q1, Q4 };
                    F5 = new int[][] { Q6, Q1, Q2 };
                    F6 = new int[][] { Q6, Q2, Q3 };
                    F7 = new int[][] { Q6, Q3, Q4 };
                    F8 = new int[][] { Q6, Q4, Q1 };
                    break;
            }
                        
            int[][][] Fs = { F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12 };

            //法線ベクトル
            double[] Nx = new double[Fs.Length];
            int[] Nz = new int[Fs.Length];
            for (int i = 0; i < Fs.Length; ++i)
            {
                int[] vec1 = new int[3];
                int[] vec2 = new int[3];
                int[] cr = new int[3];
               
                if (Fs[i].Length > 0)
                {
                    for (int j = 0; j < 3;++j)
                    {
                        vec1[j] = Fs[i][1][j] - Fs[i][0][j];
                        vec2[j] = Fs[i][2][j] - Fs[i][1][j];
                    }
                    for (int j = 0; j < 3; ++j)
                    {
                        cr[j] = vec2[(j + 1) % 3] * vec1[(j + 2) % 3] - vec2[(j + 2) % 3] * vec1[(j + 1) % 3];
                    }
                    //法線ベクトルの大きさ
                    double len = Math.Sqrt(cr[0] * cr[0] + cr[1] * cr[1] + cr[2] * cr[2]);
                    //x成分を規格化
                    Nx[i] = cr[0] / len;
                    Nz[i] = cr[2];
                }
            }
            
            Bitmap sheet = new Bitmap(pb.Width, pb.Height);
            Graphics g = Graphics.FromImage(sheet);
            double S = 0;
            for (int i = 0; i < Fs.Length; ++i)
            {
                //法線ベクトルが手前に向いている面のみ処理
                if (Fs[i].Length > 0 && Nz[i] >= 0)
                {
                    Point[] P = new Point[Fs[i].Length];
                    for (int j = 0; j < Fs[i].Length; ++j)
                    {
                        //x,y座標のみ抽出
                        P[j] = new Point(Fs[i][j][0], Fs[i][j][1]);
                        //面積
                        S += Fs[i][j][0] * Fs[i][(j + 1) % Fs[i].Length][1]
                            - Fs[i][(j + 1) % Fs[i].Length][0] * Fs[i][j][1];
                    }
                   
                    //描画
                    Brush br;
                    if (0.7 < Nx[i] && Nx[i] <= 1) { br = Brushes.LightCyan; }
                    else if (0.3 < Nx[i] && Nx[i] <= 0.7) { br = Brushes.PaleTurquoise; }
                    else if (-0.5 < Nx[i] && Nx[i] <= 0.3) { br = Brushes.Turquoise; }
                    else { br = Brushes.DarkCyan; }
                    g.FillPolygon(br, P);
                    g.DrawPolygon(Pens.Black, P);
                }
            }
            g.Dispose();
            pb.Image = sheet;
            S = Math.Abs(S) * 0.5;
            if (S <= Smin) { Smin = S; axzm = axz; ayzm = ayz; }
            l3.Text = "S: " + S.ToString("0.") + "\nSmin: " + Smin.ToString("0.")
                + "\nAxzm: " + axzm.ToString() + "\nAyzm: " + ayzm.ToString();
        }

    }
}

 

 

2012年12月23日 (日)

C#:立体を回転させて表示/その3

光が当たる効果を加えた。

光は右(xの正方向)からやってくるとし、法線ベクトルのx成分で光量を4段階に判定した。

Cube21

Cube22

Cube23

Cube24

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

namespace WindowsFormsApplication1
{
   
    public class Form1 : Form//Designer.cs 削除
    {
        private System.ComponentModel.IContainer components = null;
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        PictureBox pb = new PictureBox();
        TrackBar t1 = new TrackBar();
        TrackBar t2 = new TrackBar();
        Label l1 = new Label();
        Label l2 = new Label();
        double rxz = 0;
        double ryz = 0;
        int axz = 0;
        int ayz = 0;
        int axz0 = 0;
        int ayz0 = 0;
        Point mp;
        Point tp;
        int ck = 0;
        string[] Cubes = { "正四面体", "立方体", "正八面体", "立方体カット", "六角錐", "複合" };

        public Form1()
        {
            this.components = new System.ComponentModel.Container();
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.Text = "Cubic";
            this.ClientSize = new Size(300, 300);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;
            this.Controls.Add(pb);
            pb.SetBounds(0, 0, 200, 200);
            pb.BackColor = Color.White;
            this.Controls.Add(t1);
            t1.AutoSize = false;
            t1.SetBounds(0, 200, 200, 20);
            t1.TickStyle = TickStyle.None;
            t1.Minimum = 0;
            t1.Maximum = 360;
            t1.Value = axz + 180;
            this.Controls.Add(t2);
            t2.AutoSize = false;
            t2.Orientation = Orientation.Vertical;
            t2.SetBounds(200, 0, 20, 200);
            t2.TickStyle = TickStyle.None;
            t2.Minimum = 0;
            t2.Maximum = 180;
            t2.Value = ayz + 90;
            this.Controls.Add(l1);
            this.Controls.Add(l2);
            l1.SetBounds(70, 220, 60, 20);
            l1.TextAlign = ContentAlignment.MiddleCenter;
            l1.Text = "Axz: " + axz.ToString();
            l2.SetBounds(220, 90, 60, 20);
            l2.TextAlign = ContentAlignment.MiddleCenter;
            l2.Text = "Ayz: " + ayz.ToString();
            
            pb.MouseDown += new MouseEventHandler(pb_MouseDown);
            pb.MouseMove += new MouseEventHandler(pb_MouseMove);

            t1.MouseDown += new MouseEventHandler(t_MouseDown);
            t2.MouseDown += new MouseEventHandler(t_MouseDown);
            t1.ValueChanged += new EventHandler(t_ValueChanged);
            t2.ValueChanged += new EventHandler(t_ValueChanged);

            GroupBox gb = new GroupBox();
            gb.SetBounds(10, 240, 280, 55);
            this.Controls.Add(gb);

            RadioButton[] rd = new RadioButton[Cubes.Length];

            for (int i = 0; i < Cubes.Length; ++i)
            {
                rd[i] = new RadioButton();
                if (i < 3)
                {
                    rd[i].SetBounds(10 + i * 90, 10, 85, 20);
                }
                else
                {
                    rd[i].SetBounds(10 + (i-3) * 90, 30, 85, 20);
                }
                rd[i].Text = Cubes[i];
                rd[i].CheckedChanged += new EventHandler(rd_CheckedChanged);

            }
            rd[0].Checked = true;
            gb.Controls.AddRange(rd);
            Rotate();
        }

        void rd_CheckedChanged(object sender, EventArgs e)
        {
            RadioButton rbutton = sender as RadioButton;
            for (int i = 0; i < Cubes.Length; ++i)
            {
                if (rbutton.Text == Cubes[i])
                {
                    ck = i;
                }
            }

            Rotate();
        }

        void t_MouseDown(object sender, MouseEventArgs e)
        {
            pb.MouseMove -= new MouseEventHandler(pb_MouseMove);
            t1.ValueChanged += new EventHandler(t_ValueChanged);
            t2.ValueChanged += new EventHandler(t_ValueChanged);
            tp = new Point(t1.Value, t2.Value);
            axz0 = axz;
            ayz0 = ayz;
        }

        void t_ValueChanged(object sender, EventArgs e)
        {
            axz = t1.Value - 180;
            ayz = t2.Value - 90;

            if (axz > 180) { axz = 180; }
            else if (axz < -180) { axz = -180; }

            if (ayz > 90) { ayz = 90; }
            else if (ayz < -90) { ayz = -90; }

            l1.Text = "Axz: " + axz.ToString();
            l2.Text = "Axz: " + ayz.ToString();
            rxz = (-1) * axz * Math.PI / 180;
            ryz = ayz * Math.PI / 180;
            Rotate();
        }

        void pb_MouseDown(object sender, MouseEventArgs e)
        {
            t1.ValueChanged -= new EventHandler(t_ValueChanged);
            t2.ValueChanged -= new EventHandler(t_ValueChanged);
            pb.MouseMove += new MouseEventHandler(pb_MouseMove);
            mp = new Point(e.X, e.Y);
            axz0 = axz;
            ayz0 = ayz;
        }

        void pb_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                axz = axz0 + e.X - mp.X;
                ayz = ayz0 + mp.Y - e.Y;
               
                if (axz >= 180) { axz = 180; }
                else if (axz <= -180) { axz = -180; }

                if (ayz >= 90) { ayz = 90; }
                else if (ayz <= -90) { ayz = -90; }

                t1.Value = axz + 180;
                t2.Value = ayz + 90;
                l1.Text = "Axz: " + axz.ToString();
                l2.Text = "Ayz: " + ayz.ToString();
                rxz = (-1) * axz * Math.PI / 180;
                ryz = ayz * Math.PI / 180;
                Rotate();
            }
        }

        private void Rotate()
        {
            //初期の頂点
            int[] P1 = new int[3];
            int[] P2 = new int[3];
            int[] P3 = new int[3];
            int[] P4 = new int[3];
            int[] P5 = new int[3];
            int[] P6 = new int[3];
            int[] P7 = new int[3];
            int[] P8 = new int[3];
            int[] P9 = new int[3];
            int[] P10 = new int[3];
            //移動後の頂点
            int[] Q1 = new int[3];
            int[] Q2 = new int[3];
            int[] Q3 = new int[3];
            int[] Q4 = new int[3];
            int[] Q5 = new int[3];
            int[] Q6 = new int[3];
            int[] Q7 = new int[3];
            int[] Q8 = new int[3];
            int[] Q9 = new int[3];
            int[] Q10 = new int[3];
            //面
            int[][] F1 = new int[][] { };
            int[][] F2 = new int[][] { };
            int[][] F3 = new int[][] { };
            int[][] F4 = new int[][] { };
            int[][] F5 = new int[][] { };
            int[][] F6 = new int[][] { };
            int[][] F7 = new int[][] { };
            int[][] F8 = new int[][] { };
            int[][] F9 = new int[][] { };
            int[][] F10 = new int[][] { };
            int[][] F11 = new int[][] { };
            int[][] F12 = new int[][] { };

            //点の割り当て
            switch (ck)
            {
                case 0://正四面体
                    P1 = new int[] { 100, 10, 0 };
                    P2 = new int[] { 100, 130, (int)(60*Math.Sqrt(2)) };
                    P3 = new int[] { 100 + (int)(30*Math.Sqrt(6)), 130, -(int)(30 * Math.Sqrt(2)) };
                    P4 = new int[] { 100 - (int)(30*Math.Sqrt(6)), 130, -(int)(30 * Math.Sqrt(2)) };
                    break;
                case 1://立方体
                    P1 = new int[] { 150, 150, 50 };
                    P2 = new int[] { 50, 150, 50 };
                    P3 = new int[] { 50, 50, 50 };
                    P4 = new int[] { 150, 50, 50 };
                    P5 = new int[] { 150, 150, -50 };
                    P6 = new int[] { 50, 150, -50 };
                    P7 = new int[] { 50, 50, -50 };
                    P8 = new int[] { 150, 50, -50 };
                    break;
                case 2://正八面体
                    P1 = new int[] { 190, 100, 0 };
                    P2 = new int[] { 100, 190, 0 };
                    P3 = new int[] { 10, 100, 0 };
                    P4 = new int[] { 100, 10, 0 };
                    P5 = new int[] { 100, 100, 90 };
                    P6 = new int[] { 100, 100, -90 };
                    break;
                case 3://立方体カット
                    P1 = new int[] { 160, 40, -60 };
                    P2 = new int[] { 160, 40, 20 };
                    P3 = new int[] { 80, 40, -60 };
                    P4 = new int[] { 160, 70, 60 };
                    P5 = new int[] { 40, 160, 60 };
                    P6 = new int[] { 40, 70, -60 };
                    P7 = new int[] { 160, 160, -60 };
                    P8 = new int[] { 160, 160, 60 };
                    P9 = new int[] { 40, 160, -60 };
                    break;
                case 4://六角錐
                    P1 = new int[] { 100, 20, 0 };
                    P2 = new int[] { 100, 160, -60 };
                    P3 = new int[] { 100-(int)(30*Math.Sqrt(3)), 160, -30 };
                    P4 = new int[] { 100-(int)(30 * Math.Sqrt(3)), 160, 30 };
                    P5 = new int[] { 100, 160, 60 };
                    P6 = new int[] { 100+(int)(30*Math.Sqrt(3)), 160, 30 };
                    P7 = new int[] { 100+(int)(30 * Math.Sqrt(3)), 160, -30 };
                    break;
                case 5://複合
                    P1 = new int[] { 100, 10, 0 };
                    P2 = new int[] { 100, 130, (int)(60*Math.Sqrt(2)) };
                    P3 = new int[] { 100 + (int)(30*Math.Sqrt(6)), 130, -(int)(30 * Math.Sqrt(2)) };
                    P4 = new int[] { 100 - (int)(30*Math.Sqrt(6)), 130, -(int)(30 * Math.Sqrt(2)) };
                    P5 = new int[] { 100, 190, 0 };
                    P6 = new int[] { 100, 70, -(int)(60*Math.Sqrt(2)) };
                    P7 = new int[] { 100 + (int)(30*Math.Sqrt(6)), 70, (int)(30 * Math.Sqrt(2)) };
                    P8 = new int[] { 100 - (int)(30*Math.Sqrt(6)), 70, (int)(30 * Math.Sqrt(2)) };
                    break;
                default:
                    P1 = new int[] { 190, 100, 0 };
                    P2 = new int[] { 100, 190, 0 };
                    P3 = new int[] { 10, 100, 0 };
                    P4 = new int[] { 100, 10, 0 };
                    P5 = new int[] { 100, 100, 90 };
                    P6 = new int[] { 100, 100, -90 };
                    break;
            }

            int[][] Ps = { P1, P2, P3, P4, P5, P6, P7, P8, P9, P10 };
            int[][] Qs = { Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10 };
            double temp0, temp1, temp2;

            //変換後の頂点の座標
            for (int i = 0; i < Ps.Length; ++i)
            {
                if (Ps[i].Length > 0)
                {
                    //y軸のまわりに回転
                    temp0 = (Ps[i][0] - 100) * Math.Cos(rxz) - Ps[i][2] * Math.Sin(rxz) + 100;
                    temp1 = Ps[i][1];
                    temp2 = (Ps[i][0] - 100) * Math.Sin(rxz) + Ps[i][2] * Math.Cos(rxz);

                    //x軸のまわりに回転
                    Qs[i][0] = (int)temp0;
                    Qs[i][1] = (int)((temp1 - 100) * Math.Cos(ryz) - temp2 * Math.Sin(ryz)) + 100;
                    Qs[i][2] = (int)((temp1 - 100) * Math.Sin(ryz) + temp2 * Math.Cos(ryz));
                }
            }

            //面の割り当て
            switch (ck)
            {
                case 0://正四面体
                    F1 = new int[][] { Q1, Q2, Q3 };
                    F2 = new int[][] { Q1, Q3, Q4 };
                    F3 = new int[][] { Q1, Q4, Q2 };
                    F4 = new int[][] { Q2, Q4, Q3 };
                    break;
                case 1://立方体
                    F1 = new int[][] { Q1, Q4, Q3, Q2 };
                    F2 = new int[][] { Q5, Q6, Q7, Q8 };
                    F3 = new int[][] { Q1, Q2, Q6, Q5 };
                    F4 = new int[][] { Q2, Q3, Q7, Q6 };
                    F5 = new int[][] { Q3, Q4, Q8, Q7 };
                    F6 = new int[][] { Q4, Q1, Q5, Q8 };
                    break;
                case 2://正八面体
                    F1 = new int[][] { Q5, Q2, Q1 };
                    F2 = new int[][] { Q5, Q3, Q2 };
                    F3 = new int[][] { Q5, Q4, Q3 };
                    F4 = new int[][] { Q5, Q1, Q4 };
                    F5 = new int[][] { Q6, Q1, Q2 };
                    F6 = new int[][] { Q6, Q2, Q3 };
                    F7 = new int[][] { Q6, Q3, Q4 };
                    F8 = new int[][] { Q6, Q4, Q1 };
                    break;
                case 3://立方体カット
                    F1 = new int[][] { Q1, Q3, Q2 };
                    F2 = new int[][] { Q2, Q3, Q6, Q5, Q4 };
                    F3 = new int[][] { Q1, Q2, Q4, Q8, Q7 };
                    F4 = new int[][] { Q1, Q7, Q9, Q6, Q3 };
                    F5 = new int[][] { Q4, Q5, Q8 };
                    F6 = new int[][] { Q6, Q9, Q5 };
                    F7 = new int[][] { Q5, Q9, Q7, Q8 };
                    break;
                case 4://六角錐
                    F1 = new int[][] { Q1, Q2, Q3 };
                    F2 = new int[][] { Q1, Q3, Q4 };
                    F3 = new int[][] { Q1, Q4, Q5 };
                    F4 = new int[][] { Q1, Q5, Q6 };
                    F5 = new int[][] { Q1, Q6, Q7 };
                    F6 = new int[][] { Q1, Q7, Q2 };
                    F7 = new int[][] { Q2, Q7, Q6, Q5, Q4, Q3 };
                    break;
                case 5://複合
                    F1 = new int[][] { Q6, Q4, Q1 };
                    F2 = new int[][] { Q6, Q3, Q4 };
                    F3 = new int[][] { Q6, Q1, Q3 };
                    F4 = new int[][] { Q8, Q4, Q2 };
                    F5 = new int[][] { Q8, Q2, Q1 };
                    F6 = new int[][] { Q8, Q1, Q4 };
                    F7 = new int[][] { Q5, Q2, Q4 };
                    F8 = new int[][] { Q5, Q4, Q3 };
                    F9 = new int[][] { Q5, Q3, Q2 };
                    F10 = new int[][] { Q7, Q1, Q2 };
                    F11 = new int[][] { Q7, Q2, Q3 };
                    F12 = new int[][] { Q7, Q3, Q1 };
                    break;
                default:
                    F1 = new int[][] { Q5, Q2, Q1 };
                    F2 = new int[][] { Q5, Q3, Q2 };
                    F3 = new int[][] { Q5, Q4, Q3 };
                    F4 = new int[][] { Q5, Q1, Q4 };
                    F5 = new int[][] { Q6, Q1, Q2 };
                    F6 = new int[][] { Q6, Q2, Q3 };
                    F7 = new int[][] { Q6, Q3, Q4 };
                    F8 = new int[][] { Q6, Q4, Q1 };
                    break;
            }
                        
            int[][][] Fs = { F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12 };

            //法線ベクトル
            double[] Nx = new double[Fs.Length];
            int[] Nz = new int[Fs.Length];
            for (int i = 0; i < Fs.Length; ++i)
            {
                int[] vec1 = new int[3];
                int[] vec2 = new int[3];
                int[] cr = new int[3];
               
                if (Fs[i].Length > 0)
                {
                    for (int j = 0; j < 3;++j)
                    {
                        vec1[j] = Fs[i][1][j] - Fs[i][0][j];
                        vec2[j] = Fs[i][2][j] - Fs[i][1][j];
                    }
                    for (int j = 0; j < 3; ++j)
                    {
                        cr[j] = vec2[(j + 1) % 3] * vec1[(j + 2) % 3] - vec2[(j + 2) % 3] * vec1[(j + 1) % 3];
                    }
                    //法線ベクトルの大きさ
                    double len = Math.Sqrt(cr[0] * cr[0] + cr[1] * cr[1] + cr[2] * cr[2]);
                    //x成分を規格化
                    Nx[i] = cr[0] / len;
                    Nz[i] = cr[2];
                }
            }
            
            Bitmap sheet = new Bitmap(pb.Width, pb.Height);
            Graphics g = Graphics.FromImage(sheet);

            for(int i = 0;i < Fs.Length; ++i)
            {
                //法線ベクトルが手前に向いている面のみ処理
                if (Fs[i].Length > 0 && Nz[i] >= 0)
                {
                    Point[] P = new Point[Fs[i].Length];
                    for (int j = 0; j < Fs[i].Length; ++j)
                    {
                        //x,y座標のみ抽出
                        P[j] = new Point(Fs[i][j][0], Fs[i][j][1]);
                    }
                    //描画
                    Brush br;
                    if (0.7 < Nx[i] && Nx[i] <= 1) { br = Brushes.LightCyan; }
                    else if (0.3 < Nx[i] && Nx[i] <= 0.7) { br = Brushes.PaleTurquoise; }
                    else if (-0.5 < Nx[i] && Nx[i] <= 0.3) { br = Brushes.Turquoise; }
                    else { br = Brushes.DarkCyan; }
                    g.FillPolygon(br, P);
                    g.DrawPolygon(Pens.Black, P);
                }
            }
            g.Dispose();
            pb.Image = sheet;
        }

    }
}

 

 

2012年12月22日 (土)

C#:立体を回転させて表示/その2

けっこう面白いので、少し改良した。

  1. 隠面処理に法線ベクトルを使ってみた。
  2. 立体をラジオボタンで選べるようにした。

立方体の断面が五角形になるようにカット

Cubic11

正四面体の各面から三角錐が生えた立体

Cubic12

正八面体が長方形に見える瞬間

Cubic13

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

namespace WindowsFormsApplication1
{
   
    public class Form1 : Form//Designer.cs 削除
    {
        private System.ComponentModel.IContainer components = null;
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        PictureBox pb = new PictureBox();
        TrackBar t1 = new TrackBar();
        TrackBar t2 = new TrackBar();
        Label l1 = new Label();
        Label l2 = new Label();
        double rxz = 0;
        double ryz = 0;
        int axz = 0;
        int ayz = 0;
        int axz0 = 0;
        int ayz0 = 0;
        Point mp;
        Point tp;
        int ck = 0;
        string[] Cubes = { "正四面体", "立方体", "正八面体", "立方体カット", "六角錐", "複合" };

        public Form1()
        {
            this.components = new System.ComponentModel.Container();
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.Text = "Cubic";
            this.ClientSize = new Size(300, 300);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;
            this.Controls.Add(pb);
            pb.SetBounds(0, 0, 200, 200);
            pb.BackColor = Color.White;
            this.Controls.Add(t1);
            t1.AutoSize = false;
            t1.SetBounds(0, 200, 200, 20);
            t1.TickStyle = TickStyle.None;
            t1.Minimum = 0;
            t1.Maximum = 360;
            t1.Value = axz + 180;
            this.Controls.Add(t2);
            t2.AutoSize = false;
            t2.Orientation = Orientation.Vertical;
            t2.SetBounds(200, 0, 20, 200);
            t2.TickStyle = TickStyle.None;
            t2.Minimum = 0;
            t2.Maximum = 180;
            t2.Value = ayz + 90;
            this.Controls.Add(l1);
            this.Controls.Add(l2);
            l1.SetBounds(70, 220, 60, 20);
            l1.TextAlign = ContentAlignment.MiddleCenter;
            l1.Text = "Axz: " + axz.ToString();
            l2.SetBounds(220, 90, 60, 20);
            l2.TextAlign = ContentAlignment.MiddleCenter;
            l2.Text = "Ayz: " + ayz.ToString();
            
            pb.MouseDown += new MouseEventHandler(pb_MouseDown);
            pb.MouseMove += new MouseEventHandler(pb_MouseMove);

            t1.MouseDown += new MouseEventHandler(t_MouseDown);
            t2.MouseDown += new MouseEventHandler(t_MouseDown);
            t1.ValueChanged += new EventHandler(t_ValueChanged);
            t2.ValueChanged += new EventHandler(t_ValueChanged);

            GroupBox gb = new GroupBox();
            gb.SetBounds(10, 240, 280, 55);
            this.Controls.Add(gb);

            RadioButton[] rd = new RadioButton[Cubes.Length];

            for (int i = 0; i < Cubes.Length; ++i)
            {
                rd[i] = new RadioButton();
                if (i < 3)
                {
                    rd[i].SetBounds(10 + i * 90, 10, 85, 20);
                }
                else
                {
                    rd[i].SetBounds(10 + (i-3) * 90, 30, 85, 20);
                }
                rd[i].Text = Cubes[i];
                rd[i].CheckedChanged += new EventHandler(rd_CheckedChanged);

            }
            rd[0].Checked = true;
            gb.Controls.AddRange(rd);
            Rotate();
        }

        void rd_CheckedChanged(object sender, EventArgs e)
        {
            RadioButton rbutton = sender as RadioButton;
            for (int i = 0; i < Cubes.Length; ++i)
            {
                if (rbutton.Text == Cubes[i])
                {
                    ck = i;
                }
            }

            Rotate();
        }

        void t_MouseDown(object sender, MouseEventArgs e)
        {
            pb.MouseMove -= new MouseEventHandler(pb_MouseMove);
            t1.ValueChanged += new EventHandler(t_ValueChanged);
            t2.ValueChanged += new EventHandler(t_ValueChanged);
            tp = new Point(t1.Value, t2.Value);
            axz0 = axz;
            ayz0 = ayz;
        }

        void t_ValueChanged(object sender, EventArgs e)
        {
            axz = t1.Value - 180;
            ayz = t2.Value - 90;

            if (axz > 180) { axz = 180; }
            else if (axz < -180) { axz = -180; }

            if (ayz > 90) { ayz = 90; }
            else if (ayz < -90) { ayz = -90; }

            l1.Text = "Axz: " + axz.ToString();
            l2.Text = "Axz: " + ayz.ToString();
            rxz = (-1) * axz * Math.PI / 180;
            ryz = ayz * Math.PI / 180;
            Rotate();
        }

        void pb_MouseDown(object sender, MouseEventArgs e)
        {
            t1.ValueChanged -= new EventHandler(t_ValueChanged);
            t2.ValueChanged -= new EventHandler(t_ValueChanged);
            pb.MouseMove += new MouseEventHandler(pb_MouseMove);
            mp = new Point(e.X, e.Y);
            axz0 = axz;
            ayz0 = ayz;
        }

        void pb_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                axz = axz0 + e.X - mp.X;
                ayz = ayz0 + mp.Y - e.Y;
               
                if (axz >= 180) { axz = 180; }
                else if (axz <= -180) { axz = -180; }

                if (ayz >= 90) { ayz = 90; }
                else if (ayz <= -90) { ayz = -90; }

                t1.Value = axz + 180;
                t2.Value = ayz + 90;
                l1.Text = "Axz: " + axz.ToString();
                l2.Text = "Ayz: " + ayz.ToString();
                rxz = (-1) * axz * Math.PI / 180;
                ryz = ayz * Math.PI / 180;
                Rotate();
            }
        }

        private void Rotate()
        {
            //初期の頂点
            int[] P1 = new int[3];
            int[] P2 = new int[3];
            int[] P3 = new int[3];
            int[] P4 = new int[3];
            int[] P5 = new int[3];
            int[] P6 = new int[3];
            int[] P7 = new int[3];
            int[] P8 = new int[3];
            int[] P9 = new int[3];
            int[] P10 = new int[3];
            //移動後の頂点
            int[] Q1 = new int[3];
            int[] Q2 = new int[3];
            int[] Q3 = new int[3];
            int[] Q4 = new int[3];
            int[] Q5 = new int[3];
            int[] Q6 = new int[3];
            int[] Q7 = new int[3];
            int[] Q8 = new int[3];
            int[] Q9 = new int[3];
            int[] Q10 = new int[3];
            //面
            int[][] F1 = new int[][] { };
            int[][] F2 = new int[][] { };
            int[][] F3 = new int[][] { };
            int[][] F4 = new int[][] { };
            int[][] F5 = new int[][] { };
            int[][] F6 = new int[][] { };
            int[][] F7 = new int[][] { };
            int[][] F8 = new int[][] { };
            int[][] F9 = new int[][] { };
            int[][] F10 = new int[][] { };
            int[][] F11 = new int[][] { };
            int[][] F12 = new int[][] { };

            //点の割り当て
            switch (ck)
            {
                case 0://正四面体
                    P1 = new int[] { 100, 10, 0 };
                    P2 = new int[] { 100, 130, (int)(60*Math.Sqrt(2)) };
                    P3 = new int[] { 100 + (int)(30*Math.Sqrt(6)), 130, -(int)(30 * Math.Sqrt(2)) };
                    P4 = new int[] { 100 - (int)(30*Math.Sqrt(6)), 130, -(int)(30 * Math.Sqrt(2)) };
                    break;
                case 1://立方体
                    P1 = new int[] { 150, 150, 50 };
                    P2 = new int[] { 50, 150, 50 };
                    P3 = new int[] { 50, 50, 50 };
                    P4 = new int[] { 150, 50, 50 };
                    P5 = new int[] { 150, 150, -50 };
                    P6 = new int[] { 50, 150, -50 };
                    P7 = new int[] { 50, 50, -50 };
                    P8 = new int[] { 150, 50, -50 };
                    break;
                case 2://正八面体
                    P1 = new int[] { 190, 100, 0 };
                    P2 = new int[] { 100, 190, 0 };
                    P3 = new int[] { 10, 100, 0 };
                    P4 = new int[] { 100, 10, 0 };
                    P5 = new int[] { 100, 100, 90 };
                    P6 = new int[] { 100, 100, -90 };
                    break;
                case 3://立方体カット
                    P1 = new int[] { 160, 40, -60 };
                    P2 = new int[] { 160, 40, 20 };
                    P3 = new int[] { 80, 40, -60 };
                    P4 = new int[] { 160, 70, 60 };
                    P5 = new int[] { 40, 160, 60 };
                    P6 = new int[] { 40, 70, -60 };
                    P7 = new int[] { 160, 160, -60 };
                    P8 = new int[] { 160, 160, 60 };
                    P9 = new int[] { 40, 160, -60 };
                    break;
                case 4://六角錐
                    P1 = new int[] { 100, 40, 0 };
                    P2 = new int[] { 100, 160, -60 };
                    P3 = new int[] { 100-(int)(30*Math.Sqrt(3)), 160, -30 };
                    P4 = new int[] { 100-(int)(30 * Math.Sqrt(3)), 160, 30 };
                    P5 = new int[] { 100, 160, 60 };
                    P6 = new int[] { 100+(int)(30*Math.Sqrt(3)), 160, 30 };
                    P7 = new int[] { 100+(int)(30 * Math.Sqrt(3)), 160, -30 };
                    break;
                case 5://複合
                    P1 = new int[] { 100, 10, 0 };
                    P2 = new int[] { 100, 130, (int)(60*Math.Sqrt(2)) };
                    P3 = new int[] { 100 + (int)(30*Math.Sqrt(6)), 130, -(int)(30 * Math.Sqrt(2)) };
                    P4 = new int[] { 100 - (int)(30*Math.Sqrt(6)), 130, -(int)(30 * Math.Sqrt(2)) };
                    P5 = new int[] { 100, 190, 0 };
                    P6 = new int[] { 100, 70, -(int)(60*Math.Sqrt(2)) };
                    P7 = new int[] { 100 + (int)(30*Math.Sqrt(6)), 70, (int)(30 * Math.Sqrt(2)) };
                    P8 = new int[] { 100 - (int)(30*Math.Sqrt(6)), 70, (int)(30 * Math.Sqrt(2)) };
                    break;
                default:
                    P1 = new int[] { 190, 100, 0 };
                    P2 = new int[] { 100, 190, 0 };
                    P3 = new int[] { 10, 100, 0 };
                    P4 = new int[] { 100, 10, 0 };
                    P5 = new int[] { 100, 100, 90 };
                    P6 = new int[] { 100, 100, -90 };
                    break;
            }

            int[][] Ps = { P1, P2, P3, P4, P5, P6, P7, P8, P9, P10 };
            int[][] Qs = { Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10 };
            double temp0, temp1, temp2;

            //変換後の頂点の座標
            for (int i = 0; i < Ps.Length; ++i)
            {
                if (Ps[i].Length > 0)
                {
                    //y軸のまわりに回転
                    temp0 = (Ps[i][0] - 100) * Math.Cos(rxz) - Ps[i][2] * Math.Sin(rxz) + 100;
                    temp1 = Ps[i][1];
                    temp2 = (Ps[i][0] - 100) * Math.Sin(rxz) + Ps[i][2] * Math.Cos(rxz);

                    //x軸のまわりに回転
                    Qs[i][0] = (int)temp0;
                    Qs[i][1] = (int)((temp1 - 100) * Math.Cos(ryz) - temp2 * Math.Sin(ryz)) + 100;
                    Qs[i][2] = (int)((temp1 - 100) * Math.Sin(ryz) + temp2 * Math.Cos(ryz));
                }
            }

            //面の割り当て
            switch (ck)
            {
                case 0://正四面体
                    F1 = new int[][] { Q1, Q2, Q3 };
                    F2 = new int[][] { Q1, Q3, Q4 };
                    F3 = new int[][] { Q1, Q4, Q2 };
                    F4 = new int[][] { Q2, Q4, Q3 };
                    break;
                case 1://立方体
                    F1 = new int[][] { Q1, Q4, Q3, Q2 };
                    F2 = new int[][] { Q5, Q6, Q7, Q8 };
                    F3 = new int[][] { Q1, Q2, Q6, Q5 };
                    F4 = new int[][] { Q2, Q3, Q7, Q6 };
                    F5 = new int[][] { Q3, Q4, Q8, Q7 };
                    F6 = new int[][] { Q4, Q1, Q5, Q8 };
                    break;
                case 2://正八面体
                    F1 = new int[][] { Q5, Q2, Q1 };
                    F2 = new int[][] { Q5, Q3, Q2 };
                    F3 = new int[][] { Q5, Q4, Q3 };
                    F4 = new int[][] { Q5, Q1, Q4 };
                    F5 = new int[][] { Q6, Q1, Q2 };
                    F6 = new int[][] { Q6, Q2, Q3 };
                    F7 = new int[][] { Q6, Q3, Q4 };
                    F8 = new int[][] { Q6, Q4, Q1 };
                    break;
                case 3://立方体カット
                    F1 = new int[][] { Q1, Q3, Q2 };
                    F2 = new int[][] { Q2, Q3, Q6, Q5, Q4 };
                    F3 = new int[][] { Q1, Q2, Q4, Q8, Q7 };
                    F4 = new int[][] { Q1, Q7, Q9, Q6, Q3 };
                    F5 = new int[][] { Q4, Q5, Q8 };
                    F6 = new int[][] { Q6, Q9, Q5 };
                    F7 = new int[][] { Q5, Q9, Q7, Q8 };
                    break;
                case 4://六角錐
                    F1 = new int[][] { Q1, Q2, Q3 };
                    F2 = new int[][] { Q1, Q3, Q4 };
                    F3 = new int[][] { Q1, Q4, Q5 };
                    F4 = new int[][] { Q1, Q5, Q6 };
                    F5 = new int[][] { Q1, Q6, Q7 };
                    F6 = new int[][] { Q1, Q7, Q2 };
                    F7 = new int[][] { Q2, Q7, Q6, Q5, Q4, Q3 };
                    break;
                case 5://複合
                    F1 = new int[][] { Q6, Q4, Q1 };
                    F2 = new int[][] { Q6, Q3, Q4 };
                    F3 = new int[][] { Q6, Q1, Q3 };
                    F4 = new int[][] { Q8, Q4, Q2 };
                    F5 = new int[][] { Q8, Q2, Q1 };
                    F6 = new int[][] { Q8, Q1, Q4 };
                    F7 = new int[][] { Q5, Q2, Q4 };
                    F8 = new int[][] { Q5, Q4, Q3 };
                    F9 = new int[][] { Q5, Q3, Q2 };
                    F10 = new int[][] { Q7, Q1, Q2 };
                    F11 = new int[][] { Q7, Q2, Q3 };
                    F12 = new int[][] { Q7, Q3, Q1 };
                    break;
                default:
                    F1 = new int[][] { Q5, Q2, Q1 };
                    F2 = new int[][] { Q5, Q3, Q2 };
                    F3 = new int[][] { Q5, Q4, Q3 };
                    F4 = new int[][] { Q5, Q1, Q4 };
                    F5 = new int[][] { Q6, Q1, Q2 };
                    F6 = new int[][] { Q6, Q2, Q3 };
                    F7 = new int[][] { Q6, Q3, Q4 };
                    F8 = new int[][] { Q6, Q4, Q1 };
                    break;
            }
                        
            int[][][] Fs = { F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12 };

            //法線ベクトル
            int[] Cs = new int[Fs.Length];
            for (int i = 0; i < Fs.Length; ++i)
            {
                int[] vec1 = new int[3];
                int[] vec2 = new int[3];
                int[] cr = new int[3];
               
                if (Fs[i].Length > 0)
                {
                    for (int j = 0; j < 3;++j)
                    {
                        vec1[j] = Fs[i][1][j] - Fs[i][0][j];
                        vec2[j] = Fs[i][2][j] - Fs[i][1][j];
                    }
                    for (int j = 0; j < 3; ++j)
                    {
                        cr[j] = vec2[(j + 1) % 3] * vec1[(j + 2) % 3] - vec2[(j + 2) % 3] * vec1[(j + 1) % 3];
                    }
                    Cs[i] = cr[2];
                }
            }
            
            Bitmap sheet = new Bitmap(pb.Width, pb.Height);
            Graphics g = Graphics.FromImage(sheet);

            for(int i = 0;i < Fs.Length; ++i)
            {
                //法線ベクトルが手前に向いている面のみ処理
                if (Fs[i].Length > 0 && Cs[i] >= 0)
                {
                    Point[] P = new Point[Fs[i].Length];
                    for (int j = 0; j < Fs[i].Length; ++j)
                    {
                        //x,y座標のみ抽出
                        P[j] = new Point(Fs[i][j][0], Fs[i][j][1]);
                    }
                    //描画
                    g.FillPolygon(Brushes.Turquoise, P);
                    g.DrawPolygon(Pens.Black, P);
                }
            }
            g.Dispose();
            pb.Image = sheet;
        }

    }
}

 

 

2018年10月
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      

ブックマーク

無料ブログはココログ