marunomaruno-memo

marunomaruno-memo

[C][C++] 2 次元配列を返り値に持つ関数

2011年10月28日 | C / C++
                                                                    2011-10-28
2 次元配列を返り値に持つ関数
================================================================================

サンプルコードは C++ 言語で書かれているが、C 言語でも同じ。

サンプルの関数 addTo() は、2 次元配列(正方行列)に対して、擬似コード的に記すと
    a += b;
を行う関数。


□ sample.cpp
---
#include <iostream>
#include <iomanip>
using namespace std;

int (*addTo(int a[][2], int b[][2], int size))[2];    // 2次元配列を加算する (1)
void print(int a[][2], int size);

int main() {
    int a[2][2] = {{1, 2}, {3, 4}};
    int b[2][2] = {{5, 6}, {7, 8}};
    print(a, 2);
    cout << "--------" << endl;
    print(b, 2);

    int (*c)[2] = addTo(a, b, 2);    // (2)
    cout << "========" << endl;
    print(c, 2);

    return 0;
}

int (*addTo(int a[][2], int b[][2], int size))[2] {
    for (int i = 0; i < size; ++i) {
        for (int j = 0; j < size; ++j) {
            a[i][j] += b[i][j];
        }
    }
    return a;
}

void print(int a[][2], int size) {
    for (int i = 0; i < size; ++i) {
        for (int j = 0; j < size; ++j) {
            cout << setw(4) << a[i][j];
        }
        cout << endl;
    }
}
---

□ 実行結果
---
   1   2
   3   4
--------
   5   6
   7   8
========
   6   8
  10  12
---


(1) 2 次元配列を返す関数のプロトタイプ宣言

    int (*addTo(int a[][2], int b[][2], int size))[2];    // 2次元配列を加算する
 

通常の2次元配列を指すポインター変数の定義はつぎの(2)のように、
    int (*c)[2]
のようになる。この場合のポインター c は、int[2] のサイズを持つ型を指すことになる
ので、結果として 2 次元配列へのポインターと考えられる。

こう考えると、返り値として int[2] を持つ関数としては、
    int (*x)[2]
のようになり、x 部分が関数のシグネチャー部分とするとよい。すなわち、上記のような
表現になる。


(2)    2 次元配列の宣言

    int (*c)[2] = addTo(a, b, 2);    // (2)



■ 関数ポインターにしてみる

次のようにできる。
関数名の部分を
    (*関数ポインター名)
に置き換えるだけである。
ただし、これだとなんだかよくわからなくなるので、あまり記述しないほうがよさそう。
それ以前に、2 次元配列を返す関数を作るくらいなら、構造体に 2 次元配列を入れて扱
ったほうが記述としてはよいと思う。

---
        :
    int (*(*fp)(int a[][2], int b[][2], int size))[2];    // 関数ポインターを定
義する
    fp = addTo;    // 関数ポインターにアドレスを設定する

    int (*c)[2] = fp(a, b, 2);
        :
---




[Android] グラフィックス (1)

2011年10月16日 | Android
最終更新 2012-01-13
グラフィックス (1)
================================================================================

画面に図形を描画する。
ここでは、コンテント・ビューとして res/layout を使うのではなく、クラスを使って行
う。

基本的なクラスのつくりは以下のとおり。

・描画するクラスは、View クラスを継承して作る
・onDraw(Canvas canvas) をオーバーライドして、そこに描画する
・描画するメソッドは Canvas クラスのメソッドを使う
・描画に使う図形の属性は Paint オブジェクトを使う


■ 円、文字列、ビットマップ画像を描画する

円や文字列、ビットマップ画像を描画するには、View のサブクラスを作り、そのオブジ
ェクトを Activity に設定する。

つぎの Graphics01Activity.java は、プロジェクトによりクラス名が違うだけで、すべ
て共通で使用している。


□ Graphics01Activity.java
---
package jp.marunomaruno.android.sample;

import android.app.Activity;
import android.os.Bundle;

public class Graphics01Activity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GraphicsView v = new GraphicsView(this);    // (1)
        setContentView(v);    // (2)
    }
}
---

(1)(2) グラフィックスを設定しているクラスのオブジェクトを生成して、それをアクティビティに設定する。

    GraphicsView v = new GraphicsView(this);    // (1)
    setContentView(v);    // (2)

自分で作ったビューを設定するので、今回は、res/layout/main.xml を使うことはない。


□ GraphicsView.java
---
package jp.marunomaruno.android.sample;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;

/**
 * 円、文字列、ビットマップ画像を描画する。
 * @author marunomaruno
 * @see Graphics01Activity
 */
public class GraphicsView extends View {    // (1)

    public GraphicsView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public GraphicsView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public GraphicsView(Context context) {
        super(context);
    }

    @Override
    protected void onDraw(Canvas canvas) {    // (2)
        // ペイントオブジェクトを設定する
        Paint paint = new Paint();    // (3)
        paint.setColor(Color.WHITE);    // (4)
        paint.setAntiAlias(true);    // (5)
        
        // 円を描画する
        paint.setColor(Color.BLUE);
        canvas.drawCircle(100, 100, 50, paint);    // (6)

        // 文字列を描画する
        paint.setColor(Color.YELLOW);
        paint.setTextSize(48);    // (7)
        canvas.drawText("アイスクリーム", 50, 300, paint);    // (8)
        
        // ビットマップ画像を取得する
        Bitmap bitmap = BitmapFactory.decodeResource(
                getResources(), 
                R.drawable.icon);    // (9)

        // 画像を描画する
        canvas.drawBitmap(bitmap, 50, 400, paint);    // (10)
    }
}
---

(1) GraphicsView クラスを定義する

    public class GraphicsView extends View {    // (1)

View クラスを継承して作る。View クラスは、独自に図形などを描画するクラスを作ると
きに使用するクラス。描画する場合は、onDraw(Canvas) メソッドをオーバーライドする。


□ View クラス

画面に表示やレイアウトするクラスのスーパークラス。

・クラス階層

java.lang.Object
   +  android.view.View


・主なメソッド
---
void  onDraw(Canvas canvas) 描画が行われるときに呼び出される
---


(2) 描画するためのメソッドをオーバーライドする

    protected void onDraw(Canvas canvas) {    // (2)

このメソッドに、描画したい要素(図形、文字列、画像)を設定する。


□ Canvas クラス

描画対象の図形を描画して置いておくオブジェクトのクラス。
このクラスのメソッドを使って、図形、文字列、画像を描画する。
このとき、描画に使うペンの役割を、Paint オブジェクトが担当する。

・クラス階層
java.lang.Object
   +  android.graphics.Canvas

・主なメソッド
---
void  drawArc(RectF oval, float startAngle, float sweepAngle, 
                                    boolean useCenter, Paint paint)    円弧
void  drawBitmap(Bitmap bitmap, float left, float top, Paint paint)    画像
void  drawCircle(float cx, float cy, float radius, Paint paint)    円
void  drawLine(float startX, float startY, float stopX, float stopY, 
                                                        Paint paint)    線分
void  drawLines(float[] pts, Paint paint)    いくつかの線分
void  drawOval(RectF oval, Paint paint)    楕円
void  drawPath(Path path, Paint paint)    多角形
void  drawPoint(float x, float y, Paint paint)    点
void  drawPoints(float[] pts, Paint paint)    いくつかの点
void  drawRect(float left, float top, float right, float bottom, 
                                                        Paint paint)    四角形
void  drawText(String text, float x, float y, Paint paint)    文字列
---


(3)-(5) ペイントオブジェクトを設定する

    Paint paint = new Paint();    // (3)
    paint.setColor(Color.WHITE);    // (4)
    paint.setAntiAlias(true);    // (5)


setColor() メソッドは、ペイントの色を設定する。
色は、Color クラス(android.graphics.Color)の定数を使用する。つぎの定数がある。
---
int  BLACK     
int  BLUE     
int  CYAN     
int  DKGRAY     
int  GRAY     
int  GREEN     
int  LTGRAY     
int  MAGENTA     
int  RED     
int  TRANSPARENT     
int  WHITE     
int  YELLOW     
---

これ以外の色については、RGB を直接指定する。次のような、rgb() メソッドを利用する。

    paint.setColor(Color.rgb(red, green, blue));


□ Paint クラス

Paint クラスは、図形描画する際のペンの役割を持つ。
ここで、ペンの色や太さを設定して、描画するときに指定する。


・クラス階層
java.lang.Object
   +  android.graphics.Paint

・内部クラス
---
enum   Paint.Align           drawText() での文字列の境界
enum   Paint.Cap             線分の端の形状 
class  Paint.FontMetrics     文字列の情報 
class  Paint.FontMetricsInt  文字列の情報
enum   Paint.Join            線と線の結合具合
enum   Paint.Style           線分のみか内部も塗りつぶすかの情報
---

・主なメソッド
---
void  set(Paint src)
void  setARGB(int a, int r, int g, int b)
void  setAlpha(int a)
void  setAntiAlias(boolean aa)
void  setColor(int color)
---


(6) 円を描画する

    canvas.drawCircle(100, 100, 50, paint);    // (6)

円を描画するのは、Canvas クラスの drawCircle() メソッドを使う。
    void drawCircle(float cx, float cy, float radius, Paint paint)

AWT の描画するメソッドとは違い、中心 (cx, cy) と半径 radius を指定する。
また、塗りつぶすかどうかも、paint オブジェクトがその情報を持つので、メソッドを切
り替える必要はない。


(7)(8) 文字列を描画する

    paint.setTextSize(48);    // (7)
    canvas.drawText("アイスクリーム", 50, 300, paint);    // (8)

文字列を描画するのは、Canvas クラスの drawText() メソッドを使う。
    void drawText(String text, float x, float y, Paint paint)

文字列の開始点は、座標 (x, y) からで、ここが文字列の下になる。


・文字列の属性を設定する関係の Paint クラスのメソッド
---
void     setStrokeCap(Paint.Cap cap)
void     setStrokeJoin(Paint.Join join)
void     setStrokeMiter(float miter)
void     setStrokeWidth(float width)
void     setStyle(Paint.Style style)
void     setSubpixelText(boolean subpixelText)
void     setTextAlign(Paint.Align align)
void     setTextScaleX(float scaleX)
void     setTextSize(float textSize)
void     setTextSkewX(float skewX)
Typeface setTypeface(Typeface typeface)
void     setUnderlineText(boolean underlineText)
---


(9) ビットマップ画像を取得する

    Bitmap bitmap = BitmapFactory.decodeResource(
                getResources(), 
                R.drawable.icon);    // (9)

画像を取得するには、BitmapFactory.decodeResource() メソッドを使う。
    static Bitmap decodeResource(Resources res, int id)

id は、res/drawable フォルダに入れた画像ファイルのリソース ID。
R.drawable.icon の場合、画像ファイルは、
    res/drawable-Xdpi/icon.png
になる。ただし、拡張子は .png とは限らず、 .jpg や .gif などもある。


(10) 画像を描画する

    canvas.drawBitmap(bitmap, 50, 400, paint);    // (10)

画像を描画するのは、Canvas クラスの drawBitmap() メソッドを使う。
    void drawBitmap(Bitmap bitmap, float left, float top, Paint paint)

画像の左上の座標が、(left, top) となる。


■ いろいろな図形を描画する

基本的な画像の描画方法がわかったところで、その他の Canvas クラスのメソッドを使っ
て、図形を描画してみる。

また、このサンプルでは、座標がわかりやすいように、50ピクセルごとに格子も描画する。
格子は、単なる直線を組み合わせている。


□ GraphicsView.java
---
package jp.marunomaruno.android.sample;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Cap;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.RectF;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;

/**
 * いろいろな図形を描画する。
 * @author marunomaruno
 * @see Graphics02Activity
 */
public class GraphicsView extends View {

    public GraphicsView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public GraphicsView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public GraphicsView(Context context) {
        super(context);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // 画面のサイズを取得する
        WindowManager manager = (WindowManager) getContext()
                .getSystemService(Context.WINDOW_SERVICE);    // (1)
        Display display = manager.getDefaultDisplay();        // (2)
        int width = display.getWidth();      // (3)
        int height = display.getHeight();    // (4)

        // ペイントオブジェクトを設定する
        Paint paint = new Paint();
        paint.setColor(Color.WHITE);
        paint.setAntiAlias(true);

        // 文字列を描画する
        paint.setTextSize(24);
        canvas.drawText("画面はたてにしてください。", 50, 50, paint);

        // 格子を描画する
        paint.setTextSize(20);
        paint.setStrokeWidth(1);    // (5)    
        paint.setColor(Color.WHITE); 
        for (int i = 0; i < Math.max(width, height); i += 50) {
            canvas.drawText(Integer.toString(i), i, paint.getTextSize(), paint);// (6)
            canvas.drawLine(i, 0, i, height, paint);    // (7)
            canvas.drawText(Integer.toString(i), 0, i, paint);
            canvas.drawLine(0, i, width, i, paint);
        }

        // 点を描画する
        paint.setStrokeWidth(10);
        canvas.drawPoint(50, 100, paint);    // (8)

        // 線を描画する
        paint.setColor(Color.rgb(255, 0, 0));
        canvas.drawLine(50, 150, 150, 150, paint);

        paint.setColor(Color.rgb(0, 255, 0));
        paint.setStrokeCap(Cap.ROUND);    // (9)
        canvas.drawLine(200, 150, 300, 150, paint);

        // 四角形を描画する
        paint.setColor(Color.rgb(0, 0, 255));
        paint.setStyle(Style.FILL);    // (10)
        canvas.drawRect(50, 200, 150, 300, paint);    // (11)

        paint.setColor(Color.RED);
        paint.setStyle(Style.STROKE);
        canvas.drawRect(200, 200, 300, 300, paint);

        paint.setColor(Color.GREEN);
        paint.setStyle(Style.FILL_AND_STROKE);
        canvas.drawRect(350, 200, 450, 300, paint);

        // 円を描画する
        paint.setColor(Color.BLUE);
        paint.setStyle(Style.FILL);
        canvas.drawCircle(100, 400, 50, paint);

        // 1/4 円を描画する
        paint.setColor(Color.CYAN);
        paint.setStyle(Style.STROKE);
        RectF r1 = new RectF(200, 350, 300, 450);    // (12)
        canvas.drawArc(r1, 0, 90, true, paint);      // (13)

        paint.setColor(Color.MAGENTA);
        paint.setStyle(Style.FILL_AND_STROKE);
        RectF r2 = new RectF(350, 350, 450, 450);
        canvas.drawArc(r2, 180, 90, false, paint);

        // 三角形を描画する
        paint.setColor(Color.GRAY);
        paint.setStyle(Style.FILL);
        Path path = new Path();    // (14)
        path.moveTo(100, 500);     // (15)
        path.lineTo(50, 600);      // (16)
        path.lineTo(150, 600);
        path.lineTo(100, 500);
        canvas.drawPath(path, paint);    // (17)
    }
}
---


□ 画面のサイズを取得する

    WindowManager manager = (WindowManager) getContext()
            .getSystemService(Context.WINDOW_SERVICE);    // (1)
    Display display = manager.getDefaultDisplay();        // (2)
    int width = display.getWidth();      // (3)
    int height = display.getHeight();    // (4)

画面に格子を描画するため、画面の幅、高さを取得する。そのための画面のサイズは、
Display オブジェクトが持つ。
Display オブジェクトは、WindowManager クラスの getDefaultDisplay() メソッドを使って取得する。

画面の幅、高さは、それぞれ、Display クラスの getWidth() メソッド、getHeight() メソッドで取得する。


□ 格子を描画する
    paint.setTextSize(20);
    paint.setStrokeWidth(1);    // (5)    
    paint.setColor(Color.WHITE); 
    for (int i = 0; i < Math.max(width, height); i += 50) {
        canvas.drawText(Integer.toString(i), i, paint.getTextSize(), paint); // (6)
        canvas.drawLine(i, 0, i, height, paint);    // (7)
        canvas.drawText(Integer.toString(i), 0, i, paint);
        canvas.drawLine(0, i, width, i, paint);
    }

画面のサイズが取得できれば、それを基に、格子を描画する。格子は50ピクセルごと。
実機がたて、横どちらでも対応できるように、最大値でループする。はみ出した分は描画
されない。

(6)は、座標の値を描画する。左端が i で、テキストの長さは、paint オブジェクトから
取得する。

(7)は、格子になる直線を描画している。x 座標が同じなので、これはたての線を描画す
る。


□ 点を描画する
    paint.setStrokeWidth(10);
    canvas.drawPoint(50, 100, paint);    // (8)

座標 (x, y) に点を描画する。
    void  drawPoint(float x, float y, Paint paint)


□ 線を描画する
    paint.setColor(Color.rgb(255, 0, 0));
    canvas.drawLine(50, 150, 150, 150, paint);

    paint.setColor(Color.rgb(0, 255, 0));
    paint.setStrokeCap(Cap.ROUND);    // (9)
    canvas.drawLine(200, 150, 300, 150, paint);


座標 (startX, startY) から座標 (stopX, stopY) への直線を描画する。
    void  drawLine(float startX, float startY, float stopX, float stopY, 
                                                                    Paint paint)


□ 四角形を描画する
    paint.setColor(Color.rgb(0, 0, 255));
    paint.setStyle(Style.FILL);    // (10)
    canvas.drawRect(50, 200, 150, 300, paint);    // (11)

四角形の左上の座標 (left, top) と、右下の座標 (right, bottom) を指定して、四角形
を描画する。
    void  drawRect(float left, float top, float right, float bottom, Paint paint)


□ 1/4 円を描画する
    paint.setColor(Color.CYAN);
    paint.setStyle(Style.STROKE);
    RectF r1 = new RectF(200, 350, 300, 450);    // (12)
    canvas.drawArc(r1, 0, 90, true, paint);      // (13)

四角形 oval に内接する楕円で、startAngle 度の位置から sweepAngle 度分の円弧を描
く。角度の単位は度(degree)。0度は、X 軸の正方向。Y 軸の正方向に向かう。したがっ
て、90 度の指定は、右下に弧がある図形になる。
    void  drawArc(RectF oval, float startAngle, float sweepAngle, 
                                                boolean useCenter, Paint paint)

このとき、useCenter がtrue であれば、中心を通る円弧(扇形)。そうでなければ、2 つ
の頂点間で直線を引く。


□ RectF クラス

四角形のクラス。このオブジェクトに内接する形で、楕円などを描くことができる。

・クラス階層
java.lang.Object
   +  android.graphics.RectF

・主なインスタンス変数
---
public float  bottom
public float  left  
public float  right 
public float  top 
---

・主なコンストラクター
---
RectF()
RectF(float left, float top, float right, float bottom)
RectF(RectF r)
RectF(Rect r)
---

・主なメソッド
---
boolean     contains(float left, float top, float right, float bottom)
boolean     contains(float x, float y)
boolean     contains(RectF r)
final float height()
void        set(float left, float top, float right, float bottom)
void        set(RectF src)
void        set(Rect src)
final float width()
---


□ 三角形を描画する
    paint.setColor(Color.GRAY);
    paint.setStyle(Style.FILL);
    Path path = new Path();    // (14)
    path.moveTo(100, 500);     // (15)
    path.lineTo(50, 600);      // (16)
    path.lineTo(150, 600);
    path.lineTo(100, 500);
    canvas.drawPath(path, paint);    // (17)


path で指定した点を頂点とする図形を描画する。
    void  drawPath(Path path, Paint paint)

多角形を描画するときには、その頂点の座標を指定する。
そして、その座標どおしを結ぶ線として、多角形を描画する。
座標を結ぶ線としては、Path クラスのオブジェクトを使う。
moveTo() で起点の座標を指定し、lineTo() で、そこから線で結ぶ次の座標を指定する。


□ Pathクラス

Path クラスは、図形の輪郭を作る線分を抽象化しているクラス。これを使うことで多角
形を描くことができる。

・クラス階層
java.lang.Object
   +  android.graphics.Path

・コンストラクター
---
Path()
Path(Path src)
---

・主なメソッド
---
void  lineTo(float x, float y)
void  moveTo(float x, float y)
---


更新        内容
2012-01-13  GraphicsView クラスにコンストラクター追加