Skip to content

Instantly share code, notes, and snippets.

@hidsh
Created December 11, 2011 20:27
Show Gist options
  • Save hidsh/1216c24d125d82627c95 to your computer and use it in GitHub Desktop.
Save hidsh/1216c24d125d82627c95 to your computer and use it in GitHub Desktop.
期末レポート
期末レポートを提出いたします。
お断り:スケジュールが逼迫してきたため、テーマを当初予定しておりました「C#による組込Lisp処理系の設計と実装」から「C# によるお絵かきツールの試作」に変更させていただきました。
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 System.Collections;
namespace WindowsFormsApplication1
{
public enum Cmd { SELECT, LINE, BOX, OVAL }
public enum Mode { IDLE, BEGIN, END}
//-------------------------------------------------
public partial class Form1 : Form
{
private Shape rb;
static public List<Shape> shapes;
public Mode mode;
public Cmd cmd;
private Graphics graphic;
public Form1()
{
InitializeComponent();
shapes = new List<Shape>();
mode = Mode.IDLE;
cmd = Cmd.SELECT;
graphic = pictureBox1.CreateGraphics();
}
private void draw_all_shapes()
{
foreach(Shape sh in shapes)
{
sh.draw(graphic);
}
}
private Shape get_shape(Cmd cmd)
{
switch(cmd)
{
case Cmd.LINE: return new Line();
case Cmd.BOX: return new Box();
case Cmd.OVAL: return new Oval();
default: return new Box();
}
}
private void update_status_line(int x=0, int y=0)
{
label_pos.Text = cmd + " : " + shapes.Count + " : " + x + " : " + y;
}
//
// event handlers
//
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (mode == Mode.IDLE)
{
rb = get_shape(cmd);
rb.coodinate.Add(new Point(e.X, e.Y)); // begin
rb.coodinate.Add(new Point(e.X, e.Y)); // begin
mode = Mode.BEGIN;
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
update_status_line(e.X, e.Y);
if (mode == Mode.BEGIN)
{
pictureBox1.Refresh();
rb.coodinate[1] = new Point(e.X, e.Y);
rb.draw_rubber(graphic);
draw_all_shapes();
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
if(mode == Mode.BEGIN)
{
if(cmd == Cmd.SELECT)
{
// do nothing
} else {
rb.coodinate[1] = new Point(e.X, e.Y);
shapes.Add(rb);
}
mode = Mode.IDLE;
}
pictureBox1.Refresh();
draw_all_shapes();
}
private void cmdButtonLine_Click(object sender, EventArgs e)
{
cmd = Cmd.LINE;
mode = Mode.IDLE;
update_status_line();
}
private void cmdButtonSelect_Click(object sender, EventArgs e)
{
cmd = Cmd.SELECT;
mode = Mode.IDLE;
update_status_line();
}
private void cmdButtonBox_Click(object sender, EventArgs e)
{
cmd = Cmd.BOX;
mode = Mode.IDLE;
update_status_line();
}
private void cmdButtonOval_Click(object sender, EventArgs e)
{
cmd = Cmd.OVAL;
mode = Mode.IDLE;
update_status_line();
}
private void cmdButtonOpen_Click(object sender, EventArgs e)
{
const string fn = @"c:\data.xml";
List<Shape> ss = Util.read_file(fn);
foreach (Shape s in ss)
{
s.pen = new Pen(Color.Black);
}
Form1.shapes = ss;
pictureBox1.Refresh();
draw_all_shapes();
}
private void cmdButtonSave_Click(object sender, EventArgs e)
{
const string fn = @"c:\data.xml";
Util.write_file(Form1.shapes, fn);
}
}
//-------------------------------------------------
[Serializable()]
public abstract class Shape : ICloneable
{
public List<Point> coodinate;
[NonSerialized]
public Pen pen;
public abstract void draw(Graphics g);
public abstract void draw_rubber(Graphics g);
public Shape()
{
coodinate = new List<Point>();
pen = new Pen(Color.Black);
}
public Object Clone() { return MemberwiseClone(); }
}
[Serializable()]
public class Box : Shape
{
public override void draw(Graphics g)
{
g.DrawRectangle(pen, Util.get_rect(coodinate));
}
public override void draw_rubber(Graphics g)
{
Pen p = new Pen(Color.Blue);
p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
g.DrawRectangle(p, Util.get_rect(coodinate));
}
}
[Serializable()]
public class Oval : Shape
{
public override void draw(Graphics g)
{
g.DrawEllipse(pen, Util.get_rect(coodinate));
}
public override void draw_rubber(Graphics g)
{
Pen p = new Pen(Color.Blue);
p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
g.DrawEllipse(p, Util.get_rect(coodinate));
}
}
[Serializable()]
public class Line : Shape
{
public override void draw(Graphics g)
{
g.DrawLine(pen, coodinate[0], coodinate[1]);
}
public override void draw_rubber(Graphics g)
{
Pen p = new Pen(Color.Blue);
p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
g.DrawLine(pen, coodinate[0], coodinate[1]);
}
}
}
2011年11月18日 提出
「レポート・論文の書き方」期末レポート計画書
サイバー大学 IT総合学部 1年
1101015185 宍戸秀昭
■期末レポートのタイトル(仮)
 C#による組込Lisp処理系の設計と実装(仮)
■概要・目的(レポートの概要、目的、研究方法など)
 アプリケーションプログラムの機能拡張方法として、組込言語による機能拡張がある。
これは、アプリケーションの中にあらかじめ簡易的な言語処理系を組み込んでおき、ユーザがこの言語を使って自分の欲しい機能を追加できるようにする仕組みである。
 組込言語を取り入れたアプリケーションは作るのに余計な手間がかかるといわれているが、シンプルな言語処理系を選択すれば比較的短時間で可能という意見もある。
 私は今までアプリケーションを幾つか書いたことはあるが、組込言語を実装したことはない。そこで期末レポートでは、組込言語処理系として Lisp を採用したアプリケーションを実際に試作し、Lisp 処理系の実装に要した作業時間を測ることで、現実的な手法であるかどうかを評価したいと思う。
■目次(案)
 1概要
  1-1背景 アプリケーションへの機能追加の方法の説明と組込言語の必要性
  1-2要約 試作物の紹介、作業時間を表示、現実的な手法かどうかを結論
 2序論
   2-1 アプリケーションへの機能追加の方法
   2-2 組込言語の必要性
   2-3 プログラミング言語 Lisp の概説
 3本論
   3-1 お絵かきツールの概説
   3-2 お絵かきツールの機能分割とAPIの説明
   3-3 Lisp 処理系の構造説明
   3-4 ユースケースの例と処理の流れ
   3-5 Lisp 処理系の設計と実装にかかった作業時間の考察
   3-6 現実的な手法として使えるかどうかを結論
 4論議
   4-1 理想的な機能分割について
   4-2 他のアプリケーションへの応用
■執筆計画
  ~11/16 参考文献収集
  ~12/ 4 参考文献調査
  ~12/10 Lisp 処理系作成
  ~12/20 論文執筆
■参考文献
 著者/訳者:Peter Seibel / 佐野匡俊・水丸淳・園城雅之・金子祐介
 タイトル:実践 Common Lisp (Practical Common Lisp)
 出版社/版次/出版年:オーム社 / 第1版第1刷 / 2008/7/25
■参考文献
%
1.著者:匿名
 タイトル:「C Sharp」
 URI:http://ja.wikipedia.org/wiki/C_Sharp
 アクセス日付:2010/12/10
2.著者:Microsoft Corporation
 タイトル:「.NET Framework とは何ですか?」
 URI:http://msdn.microsoft.com/ja-jp/library/ms973850.aspx#faq111700_concepts02
 アクセス日付:2010/12/10
3.著者:日向 俊二
 タイトル:「サンプルコードで使い方がわかる .NET Framework実践ライブラリリファレンス」
 出版社/版次/出版年:アスキー・メディアワークス/第1版第1刷/2009/4/28
4.著者:丸岡 孝司
 タイトル:「絶対現場主義 C#入門」
 出版社/版次/出版年:ラトルズ/第1版第1刷/2011/10/12
5.著者/訳者:ハーバート・シルト/エディフィストラーニング株式会社 矢嶋聡
 タイトル:「独習C#」
 出版社/版次/出版年:翔泳社/第3版第1刷/2010/12/3
題名:C# によるお絵かきツールの試作
■目次
1 概要
2 序論
3 本論
3-1 お絵かきツールの説明
3-2 図形クラス
3-3 描画コマンドと操作モード
3-4 マウスボタンを押す
3-5 マウスをドラッグする
3-6 マウスボタンを離す
4論議
1 概要
 C# を使うと、アプリケーションプログラムが簡単に作れると言われている。本稿では C# を使って簡単なお絵かきツールを実際に作ってみることで、その真偽を検証した。
2 序論
 「C#(シーシャープ)はマイクロソフト社によって同社の.NET戦略の一環として開発されたオブジェクト指向プログラミング言語である。」%1 とあるように、C# で何かプログラムを作るときは .NET Framework と組み合わせて使われることが多い。そこで、ここでは処理系として Microsoft VC# 2010 Express Edition とそれに含まれる .NET Framework 4.0 を使うことにする。
 作画やその他のアプリケーションとして備える機能は、.NET Framework が提供するライブラリ関数を呼ぶことで実現する。
 .NET Framework は「.NET Framework は、共通言語ランタイム、階層的な統一されたクラス ライブラリのセット、および Active Server Pages のコンポーネント化されたバージョンである ASP.NET の 3 つの部分から構成されています。」%2 と書かれているように複合的な側面を持つが、ここでは、.NET Framework を単なるGUIのクラスライブラリとして扱う。
%1 http://ja.wikipedia.org/wiki/C_Sharp
%2 http://msdn.microsoft.com/ja-jp/library/ms973850.aspx#faq111700_concepts02
3 本論
3-1 お絵かきツールの説明
 今回作成したお絵かきツールの概観を図2に示す。これは一般的な Windows で動作する GUIアプリケーションである*。
[図2]
 ユーザはアイコンで選んだ図形をマウスで描画できる。ユーザの操作はイベントとしてアプリケーションに伝達され、これをアプリケーション内のイベントハンドラで取得する。図形の描画は .NET Framework の PictureBox クラスに対して行う。ユーザが描画した図形はアプリケーションの単一のリストに保存される。ユーザがマウスをドラッグしている間は、選択した図形を破線で表現したラバーバンドを表示する。
 C# はオブジェクト指向プログラムなので、このアプリケーションは次のようにクラスとして定義される。
%code
public partial class Form1 : Form
{
private Shape rb;
private List<Shape> shapes;
:
}
 .NET Framework でGUIアプリケーションを作る場合、アプリケーションのウィンドウを、.NET Framework が提供する Form という基底クラスから継承して作るのが一般的である%3。
 上記は Form クラスを元に、お絵かきツールが備える特徴を追加した Form1 クラスを定義している。
 図形の保存先であるリストには、.NET Framework が提供する List<> コレクションを使う。.NET Framework が提供するコレクションにはさまざまなものがある%4 がここでは簡単さから List<>を選択した。
%3 「サンプルコードで使い方がわかる .NET Framework実践ライブラリリファレンス」 第11章 ウィンドウとダイアログボックス
%4 「絶対現場主義 C#入門」 3.3.2. 組み込み型とその他大勢型
* 試作したお絵かきツールは実使用を想定したものではなく、作画に必要な最低限の構成とした。
3-2 図形クラス
 ユーザが描画する図形もクラスとして定義する。まず図形の元となる Shape クラスを下記のように定義する。
%code
public abstract class Shape : ICloneable
{
public List<Point> coodinate;
public Pen pen;
public abstract void draw(Graphics g);
public abstract void draw_rubber(Graphics g);
public Shape()
{
coodinate = new List<Point>();
pen = new Pen(Color.Black);
}
public Object Clone() { return MemberwiseClone(); }
}
 ここで coodinate リストは図形を構成する座標を表す。例えば四角形の場合は、左上と右下の座標をリストとして保持する。
 図形の描画は draw() メソッドが行う。ラバーバンド用の描画は別扱いとして draw_rubber() で行うようにした。
 ユーザが描画するすべての図形は Shape クラスを継承して作られる。例えば四角形は下記のようになる。
%code
public class Box : Shape
{
public override void draw(Graphics g)
{
g.DrawRectangle(pen, Util.get_rect(coodinate));
}
public override void draw_rubber(Graphics g)
{
Pen p = new Pen(Color.Blue);
p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
g.DrawRectangle(p, Util.get_rect(coodinate));
}
}
3-3 描画コマンドと操作モード
 ユーザは線分、四角形、楕円等の描画コマンドをアイコンで選択する。プログラムではこれを描画モードとして扱う。また、マウスボタンの押/離等のユーザの操作を操作モードとして扱う。これらを C# の 列挙型 で定義すると次のようになる%5。
%code
public enum Cmd { SELECT, LINE, BOX, OVAL }
public enum Mode { IDLE, BEGIN }
 描画コマンドはユーザによる描画アイコンの選択で決定される。描画アイコンの四角形が選択された場合のイベントハンドラは下記のようになる*。
%code
private void cmdButtonSelect_Click(object sender, EventArgs e)
{
cmd = Cmd.SELECT;
mode = Mode.IDLE;
}
 一方、操作モードはマウスボタンの押/離により遷移する状態であり、図3のようになる。
[図3]
 操作モードの状態遷移は単に mode 変数への代入として扱い、それぞれのイベントハンドラに書く。
%5 「独習C#」 第9章 インターフェイス、構造体、列挙型
* ここで、操作モードを IDLE にしているのは、描画アイコンが選択されたら、それまでのユーザ操作を中止する意図がある。
3-4 マウスボタンを押す
 マウスボタンが押されたときに必要な処理を次に示す。
 1.描画コマンドを取得する
 2.座標を記録する
 3.操作モードを BEGIN に遷移
 2と3はラバーバンドを表示するための準備である。イベントハンドラは下記のようになる。
%code
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (mode == Mode.IDLE)
{
rb = get_shape(cmd);
rb.coodinate.Add(new Point(e.X, e.Y));
rb.coodinate.Add(new Point(e.X, e.Y));
mode = Mode.BEGIN;
}
}
}
 ラバーバンドはこれからユーザが描く図形と同じ形状にしたい。get_shape() はそのための関数で、最後に押された描画ボタンの種類を引数に渡すとその図形オブジェクトを作って返す。
