Вид из окна

Программа во время работы

Задача : создать приложение, выводящее на экран анимированное изображение
Использованный API : GTK/GDK
Среда разработки : Dev-C++

Приложение выводит на экран вид из окна на некоторый пейзаж. Горы нарисованы при помощи рисования многоугольников, брёвна причала рисуются при помощи двух окружностей и одного прямоугольника.
Корабль рисуется при помощи многоугольников. Для того, чтобы было ощущение, что корабль качается на волнах, к его y координате прибавляется 5*sin(t), где t - время, прошедшее от начала работы программы.
Лучи солнца имеют непостоянную длину: она рассчитывается по правилу r[i]*sin(rt[i]), где r[i] - изначальная длина луча, а rt[i] - некоторый параметр, который растёт одновременно с t. Луч рисуется от точки ((srad+5)*cos(t);(srad+5)*sin(t)) до точки ((srad+5+rad[i] ))*cos(t);(srad+5+rad[i])*sin(t)) , где rad[i] - длина луча в текущий момент времени

#include <gtk/gtk.h>
#include <stdlib.h>
#include <math.h>
 
// макрос для создания графического контекста gc с цветом r;g;b на основании окна w, 
// используя вспомагательную переменную-цвет clr
 
#define CREATE_RGB_GC(w,gc,clr,r,g,b)\
gc=gdk_gc_new(w);\
 
(clr).red=(r)*257;\
 
(clr).green=(g)*257;\
 
(clr).blue=(b)*257;\
 
gdk_gc_set_rgb_fg_color((gc),&(clr))
 
 
// t - текущий момент времени (поддерживается в виде числа из отрезка [0,2*M_PI])
double t;
// r[i] - базовая длина i-того луча солнца
int r[10];
// rt[i] - параметр, который используется для того, чтобы посчитать длину i-того луча в текущий момент.
// Тоже является числом из отрезка [0,2*M_PI]
double rt[10];
// Координаты солца
int sunx,suny;
// Радиус солнца
double srad;
// Ширина и высота окна
int width=640,height=480;
// Палитра: для рисования солнца, окна, неба, гор, травы, моря и причала
GdkGC *sungc,*windowgc,*skygc,*mountaingc,*grassgc,*seagc,*shipyardgc;
// Буфер, в котором будет рисоваться картинка, перед тем как нарисовать в окне.
GdkPixmap *pixm;
 
// Функция, которая будет вызываться каждую десятую секунды, используется для того, чтобы 
// увеличить t и все rt[i] и перерисовать всю картинку
gboolean inc_t(GtkWidget* w)
{
 
	int i;
	t+=0.1;
	if(t>2*M_PI)
		t-=2*M_PI;
	for(i=0;i<10;i++)
	{
		rt[i]+=0.05;
		if(rt[i]>2*M_PI)
			rt[i]-=2*M_PI;
	}
	expose_event(w,NULL,NULL);
	return TRUE;
}
 
// Нарисовать горы в буфере, используя информацию о
// чёрном и белом цвете из виджета w
void draw_mountain(GtkWidget *w)
{
	int i;
	GdkPoint m1[5],m2[4];
	// задаём вершины многоугольника, которые обозначают коричневую часть горы
	m1[0].x=0+70;
	m1[0].y=height-20-180;
	m1[1].x=90+70;
	m1[1].y=height-180-20-180;
	m1[2].x=120+70;
	m1[2].y=height-120-20-180;
	m1[3].x=150+70;
	m1[3].y=height-180-20-180;
	m1[4].x=240+70;
	m1[4].y=height-20-180;
	// задаём вершины многоугольника, которые обозначают белую часть горы
	m2[0].x=120+70;
	m2[0].y=height-120-20-180;
	m2[1].x=150+70;
	m2[1].y=height-180-20-180;		
	m2[2].x=120+70;
	m2[2].y=height-240-20-180;
	m2[3].x=90+70;
	m2[3].y=height-180-20-180;
	// рисуем залитые многоугольника, обозначающие горы
	gdk_draw_polygon(pixm,mountaingc,TRUE,m1,5);
	gdk_draw_polygon(pixm,w->style->white_gc,TRUE,m2,4);
	// обводим их линиями
	gdk_draw_lines(pixm,w->style->black_gc,m1,5);
	gdk_draw_lines(pixm,w->style->black_gc,m2,4);
	// смещаем гору на 70 единиц влево и на 20 единиц вниз и перерисовываем их
	for(i=0;i<5;i++)
	{
		m1[i].x-=70;
		m1[i].y+=20;
	}
	for(i=0;i<4;i++)
	{
		m2[i].x-=70;
		m2[i].y+=20;
	}
	gdk_draw_polygon(pixm,mountaingc,TRUE,m1,5);
	gdk_draw_polygon(pixm,w->style->white_gc,TRUE,m2,4);
	gdk_draw_lines(pixm,w->style->black_gc,m1,5);
	gdk_draw_lines(pixm,w->style->black_gc,m2,4);
}
 
