グラフィック画面の座標に縛られないよう、最初から座標変換を導入します。
MyDC クラスの、座標変換部分です。 図形の平行移動、拡大・縮小、回転ができるようにします。
#pragma once
//
// MyDC クラス(デバイスコンテキスト)
// MyDC.h
//
class MyDC{
protected:
HDC hdc;
// 座標変換
double a, b, c, d, p, q;
int px(double x, double y){
return (int)(a * x + c * y + p);
}
int py(double x, double y){
return (int)(b * x + d * y + q);
}
public:
// コンストラクタ
MyDC(HDC hDc){
hdc = hDc;
a = 1; b = 0;
c = 0; d = 1;
p = q = 0;
}
// 座標変換
void Trans(double a1, double b1, double c1, double d1, double p1, double q1){
a = a1;
b = b1;
c = c1;
d = d1;
p = p1;
q = q1;
}
void Trans(double a1, double b1, double c1, double d1){
a = a1;
b = b1;
c = c1;
d = d1;
}
void Trans(double p1, double q1){
p = p1;
q = q1;
}
// 直線
void MoveTo(double x, double y){
MoveToEx(hdc, px(x, y), py(x, y), NULL);
}
void LineTo(double x, double y){
::LineTo(hdc, px(x, y), py(x, y));
}
};
座標変換のための計算式です。
x' = ax + cy + p y' = bx + dy + q
内容は簡単です。
-------------------------------------
原点 (0, 0) から考えて、
点 (1, 0) は点 (a, b)へ、
点 (0, 1) は点 (c, d) に移ります。
これで、拡大・縮小・回転します。そして、
原点 (0, 0) は点 (p, q) に移ります。
これで、平行移動します。
--------------------------------------
メンバーは、a, b, c, d, p, q です。 値は実数値の、double 型にします。
private: double a, b, c, d, p, q;
コンストラクタです。 これは、クラスを宣言するとき、1度だけ実行します。 ですから、メンバーの初期化に使えます。 最初は、変換はしない値を代入します。
public: // コンストラクタ a = 1; b = 0; c = 0; d = 1; p = q = 0;
メンバーに値を代入するメソッド Trans です。 Trans が幾つもあります。 このように、引数が異なれば幾つでも作れます。 値は返さないので、void を付けます。
// 座標変換 void Trans(double a1, double b1, double c1, double d1, double p1, double q1){ a = a1; b = b1; c = c1; d = d1; p = p1; q = q1; }
座標を計算するメソッドです。 上の計算式となっています。 返す値は、return の後ろに書きます。 x座標を返すのが、px で、y座標を返すのは、py です。
// 座標を変換 int px(double x, double y) { return (int)(a * x + c * y + p); } int py(double x, double y) { return (int)(b * x + d * y + q); }
"MyApp.h" です。 青い部分だけ見てください。
//
// MyApp.h
//
class MyApp : public MyMainWnd{
public:
// WM_PAINT
void wmPaint(HDC hdc){
MyDC g(hdc);
g.Trans(50, 0, 0, 50);
// 直線を描く----------------------------
g.MoveTo(0, 0);
g.LineTo(3, 3);
}
};
実行画面です。
点(0, 0) と点(3, 3) を線で結んでいます。 しかし、スクリーンの座標はドット単位ですから、座標変換しなければ短い線となってしまいます。 そこで、次のようにしています。
g.Trans(50, 0, 0, 50);
これより、 点(1, 0) は、点(50, 0) に、点(0, 1) は点(0, 50) に移ります。 結局、縦横に50倍に拡大したことになります。 ですから、長い線になりました。
この他にも、スクリーン座標のy座標は、下に向かって増えていきますが、逆にすることもできます。 たとえば、点(0, 1) を点(0, -1) のようにします。
g.Trans(1, 0, 0, -1);
このように、スクリーン座標から開放されます。