PPPUC++#15

13. Graphics Classes

"A language that doesn't change the way you think isn't worth learning."
逆に言うと僕たちは自分が考えていると思っているけど、本当は言語こそが主であり、僕たちは寄生された哀れな生き物なのかも。。。。こわー、、、、、、いけどそれでいいや。
この章は各クラスについて設計や実装をみていきます。

13.1 Oerview of graphics classes

  • グラフィックスやGUIライブラリというのは巨大になりがち
    • 数百ものクラスや何十個もの関数とか
    • マニュアルが古臭い植物学の教科書みたいに曖昧な分類項目で埋め尽くされてて、、、
      • ねむい
  • しかしエキサイティングでもあります。機能を見ていくと結構きゃーきゃーできるくらいに
  • ただ、どっから始めたら良いものなのか、なのですよね
  • 本書で使ってるラッパライブラリはそんなあなたのための緩衝材
    • ほぼ全ての操作が、2ダース程度のクラスで可能
      • ものすごいドヤ顔がみえる気がします。。。
  • グラフィックスやGUIに必要な主要な概念を抽出しているので、学習にピッタリ。効率的に概念を吸収できます
      • またしてもドヤ顔がみえる気がします。。。
  • 14章を終える頃にはコンセプトも理解できる予定
    • 他のツールでもきっと大丈夫
  • 中心となるI/Fクラスは以下のものです
I/Fクラス 説明
Color 線、テキスト、塗りつぶし色
Line_style 線の描画に使用
Point スクリーンやウィンドウ上の場所を表す
Line 線分。2つのPointからなる
Open_polyline 連結されたPointのつらなり
Closed_polyline Open_polylineと同様で更に最後のPointが最初のPointと連結したもの
Polygon 交差の無い Open_polyline
Text 文字列
Lines 複数の線分の集まり
Rectangle 矩形
Circle
Ellipse 楕円
Function 任意の1変数関数
Axis ラベル付の座標軸
Mark 文字でマークされた点
Marks Markの集まり
Marked_polyline 各点がMarkであるようなOpen_polyline
Image ファイル中のビットマップ画像
  • 15章では更にGUI部品となるようなものを扱います
I/Fクラス 説明
Window ウィンドウ
Simple_window Nextボタンによる紙芝居に対応したWindow
Button ボタン
In_box ユーザからの入力用の領域(テキストボックス)
Out_box プログラムからの出力用の領域
Menu Buttonのvectorアレイ
  • モジュール設計とかは略
  • ShapeやWidgetを取って置くための便利クラスも用意しています
Vector_ref 無名オブジェクトに便利なvector
  • この章はあまり急いで進むべからず。
    • この章の目的は奇麗な絵を見せる事にあらず。
    1. コードと生成される絵の対応
    2. コードリーディングに慣れ、コードがどう動くかを考えるのに慣れてもらう
    3. コード設計を考えてもらう。特にアイディアをクラスに落とし込む方法. どうしてこうすると上手く行くのか、とかどんな選択がでてくるのか、とか。
  • てなわけでゆっくり着実に。

13.2 Point and Line

  • 点、それは基本
    • 点を定義するというのは、幾何空間をどう考えるか、というのを決めてしまうこと
  • ここで使っているのは良くある(x,y)の対で表す方法(12.5にて説明済み)
  • Pointは単に int の対を保持するのみ。あとはコンストラクタだけ
struct Point { 
  int x,y;
  Point(int x, int y) : x(x), y(y) { }
  Point() : x(0), y(0) { }
};
  • あと等価性の演算子 == , != 。これは単に 二つのベクトルの各対が a.x == b.x && a.y == b.y なら trueというやつ
      • != は !(==) で表しててぐっとー DRYDRY
  • Line等の具体的な図形は抽象クラス Shapeを継承する、という良くあるパターンになっている
struct Line : Shape { 
  Line(Point p1, Point p2);
} ;
  • こう書くことで「LineはShapeの一種です」という表明に
    • Shapeはベースクラスとか、単にベースとか(親クラスとも)
    • 詳細は後の章で。とりあえず Shapeは Lineを定義するのに便利な要素が入っているというイメージです
  • LineはPointを2つ用意して作ります