%code
private Shape get_shape(Cmd cmd)
{
switch(cmd)
{
case Cmd.LINE: return new Line();
case Cmd.BOX: return new Box();
case Cmd.OVAL: return new Oval();
default: return new Box();
}
}
 3-5 マウスをドラッグする
 ドラッグ中はラバーバンドを表示する。操作モードは変更しない。
%code
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (mode == Mode.BEGIN)
{
pictureBox1.Refresh();
rb.coodinate[1] = new Point(e.X, e.Y);
rb.draw_rubber(graphic);
draw_all_shapes();
}
}
 ラバーバンドはドラッグ操作に追従する必要があるため、このイベントハンドラで消去→描画を繰り返すことで表示している。ラバーバンドを消去するためには、すでに描画されている図形も含め一旦すべて消去する必要がある。その後 draw_all_shapes()が、それらを再描画する。
%code
private void draw_all_shapes()
{
foreach(Shape sh in shapes)
{
sh.draw(graphic);
}
}
なお、pictureBox1.Refresh() は描画領域を再描画する .NET Framework が提供するメソッドである。
3-6 マウスボタンを離す
 ユーザがマウスボタンを離したとき、以下のような処理が必要となる。
 1.それまで表示していたラバーバンドを消す
 2.ユーザの描画図形として登録
 3.登録した図形を再描画
 4.次のユーザ操作に備えるため、操作モードを IDLE にする
 これをイベントハンドラに記述すると次のようになる。