// море рисуется как множество точек, ограниченное функцией, заданной параметрическими уравнениями:
int sea_func_x(double t)
{
	return width+10*t*t-300;
}
 
int sea_func_y(double t)
{
	return height-200+40*t;
}
 
// рисуем море
void draw_sea(GtkWidget *w)
{
	double c;
	int i;
	// заводим большой массив под море и заполняем его координатами моря
	GdkPoint m1[100];
	m1[0].x=width;
	m1[0].y=height-200;
	for(i=1,c=0;;i++,c+=0.5)
	{
		if(i!=1&&m1[i-1].x>=width) break; // если это первый элемент массива или предыдущий
		// был виден, то добавляем ещё одну точку
		m1[i].x=sea_func_x(c);
		m1[i].y=sea_func_y(c);
	}
	// отрисовываем море и обводим его линией
	gdk_draw_polygon(pixm,seagc,TRUE,m1,i);
	gdk_draw_lines(pixm,w->style->black_gc,m1,i);
}
 
// рисуем причал
void draw_shipyard(GtkWidget *w)
{
	int i;
	// рисуем 14 брёвен
	for(i=0;i<14;i++)
	{
		//задаём координаты правого верхнего угла
		int x=width-320+20*i,y=height-145;
		// рисуем дальнюю окружность и обводим её линиями
		gdk_draw_arc(pixm,shipyardgc,TRUE,x,y,20,20,0,64*360);
		gdk_draw_arc(pixm,w->style->black_gc,FALSE,x,y,20,20,0,64*360);
		// рисуем прямоугольник от середины дальней окружности к середине ближней и обводим побокам линией
		gdk_draw_rectangle(pixm,shipyardgc,TRUE,x,y+10,20,50);
		gdk_draw_line(pixm,w->style->black_gc,x,y+10,x,y+60);
		gdk_draw_line(pixm,w->style->black_gc,x+20,y+10,x+20,y+60);
		// рисуем ближнюю окружность и обводим окружностью
		gdk_draw_arc(pixm,shipyardgc,TRUE,x,y+50,20,20,0,64*360);
		gdk_draw_arc(pixm,w->style->black_gc,FALSE,x,y+50,20,20,0,64*360);
	}
}
 
// рисуем корабль
void draw_ship(GtkWidget *w)
{
	// координаты точки, относительной которой будет проивзодится рисование корабля.
	// чтобы корабль плавал её y координата меняется в интервале [height-155; height-145]
	int x=width-200,y=height-150+5*sin(t);
	GdkPoint m1[4],m2[5];
	// задаём координаты "кормы" корабля
	m1[0].x=x;
	m1[0].y=y;
	m1[1].x=x+20;
	m1[1].y=y+20;
	m1[2].x=x+100;
	m1[2].y=y+20;
	m1[3].x=x+110;
	m1[3].y=y;
	// рисуем корму корабля
	gdk_draw_polygon(pixm,shipyardgc,TRUE,m1,4);
	gdk_draw_polygon(pixm,w->style->black_gc,FALSE,m1,4);
	// рисуем мачту корабля
	gdk_draw_rectangle(pixm,shipyardgc,TRUE,x+65,y-40,4,40);
	gdk_draw_rectangle(pixm,w->style->black_gc,FALSE,x+65,y-40,4,40);
	// задаём координаты "флага" корабля
	m2[0].x=x+65;
	m2[0].y=y-40;
	m2[1].x=x+45;
	m2[1].y=y-40;
	m2[2].x=x+40;
	m2[2].y=y-30;
	m2[3].x=x+45;
	m2[3].y=y-20;
	m2[4].x=x+65;
	m2[4].y=y-20;
	// рисуем флаг корабля
	gdk_draw_polygon(pixm,w->style->white_gc,TRUE,m2,5);
	gdk_draw_polygon(pixm,w->style->black_gc,FALSE,m2,5);
}
 
// рисуем солнце и его лучи
void draw_sun(GtkWidget *w)
{
	int i;
	// рисуем солнце
	gdk_draw_arc(pixm,sungc,TRUE,sunx-srad,suny-srad,2*srad,2*srad,0,64*360);
	for(i=0;i<10;i++)
	{
		// рисуем лучи солнца.
		// т.к. окружность можно задать в форме:
		//  x=r*cos(t)
		//  y=r*sin(t)
		// то мы можем рисовать луч от точки ((srad+5)*cos(t);(srad+5)*sin(t)) до точки 
		// ((srad+5+rad[i] ))*cos(t);(srad+5+rad[i])*sin(t)), где rad[i]=r[i]*|cos(rt[i])|- радиус луча
		gdk_draw_line(pixm,sungc,(int)((srad+5)*cos(t+i*M_PI/5))+sunx,(int)((srad+5)*sin(t+i*M_PI/5))+suny,(int)(((srad+5)+r[i]*fabs(cos(rt[i])))*cos(t+i*M_PI/5))+sunx,(int)(((srad+5)+r[i]*fabs(cos(rt[i])))*sin(t+i*M_PI/5))+suny);
	}
}
 
// отрисовка картинки в буфере и перемещение его на окно
gboolean expose_event(GtkWidget* w,GdkEvent*e,gpointer d)
{
	int i;
	// рисуем небо
	gdk_draw_rectangle(pixm,skygc,TRUE,15,20,width-30,height-40);
	// рисуем траву
	gdk_draw_rectangle(pixm,grassgc,TRUE,0,height-200,width,200);
	// рисуем солнца, горы, море, корабль и пристань
	draw_sun(w);
	draw_mountain(w);
	draw_sea(w);
	draw_ship(w);
	draw_shipyard(w);
	// рисуем окно и обводим его
	gdk_draw_rectangle(pixm,windowgc,TRUE,0,0,15,height);
	gdk_draw_rectangle(pixm,windowgc,TRUE,width-15,0,15,height);
	gdk_draw_rectangle(pixm,windowgc,TRUE,15,0,width-30,20);
	gdk_draw_rectangle(pixm,windowgc,TRUE,15,height-20,width-30,20);
	gdk_draw_rectangle(pixm,w->style->black_gc,FALSE,0,0,width,height);
	gdk_draw_rectangle(pixm,windowgc,TRUE,width/2-15,20,30,height-40);
	gdk_draw_rectangle(pixm,w->style->black_gc,FALSE,15,20,width/2-30,height-40);
	gdk_draw_rectangle(pixm,w->style->black_gc,FALSE,width/2+15,20,width/2-30,height-40);
	// копируем прямоугольник из буфера с левым верхним углом в точке (0,0) в окно в прямоугольник 
	// с левым верхним углом (0,0) шириной width и высотой height
	gdk_draw_drawable(w->window,w->style->fg_gc[0],pixm,0,0,0,0,width,height);
	return TRUE;
}
 
int main(int argc,char **argv)
{
	GdkColor c;
	GtkWidget * window;
	int i;
	// инициализируем gtk, создаём окно, задаём его размеры и показываем его
	gtk_init(&argc,&argv);
	window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_widget_set_size_request(window,width,height);
	gtk_widget_show(window);
	// создаём палитру
	CREATE_RGB_GC(window->window,sungc,c,255,255,0);
	CREATE_RGB_GC(window->window,windowgc,c,255,125,0);
	CREATE_RGB_GC(window->window,skygc,c,0,170,224);
	CREATE_RGB_GC(window->window,mountaingc,c,131,61,1);
	CREATE_RGB_GC(window->window,grassgc,c,150,255,0);
	CREATE_RGB_GC(window->window,seagc,c,0,51,181);
	CREATE_RGB_GC(window->window,shipyardgc,c,121,46,0);
	// задаём координаты и радиус солнца
	sunx=width-50;
	suny=50;
	srad=25;
	// задаём некоторые начальные параметры лучей
	for(i=0;i<10;i++)
	{
		rt[i]=((double)i)/23*M_PI;
		r[i]=10*(2+sin(((double)i))/5);
	}
	// создаём буфер, в котором мы будем рисовать.
	pixm=gdk_pixmap_new(window->window,width,height,-1);
	// привязываем событие при закрытии окна и событие перерисовки с соответствующими функциями
	g_signal_connect(G_OBJECT(window),"delete-event",G_CALLBACK(gtk_main_quit),NULL);
	g_signal_connect(G_OBJECT(window),"expose-event",G_CALLBACK(expose_event),NULL);
	// функция inc_t будет вызываться по таймеру каждые 100 милисекунд 
	g_timeout_add(100,(GSourceFunc) inc_t,window);
	gtk_main();
	return 0;
}

Ключевые слова: 
анимация
ВложениеРазмер
window.zip11.37 кб