2017年12月アーカイブ

第16章練習問題4

| コメント(0)

 GUIの基本として、ボタンをクリックした時の動作をコールバックで実装する方式を勉強。コールバック関数を登録する際のラムダ式は、まだ記述方法がよくわかっていない。真似をしている段階だ。
 練習問題4は、以下の通り。

 円、正方形、正三角形、六角形をそれぞれ作成するアイテムが含まれたメニューを作成する。座標を指定するための入力ボックスを(1つまたは2つ)作成し、メニューアイテムを押すことによって作成された図形をその座標に配置する。ドラッグ&ドロップはサポートしない。

 メニューを作って、それぞれのボタンから図形を描画する関数を呼べばいいだけだ、と思った。確かにその通りなのだが、それが結構やっかいであった。17.2.9の手法を借用した。
 さらに、円、正方形は、CircleおよびRectangleを使えばいいが、正三角形、六角形はライブラリにないので、Closed_polylineでinitializer_listを使えるようにした関数NClosed_polylineを作って、作成するようにした。とりあえず、動いたのが、下記のコードである。

#include "Simple_window.h"
#include "Graph.h"

struct Draw_menu :Window {
    Draw_menu(Point xy, int w, int h, const string& title);
    Menu shape_menu;

private:
    const int size = 50; //Size of Shape
    Shape* p;
    In_box posx;
    In_box posy;
    void circle_pressed();
    void square_pressed();
    void triangle_pressed();
    void hexagon_pressed();
    void quit_pressed();
};

Draw_menu::Draw_menu(Point xy, int w, int h, const string& title)
    :Window{ xy, w, h, title },
    posx{ Point{x_max() - 310,0},50,20,"x:" },
    posy{ Point{x_max() - 210,0},50,20,"y:" },
    shape_menu{ Point{x_max() - 100,0},70,20,Menu::vertical,"shape" }
{
    attach(posx);
    attach(posy);
    shape_menu.attach(new Button{ Point{0,0},0,0,"circle",
        [](Address, Address pw) {reference_to<Draw_menu>(pw).circle_pressed(); } });
    shape_menu.attach(new Button{ Point{ 0,0 },0,0,"square",
        [](Address, Address pw) {reference_to<Draw_menu>(pw).square_pressed(); } });
    shape_menu.attach(new Button{ Point{ 0,0 },0,0,"triangle",
        [](Address, Address pw) {reference_to<Draw_menu>(pw).triangle_pressed(); } });
    shape_menu.attach(new Button{ Point{ 0,0 },0,0,"hexagon",
        [](Address, Address pw) {reference_to<Draw_menu>(pw).hexagon_pressed(); } });
    shape_menu.attach(new Button{ Point{ 0,0 },0,0,"quit",
        [](Address, Address pw) {reference_to<Draw_menu>(pw).quit_pressed(); } });
    attach(shape_menu);
}

void Draw_menu::circle_pressed() {
    int x = posx.get_int();
    int y = posy.get_int();

    p = new Circle{ Point{ x + size/2, y + size/2}, size/2 };
    attach(*p);
    redraw();

}

void Draw_menu::square_pressed() {
    int x = posx.get_int();
    int y = posy.get_int();

    p = new Graph_lib::Rectangle { Point{ x , y }, size, size };
    attach(*p);
    redraw();

}

struct NClosed_polyline :Closed_polyline {
    NClosed_polyline(initializer_list<Point> lst){
        for (auto p:lst) Shape::add(p); }   
};


void Draw_menu::triangle_pressed() {
    int x = posx.get_int();
    int y = posy.get_int();
    const int h = static_cast<int>(sqrt(2.0) / 2.0 * size);

    p = new NClosed_polyline{
        Point{x + size / 2, y + (size - h) / 2 },
        Point{x + size,     y + (size + h) / 2 },
        Point{x,            y + (size + h) / 2 }};

    attach(*p);
    redraw();
}

void Draw_menu::hexagon_pressed() {
    int x = posx.get_int();
    int y = posy.get_int();
    const int h = static_cast<int>(sqrt(3.0) / 2.0 * size);

    p = new NClosed_polyline{
        Point{x + size / 4,     y + (size - h) / 2 },
        Point{x + size * 3 / 4, y + (size - h) / 2 },
        Point{x + size,         y + size / 2 },
        Point{x + size * 3 / 4, y + (size + h) / 2 },
        Point{x + size / 4,     y + (size + h) / 2 },
        Point{x,                y + size / 2 } };

    attach(*p);
    redraw();
}

void Draw_menu::quit_pressed() {
    hide();
}


int main()
{
    using namespace Graph_lib;

    try {
        Draw_menu win{ Point{100,100}, 600, 400, "Draw Shape" };
        return gui_main();
    }

    catch (exception& e) {
        cerr << "exception: " << e.what() << endl;
        return 1;
    }

    catch (...) {
        cerr << "Some exception" << endl;
        return 2;
    }
}

第15章練習問題5

| コメント(0)

 この章で、関数をグラフ化する方法とか、ラムダ式の書き方とかを勉強。練習問題5をやることにした。

1-1/3+1/5-1/7+1/9-1/11+・・・をアニメーションにする。これは「ライプニッツの級数」と呼ばれ、π/4に収束する。

 軸の描き方とか、いろいろ面倒な用意をする必要がある。πの値は、#define _USE_MATH_DEFINESを宣言しておくと、M_PIで値が定義されているようである。
 アニメーションといっても、単一の値が収束していくだけである。級数の添え字をnとし、nの値をX軸にして、その時の級数の値をy軸に表示するような構成にしてみた。あまり面白い表示にはなっていないが、一応、私の作った例は下記の通り。