%code
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
if(mode == Mode.BEGIN)
{
if(cmd != Cmd.SELECT)
{
rb.coodinate[1] = new Point(e.X, e.Y);
shapes.Add(rb);
}
mode = Mode.IDLE;
}
pictureBox1.Refresh();
draw_all_shapes();
}
3-7 まとめ
 ここまでで、お絵かきツールとしての一通りの機能を実装したことになる。
実際にビルドしてみると Windows アプリケーションとして動作することが分かる。
 冒頭で述べたとおり、お絵かきツールは .NET Framework が提供する Form クラスを元に作られており、アプリケーションとしてのほとんどの処理はそこで実行されている。そのため、アプリケーションの設計者としては、Form が処理しないアプリケーション固有の処理だけを記述するだけでよい。さらに、作画等のアプリケーション固有の処理も .NET Framework が提供するメソッドを効果的に呼び出すだけで済ませられる場合が多いことが分かった。
4 論議
 今回は、簡単なアプリケーションを書いてみる事で C# の簡便さを評価できたと思う。実際、筆者は C# についてはほとんど知らない状態から入門書を片手に始めたが、プログラミングにかけた時間は多く見ても8時間足らずであった*。
 C# にはここでは挙げていない機能が多くあり、それらを使うことでよりシンプルにコードを記述できる。例えば、C# バージョン3.0で導入された Func という標準のジェネリックデリゲートを用いると、描画コマンドの選択処理と図形ごとの描画メソッドはより短く書き換えることができると思う。
 C# が短い期間でバージョンアップを繰り返していることから、今後も C# の簡便さはより高まっていくと思われる。
* ソースコード(Form1.cs)は 250行程度となった。この行数は Visual C# のGUIデザイナが自動生成したコードを含んでいる。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace WindowsFormsApplication1
{
public static class Util
{
public static void write_file(List<Shape> shapes, string path)
{
FileStream f = new FileStream(path, FileMode.Create, FileAccess.Write);
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(f, shapes);
f.Close();
}
public static List<Shape> read_file(string path)
{
FileStream f = new FileStream(path, FileMode.Open, FileAccess.Read);
BinaryFormatter formatter = new BinaryFormatter();
Object obj = formatter.Deserialize(f);
f.Close();
return obj as List<Shape>;
}
public static Rectangle get_rect(List<Point> paired_point)
{
Point lu = new Point();
Size sz = new Size();
Point p1 = paired_point[0];
Point p2 = paired_point[1];
lu.X = Math.Min(p1.X, p2.X);
lu.Y = Math.Min(p1.Y, p2.Y);
sz.Width = Math.Abs(p1.X - p2.X);
sz.Height = Math.Abs(p1.Y - p2.Y);
return new Rectangle(lu, sz);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment