Имя пользователя:
Пароль:  
Помощь | Регистрация | Забыли пароль?  

Показать сообщение отдельно
pva pva вне форума

Аватара для pva

Ветеран


Сообщения: 1180
Благодарности: 279

Профиль | Отправить PM | Цитировать


Изображения
Тип файла: jpg Безымянный.JPG
(78.8 Kb, 8 просмотров)

поворот производится операцией сдвига и умножения на матрицу. немного теории:
в 2-мерном векторном (евклидовом) пространстве все элементы можно представить как векторы от точки {0,0} до точки {a,b}. Ещё над векторами есть операции: сложение/вычитание и скалярное умножение (свёртка). Ещё есть так называемое тензорное (внешнее) умножение. Оно превращает 2 вектора в матрицу, 3 и больше векторов в более сложные объекты. Есть группа простейших преобразований: масштабирование, сдвиг и поворот. Они все описываются скалярным умножением вектора на матрицу. И ещё последовательные операции можно комбинировать в одну матрицу.
Короче:
{{Cos[a], Sin[a]}, {-Sin[a], Cos[a]}} . {x,y} - поворот вектора на угол a вокруг точки {0,0}

{c,d} + {{Cos[a], Sin[a]}, {-Sin[a], Cos[a]}} . {x,y} - поворот вектора на угол a и сдвиг точки на {c,d}

есть волшебная функция, которая задаёт это преобразование SetWorldTransform и которая накладывает 2 преобразования ModifyWorldTransform. Они использут структурку:
Код: Выделить весь код
x' = x * eM11 + y * eM21 + eDx,  
y' = x * eM12 + y * eM22 + eDy, 

typedef struct  _XFORM {  // xfrm  
    FLOAT eM11; 
    FLOAT eM12; 
    FLOAT eM21; 
    FLOAT eM22; 
    FLOAT eDx; 
    FLOAT eDy; 
} XFORM;
теперь делаем следующим образом: пусть есть исходные координаты, описанные структурой POINT (из winapi)
используем подход с откатами при исключениях (при исключении не портим DC). Что получилось - смотрим скриншот.
Код: Выделить весь код
#include <vector>
//#include <string>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <stdexcept>
//#include <windows.h>
using namespace std;
//----------------------------------------------

void _error(const char* message);
//----------------------------------------------

// не то, чтобы класс, просто кучка функций для того, чтобы было на чём рисовать
// и чтобы результаты трудов потом не затирались

class Window
{
public:
    static void execute();
    static char* ginit();
    static void paint(HDC dc);

private:
    static long __stdcall WndProc(HWND, unsigned, unsigned, long);
};
//----------------------------------------------

// класс для рисования объектов
// 1. сохраняет восстанавливает параметры DC
// 2. вычисляет преобразование координат
// 3. рисует полигоны

class draw_t
{
	HDC 	rest_dc;
	int		rest_mode;
	XFORM	rest_form;
	unsigned steps;
	
	XFORM	adv_form;
	
public:	
	draw_t(HDC dc) :
		steps		(10),
		rest_dc		(dc),
		// включаем продвинутый режим драйвера рисования для устройства
		// нужно для 2k, не всегда нужно для NT и XP:
		rest_mode	(SetGraphicsMode(rest_dc, GM_ADVANCED)),
		// по умолчанию уменьшение и небольшое смещение
		adv_form	(draw_t::rotate_scale(0.0174533*10.0, 0.95, -3, -.5))
	{
		if (0==rest_mode ||
			!GetWorldTransform(rest_dc, &rest_form)) // запомнили настройки отображения.
		{
			_error("draw_t::draw_t");
		}
	}
	
	~draw_t()
	{
		// не проверяем на ошибки ибо пофиг уже
		SetWorldTransform(rest_dc, &rest_form);
		SetGraphicsMode(rest_dc, rest_mode);
	}

	// вычисляем поворот и масштабирование
	static XFORM rotate_scale(const double& angle, const double& scale, const double& x, const double& y)
	{
		double cos1 = scale*cos(angle);
		double sin1 = scale*sin(angle);
		XFORM form1 = {cos1, sin1, -sin1, cos1, x, y};
		return form1;
	}
	
	// вычисляем поворот и масштабирование
	static XFORM rotate_scale2(const double& angle, const double& scaleX, const double& scaleY, const double& x, const double& y)
	{
		double cos1 = cos(angle);
		double sin1 = sin(angle);
		XFORM form1 = {scaleX*cos1, scaleX*sin1, -scaleY*sin1, scaleY*cos1, x, y};
		return form1;
	}
	
	// настройка параметров
	void setSteps(unsigned n)
	{
		steps = n;
	}
	
	void setTransform(const XFORM& form1)
	{
		if (!SetWorldTransform(rest_dc, &form1)) _error("draw_t::setTransform");
	}
	