13.3 Lines

  • つ、つまらん技だと(ry (もういいって...)
  • まあ一本だけってのもあんまり無いかと
    • グルーピングできます、という機能
  Lines x;
  x.add(Point(xx1,yy1), Point(xxx1,yyy1))
  x.add(Point(xx2,yy2), Point(xxx2,yyy2))
  • のような感じに連ねていく
  • Line をたくさん使えばいいじゃん ー>
    • <ー 複数の線をいっぺんに一塊で扱います、という表明になります
    • 例えば、全部の色を変えるとかの時に便利
      • 正直それなら Containerクラスを作る方が好み。線だけじゃなくいろんなオブジェクトにそうできるし。
      • あ、そうか、「オブジェクトは画面に表示されるものと一対一対応している」っていう表明と矛盾してしまうのか
  • gridを表示する例として出ている.
      • 一本一本の線に名前付きオブジェクトってバカバカしす。なるほどなあ
  • Lines::add()はベースクラスのadd()を使って2点を登録するだけ
    • この際、ベースクラスの add()は Shape::add()と修飾する必要あり
  • draw_lines()が描画コールバック(次の章でやります)
    • 実装としては2点ずつ拾っていって(FLTKの場合)fl_line()で線を引いていってるだけ
void Lines::draw_lines() const
{
  if (color().bisibility())
    for (int i = 1; i< number_of_points(); i+=2)
      fl_line(point(i-1).x, point(i-1).y, point(i).x, point(i).y);
}
      • ぎゃーす、最後きちゃない
    {
      const Point &from = point(i-1), &to =point(i);
      fl_line(from.x, from.y,  to.x, to.y);
    }
      • しませんのこと? (冗長かな?) 個人的にはforチェックに-1が入っても良いからfrom=cur-1ではなくてto=cur+1な概念で書きたい気もします
    • color().visibility()で現在設定されているカラーのアルファがとれるのでこれで描画の要不要判定
    • 登録されている点が偶数であることのチェックは明確だからいらん、とゆうてるけど・・・
      • ここはassert()いれといた方が良いんじゃ? 明確な invariantだと思う。コンパイル時外せば良いんだし, というのが tkuro的意見
  • コンストラクタは定義しない。空っぽで初期化、後からadd()のほうが柔軟でいいじゃん、しかも、それ以外の使いかたってまず現実にはないヨ、というご意見。
      • これ自身はまあどっちとも言えないんだけど、次の台詞はシビれた
  • あやしい、と感じたらその新機能を追加してはイケナイ
    • 機能の追加は必要になったらいつでもできるが、既に使っている機能の削除は大変困難

13.4 Color

  • Colorはこの場合単純に FLTK1.0の FL_Colorをラップしたもの
  • enumにてsymbolic nameを規定している。これによってColorクラスの名前空間に色名を閉じ込めています
  • この他にさっきもちらっと出てきた透明度(アルファ)をパックしています
  • 色を選ぶためにはいくつかの方法があります
    1. enumによる名前付きカラー、Color::dark_blueみたいな感じ
    2. パレットカラー(0〜255) 。これはFLTK1.0で規定されている下のようなパレット
    3. RGBはここではせつめいしませぬ。Exerciseであるらしいけど


  • コンストラクタはColor_typeか intかどちらかを引数にとります。内部カラーは メンバの c に入れます。
    • 名前が短い、衝突するぞって? いいんです。ここでしか使わないし、外には見えない privateなので。これがprivateの力!
  • cは実装である所の Fl_Colorをユーザから隠蔽しています
      • そしてFl_ColorはウィンドウシステムやOSの色空間を隠蔽してる、と( X11とか Quartzとか W32とか)。タマネギの皮みたい
  • 透明度はメンバ vとして定義しています。これも 0〜255
    • あと定数メンバとして Color::visible( = 255), Color::invisible( = 0)を用意
    • invisibleカラーってなんにつかうのー? と思うかもしれないけど便利なんだなー、とかいう達観者視点

13.5 Line_style

  • 色だけじゃないよ線種もだよ、という話
    • 点線とかね
  • まあ基本的には Colorと同じようなクラス。特記事項あんまなし
    • 実は FLTK1.0では線種をintで表しててださいのでこれをラップしているなんて先進的
      • むろん新しいバージョンの FLTKではそんな事はありませぬ(カラーも古いFLTKは pseudo color時代のだったので大変古くさいですニ)
  • 大抵は線種なんて気にする必要も無い
    • なんも指定しないでも、結構イケテル線が引けてるのは デフォルトコンストラクタのお陰だぜ、とか
  • Line_styleは 線種と太さの指定ができます
      • そうか、まだデフォルト引数自体は出てきてないのか・・・
  • デフォルトの太さは1です!!!!!
    Line_style(Line_style_type ss) :s(ss), w(0) { }
    • のあーー
      • まあ、0のときシステムのデフォルト(大抵は =1 + antialias)なので間違っちゃあいないのですが・・・探究心の強い人がソース見たらビックリする罠が
  • あとここで言うの微妙だけど、さっきのLinesだとグルーピングしちゃうのでここの部品の色分けとかできないよーとかいう話してます。なんのこっちゃ

13.6 Open_polyline

  • 連結線成分です
  • 余談ですが poly とはギリシア語で manyという意味だそうです
      • 知っとるわ、という声が聞こえてきそうです
  • 下記のようにしててですね、
struct Open_polyline : Shape {
  void add(Point p){ Shape::add(p); }
};
    • なんよ? なぜな? なぜ まったく一緒な事するのにオーバーライドしてんのよ、とか言うと
class Shape {
 :
protected:
  void add(Point p) ;
};
    • だからだったりします
      • どうなんよこれ
      • ちなみにさらに悲劇は続きます
  • この後に"We don't even need to define a draw_lines() because Shape by default interprets the Points add()ed as a sequence of connected lines."(まあようは「draw_lines()はいらないよー。Shapeのデフォルト実装と一緒だからー」)とかほざいてる書いてるのですが、、、
    • Graph.hを見ると・・・
struct Open_polyline : Shape {         // open sequence of lines
    void add(Point p) { Shape::add(p); }
    void draw_lines() const;
};
      • ........orz
      • まあそりゃそうなんだよね、だってpolylineはClosedで無くても内部をfillできるのでそのコードが必要なのですよね
      • 実際、外側のLine描画には Shape::draw_lines()しているだけなので、全く嘘でも無いのですが。。ねえ
      • きっと教育的配慮なんだようんまちがいないよそうだようん
    • おそらくは最初はOpen_polylineにはfillが無かったんじゃないかと妄想。それなりにいいかげん細部にこだわらず大局を見るMr.Sのことなので、そのときの記憶のまま説明書いたか、または本文書いた後で思いついてコードを直してしまい個体ロケットブースター切り離し仕様になったとかそういう・・・

|

13.7 Closed_polyline

  • さっきのヤツに最後の終点と視点をつなぐコードを継承で足しただけ
  • というわけで実際その通りのコードになっています
  • FLTKの素の部分を隠蔽してるので入れ替えし放題! ということを言いたいそうな
      • visibility() == false の場合も Open_polyline::draw_lines()呼んでるのがダブルチェックでなかなかに赴き深いんだけど、ひょっとしたらあるのかもしれない。visibility()が無くても表示させなきゃな図形が!(ねーよ)。うーんどっちがいーのか僕にもわかりません
void Closed_polyline::draw_lines() const
{
  Open_polyline::darw_lines(); // first draw the "open polyline part"
  // then draw closing line;
  if (color().visibility())
    fl_line( .../* start/end point */ ...);
}

13.8 Polygon

  • Closed_polylineと何が違うっ!?
    • 各辺が交わらないのが新鮮、とのこと 以上
  • どうかく?
    • 今言ったじゃん、とかいう一人コント状態
  • 要するにintersect_line_segmentするかどうか、というだけの話な感じ
    • いらんおせっかい?
      • 特に説明無いけど、equality checkと代入間違えちゃった!( == or = )の回避策がひっそりとさりげなく入ってたり。Mr.Sの性格的にここは自慢説明するかと思ってました
    • intersect_line_segment()自体は、良くある媒介変数を使った線分の交差判定式そのままです
  • いらんおせっかいは、なかなかのパワー食いで、O(N2)だよとか
  • しかもそれだけでは「ポリゴンです」と言い切るのは不十分で、例えば2点以下のポリゴンとかありえへん、という話
    • ポリゴンの普遍条件が、困った事にポリゴンのすべての点がわかるまでわからない、というのが原因
  • 実はこれに関する問題がまだあって、(1)「全部一直線上に並んでしまった」と(2)「前の点と同じ場所」というケース
    • これ実地では微妙な気が。。。
      • (1)コード中では全ポイントを調べるのは重過ぎ、という判断からか直前の2点との関係だけ調べてるので、例えばマウスクリックで順に点を作る場合に一回でも直前の辺とparallelなだけではねられてしまう
      • (2)にも同様な問題が。

13.9 Rectangle

  • 最も基本的な図形らしい
      • 点は・・・
  • 大抵のGUIシステムでは、良く使う、ということと、実装の容易性から、多角形描画とは別に矩形描画機能を持たせる場合が多い
      • というかたいていはハードウェアで持ってますね
  • コンストラクタは二点指定(対角線)か、視点+(縦、横)かのどちらか
    • sanity checkもしてますよ、とか
    • fill_color指定なしだと透明になりますよ、とか
  • move()もできます
      • なぜresizeつけなかった? なぜだベン!
  • ついでにここでreorderingについて書いてる
    • 基本的にはテーブルに紙を置いていくように後から書いたものが上、という説明方法。いわゆるペインターズアルゴリズムですね
    • Window::put_on_top(Shape&)で一番上に持っていく事もできるみたい。これはおそらくリニアサーチして見つかったヤツをvectorの一番後ろにもっていくのですね。。。ってその通りだった (>>Window.cpp)
      • 見つからなかった場合はスルー
  • あとは外枠を書かなくするには Color::invisibleを指定して、とか言う話
    • FLTK内部では fl_rectf()(塗りつぶし)、fl_rect()(枠だけ)と言う風にAPIが分かれてるらしいですが、まあ些末な事ですね。Mr.S的にはこれがmessyらっしー