うにゅーん、って感じだ

SRM highest:1959. C#を書きます.

Visual Studio + C# で寿司を回した

タイトルそのままです。頑張って以下のような gif を作成しました。
f:id:riantkb:20171216004558g:plain

背景

寿司回したい……寿司回したくない?

私は ICPC のチーム「kurukuru-sushi」の一員でなので、無性に sushi を kurukuru したくなりました。

だがしかし、チームメイトの賛同は得られなかった…。
f:id:riantkb:20171216004940p:plain

仕方がないので、自分で寿司を回すことにしました。

圧倒的スキルのなさ

でもどうやって回せばいいのかわからなかったのでとりあえず呟く。

得られた回答

寿司を買って回す
  • それは本当にやるつもりで、回転寿司にいく予定を立てていたが結局それは叶わなかった。
css, processing 等で作って録画
  • そこらへんの便利そうな言語触ったことないので険しい……


あれ、もしかして私の技術力低すぎ……?


仕方ないので、自分が書ける言語の中でフォームアプリケーションが書ける、Visual Studio + C# で寿司を回すことにしました。*1

実際に作る

寿司の画像

寿司の画像は、はま寿司の HP に素材にしてくださいと言わんばかりの透過 PNG 画像があったので、それを用いました。
(一見右クリックしても取れないが、html ソースを追うか英語版のページに行くと取れます)

コードを書く

もう Visual Studio を触るのが 2 年ぶりくらいなのでとりあえず動けばいいやな感じなので、とても適当です。
(このコードを書く前に、デザイナの方で picturebox やら timer やらを追加したりイベントを追加したりしています)

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 WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        int cx, cy;
        Bitmap canvas;
        Bitmap maguro, ebi, tamago;
        Graphics g;
        double[] sin, cos;
        int cnt = 3600;

        public Form1()
        {
            InitializeComponent();
            SetStyle(ControlStyles.DoubleBuffer, true);
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            cx = pictureBox1.Width / 2;
            cy = pictureBox1.Height / 2;
            sin = new double[cnt];
            cos = new double[cnt];
            for (int i = 0; i < cnt; i++)
            {
                sin[i] = Math.Sin(Math.PI * 2 * i / cnt);
                cos[i] = Math.Cos(Math.PI * 2 * i / cnt);
            }

            maguro = new Bitmap(@"E:\Downloads\pho_maguro.png");
            ebi = new Bitmap(@"E:\Downloads\pho_ebi.png");
            tamago = new Bitmap(@"E:\Downloads\pho_umadashitamago.png");
        }
        void rotate(double x, double y, int t, out float ox, out float oy)
        {
            ox = (float)(x * cos[t] + y * sin[t]);
            oy = (float)(-x * sin[t] + y * cos[t]);
        }
        PointF[] getpoints(float cx, float cy, int h, int w, int t)
        {
            float x1, y1, x2, y2, x3, y3;
            rotate(-w * 0.5, -h * 0.5, t, out x1, out y1);
            rotate(w * 0.5, -h * 0.5, t, out x2, out y2);
            rotate(-w * 0.5, h * 0.5, t, out x3, out y3);
            return new[] { new PointF(x1 + cx, y1 + cy), new PointF(x2 + cx, y2 + cy), new PointF(x3 + cx, y3 + cy) };
        }
        int c = 0, r = 280;

        private void timer1_Tick(object sender, EventArgs e)
        {
            canvas = new Bitmap(pictureBox1.Width, pictureBox1.Height);
            g = Graphics.FromImage(canvas);
            double x = cx + r * cos[c], y = cy - r * sin[c];
            g.DrawImage(maguro, getpoints((float)x, (float)y, maguro.Height, maguro.Width, c * 7 % cnt));
            x = cx + r * cos[(c + cnt / 3) % cnt];
            y = cy - r * sin[(c + cnt / 3) % cnt];
            g.DrawImage(ebi, getpoints((float)x, (float)y, ebi.Height, ebi.Width, c * 7 % cnt));
            x = cx + r * cos[(c + cnt * 2 / 3) % cnt];
            y = cy - r * sin[(c + cnt * 2 / 3) % cnt];
            g.DrawImage(tamago, getpoints((float)x, (float)y, tamago.Height, tamago.Width, c * 7 % cnt));
            pictureBox1.Image = canvas;
            c = (c + 97) % cnt;
        }
    }
}

やってることは単純で、半径 r の円周上に寿司を置きつつ適当な角度自転させています。
timer の間隔は 25ms くらいにしていたはずです(40fps?)。
実行時めっっっちゃ重かったので三角関数を前計算したりとかしてみましたが、クソ重いのは改善しませんでした。
自転のパラメータを調整することで、公転に対する自転の速さを自由に変えられるので、それを適当に変えて色々実行しまくるとめっちゃ楽しかったです。



あとはこれを実行させつつ gif を撮影して、上に載せたような gif を得ることができました。
(gif の撮影には ScreenToGif とかいうフリーソフトを用いました)

完成!

なんとか、それっぽいのを作ることができました。
かかった時間としては 2 時間くらいなので、まぁまぁ上出来ですかね……。


某の紹介スライドでこの gif が出てくるのですが、しらけるとつらすぎるので皆さん笑ってくださいお願いしますなんでも



よく考えたらこのブログも寿司が回ってますね……(全く気にしてなかった)

*1:本当は windows のピッシを開くのが面倒なのでその前に mono でフォームアプリケーションが作れないか試したのですが、謎エラーが頻発して疲弊したので素直に VS を使うことにしました