	void setAdvance(const XFORM& form1)
	{
		adv_form = form1;
	}
	
    // рисование
	void operator()(POINT* points, unsigned count)
	{
		// собственно говоря, самая полезная функция,
		// ради которой все затевалось. Отрисовка на низком уровне.
		// остальной код - только для поддержания порядка.
	
		for(unsigned n=0; n<steps; ++n)
		{
			if (!Polygon(rest_dc, points, count))
				_error("draw_t() at polygon");
				
			if (!ModifyWorldTransform(rest_dc, &adv_form, MWT_LEFTMULTIPLY))
				_error("draw_t() at modify transform");
		}
	}
};
//----------------------------------------------

void Window::paint(HDC dc)
{
    // здесь происходит отрисовка на высоком уровне
	draw_t draw(dc);
	
    // параметры
	draw.setSteps(150);
	draw.setTransform(draw_t::rotate_scale(0.0174533*0.0, 5.0, 200, 400));
	draw.setAdvance(draw_t::rotate_scale2(0.0174533*9.0, 0.90, 1.05, 5., -7.5));
	
    // рисуешь в кореле фигуру и переписываешь сюда координаты её точек ;-)
 	static POINT tryangle_points[3] = {{-20,-10}, {20, -10}, {0, 20}};
	draw(tryangle_points, 3);
}
//----------------------------------------------

char* Window::ginit()
{
    // это требуется для создания окна. Можно было через диалоги сделать,
    // но там код короче, но запутанней
    static char* class_name = 0;

    if (!class_name)
    {
        WNDCLASSEX wcla;
        wcla.cbSize         = sizeof(WNDCLASSEX);
        wcla.style          = 0;
        wcla.lpfnWndProc    = WndProc;
        wcla.cbClsExtra     = 0;
        wcla.cbWndExtra     = 4;
        wcla.hInstance      = GetModuleHandle(0);
        wcla.hIcon          = LoadIcon(0, IDI_APPLICATION);
        wcla.hCursor        = LoadCursor(0, IDC_ARROW);
        wcla.hbrBackground  = HBRUSH(1 + COLOR_BTNFACE);
        wcla.lpszMenuName   = 0;
        wcla.lpszClassName  = "Window";
        wcla.hIconSm        = LoadIcon(0, IDI_APPLICATION);

        class_name = reinterpret_cast<char*>(RegisterClassEx(&wcla));
    }

    return class_name;
}

// структура для обработки события WM_PAINT (вынесена отдельно)
// и чистки мусора после неё, exception-safe
struct safe_dc
{
	HWND hwnd;
	PAINTSTRUCT ps;	
	
	safe_dc(HWND hwnd1) : hwnd(hwnd1)
	{
		if (!BeginPaint(hwnd1, &ps)) _error("safe_dc::safe_dc");
	}

	~safe_dc()
	{
		EndPaint(hwnd, &ps);
	}
};

long __stdcall Window::WndProc(HWND hwnd, unsigned code, unsigned wparam, long lparam)
{
	// оконная процедура
	try
	{
		if (code==WM_PAINT) // рисуем
		{
			safe_dc safe_dc1(hwnd);
			Window::paint(safe_dc1.ps.hdc);
			return 0;
		}
		else if (code==WM_CLOSE) // закрываемся (выходим)
		{
			PostQuitMessage(0);
		}
	}
	catch(exception& e)		
	{
		MessageBox(0, e.what(), 0, MB_OK|MB_ICONERROR);
	}

    return DefWindowProc(hwnd, code, wparam, lparam);
}

void Window::execute()
{
	// простейшая обработка ввода/вывода приложения.
    MSG cmsg;

    while (GetMessage(&cmsg, 0, 0, 0))
    {
//        TranslateMessage(&cmsg);
        DispatchMessage(&cmsg);
    }
}
//----------------------------------------------

void _error(const char* message)
{
    // генерируем исключение с кодом ошибки ОС
	long last_error = GetLastError();
	ostringstream ss;
	ss << message << " error=";
	ss.width(8);
	ss.fill('0');
	ss.flags(ios::hex|ios::right);
	ss << last_error;
	
	throw runtime_error(ss.str());
}

int main()
{
    // создали окно, поигрались, уничтожили
	HWND hwnd = CreateWindowEx(0, Window::ginit(), "Draw Test", WS_OVERLAPPEDWINDOW|WS_VISIBLE,
				CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
				GetDesktopWindow(), 0, GetModuleHandle(0), 0);
   
   	if (hwnd)
   	{
   		Window::execute();
   		DestroyWindow(hwnd);
	}   		
   
	return 0;
}
Это сообщение посчитали полезным следующие участники:

Отправлено: 16:47, 07-01-2008 | #7