#define _USE_MATH_DEFINES
#include "Simple_window.h"
#include "Graph.h"

double leibniz(int n) {
    double r = 1;
    while (n > 0) {
        if (n % 2 == 0) r += 1.0 / (2.0 * n + 1.0);
        else r -= 1.0 / (2.0 * n + 1.0);
        --n;
    }
    return r;
}

 

int main()
{
    using namespace Graph_lib;

    constexpr int xmax = 600;
    constexpr int ymax = 400;

    constexpr int margin = 40;

    constexpr int xlength = xmax - margin;
    constexpr int ylength = ymax - margin;

    constexpr int x_orig = margin;
    constexpr int y_orig = ymax- margin;
    const Point orig{ x_orig,y_orig };

    constexpr int nmax = 50;

    constexpr double x_scale = xlength / nmax;
    constexpr double y_scale = (ylength-margin) / 5.0 * 4.0; //縦軸を0.25刻みで5個分取る

    Simple_window win{ Point{100,100},xmax,ymax,"Functopn graphing" };

    Axis x{ Axis::x,Point{ margin,y_orig },xlength,nmax,"one notch = 1" };
    Axis y{ Axis::y,Point{ x_orig,ylength },ylength - margin,5,"one notch =0.25" };
    x.set_color(Color::red);
    y.set_color(Color::red);
    win.attach(x);
    win.attach(y);

    Function pi4([](double x) {return M_PI / 4;}, 0, nmax, orig, 200, x_scale, y_scale); // pi/4
    pi4.set_color(Color::blue);
    win.attach(pi4);

    for (int n = 1;n < 50;++n) {
        ostringstream ss;
        ss << "Leibniz; n=" << n;
        win.set_label(ss.str());

        double y;
        int xp,yp;

        xp = x_orig + n*x_scale;

        y = leibniz(n);
        yp = ylength - y*y_scale;

        Circle l{ Point{ xp, yp },2 };

        win.attach(l);

        win.wait_for_button();

        win.detach(l);
    }

}

 プログラムそのものよりも、ボタンを押す都度、画面が小さくなっていくという現象があって、解決に時間を取ってしまった。c++ - Stroustrup's "Simple_window" shrinks when pushing "Next" button - Stack Overflowで、ある環境でのみ生じるFLTKのバグであることがわかった。この記事では、FLTK1.3.3で解決済みとあったが、私の環境では、それでもだめで、1.4.2をダウンロードし、環境を再構築して、やっと動かすことができた。この手の問題は、勉強の本質の部分よりも時間が必要でやっかいだ。

第14章:ドリル1~4

| コメント(0)

 グラフィックスというよりは、C++の機能の解説の章である。なかなか読みごたえがあった。練習問題は、あまり面白そうな問題はないので、まじめにドリルをやることにした。
 まず、ドリル1~3の問題は下記の通り。

1.仮想関数vfと非仮想関数fを持つクラスB1を定義する。これらのクラスをB1クラスの内部で定義し、それぞれの名前(”B1::vf()”など)を出力するように実装する。これらの関数はpblicで宣言する。B1オブジェクトを作成し、これらの関数を呼び出す。
2.B1の派生クラスとしてD1を作成し、vf関数をオーバーライドする。D1オブジェクトを作成し、そこでvf関数とf関数とを呼び出す。
3.B1への参照(B!&)を定義し、それを作ほど定義したD1オブジェクトとして初期化する。その参照を使ってvf関数とf関数を呼び出す。

 問題文だけ読むと何が書いてあるのか、よくわからない。14.3.3の例題を参考に、作ったのが、以下の通り。

#include "..\..\std_lib_facilities.h"

struct B1 {
    virtual void vf() const { cout << "B1::vf()" << endl; }
    void f() const { cout << "B1::f()" << endl;; }
};


struct D1:B1 {
    void vf() const override { cout << "D1::vf()" << endl; }

};


int main() {
    B1 b1;
    D1 d1;

    b1.vf();
    b1.f();

    d1.vf();
    d1.f();

    B1& b{ d1 };
    b.vf();
    b.f();
}

 この時の出力結果は、

image

 

 ドリル4の問題は、以下の通り。

4.D1にfという関数を定義し、問題1~3を繰り返し、その結果を説明する。

 ほとんど、問題1~3と同様のコードになる。

#include "..\..\std_lib_facilities.h"

struct B1 {
    virtual void vf() const { cout << "B1::vf()" << endl; }
    void f() const { cout << "B1::f()" << endl;; }
};


struct D1 :B1 {
    void vf() const override { cout << "D1::vf()" << endl; }
    void f() const { cout << "D1::f()" << endl;; }
};


int main() {
    B1 b1;
    D1 d1;

    b1.vf();
    b1.f();

    d1.vf();
    d1.f();

    B1& b{ d1 };
    b.vf();
    b.f();
}

 出力は、

image

 

 ドリル1~3では、クラスD1にはf()を定義していないので、d1.f();では、B1で定義されたf()を実行する。一方、ドリル4では、D1でf()を定義したので、d1.f();ではD1で定義したf()が実行されている。

このアーカイブについて

このページには、2017年12月に書かれた記事が新しい順に公開されています。

前のアーカイブは2017年11月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

ウェブページ

  • about
Powered by Movable Type 6.3.6