Задача: нарисовать паровоз и придать ему эффект движения; В этой картинке 3 анимированых объекта: Толкательная система движется при помощи функций sin и cos, делая круговые движения. Координаты стыков палок толкательной системы вычисляются по формулам: x = cx + r1*cos(p) где cx, cy - координаты первоначального положения стыков Очевидно, что это окружность. Но при r2=0 стык между палками будет делать движения только влево-вправо. Дым движется по зеркальному отражению функции y=sqrt(x) описывая достаточно реалистичную траекторию, свойственную настоящему дыму. Чем дальше клуб дыма от трубы, тем больше его радиус и прозрачность. В каждый момент, когда из трубы должен выйти очередной клуб дыма, стартовая позиция рисования дыма сдвигается в самое начало. Со шпалами ещё проще - это просто прорисовка прямоугольников вдоль прямой, стартовая позиция рисования так же сдвигается когда справа должна показаться новая шпала. #include <stdlib.h> #include <gtk/gtk.h> #include <math.h> typedef struct dat dat; struct dat { GtkWidget *wid; //виджет float pos; //позиция толкательной системы паровоза char drawed; //переменная необходима для проверки состояния нарисованости неподвижных частей анимации GdkPixbuf *pb; //буфер для перерисовки неподвижных частей картинки cairo_t *cr; //переменная для рисования cairo gint start_shpala, start_dym; //позиция шпал и дыма float color[3]; //цвет заливки паровоза }; gboolean on_draw(GtkWidget *wid, GdkEventExpose *event, gpointer data) //функция рисования { dat *d=(dat*)data; d->cr = gdk_cairo_create(wid->window); //инициализация cairo gint i; if(d->drawed == 0) //если эта часть кода не нарисована, то рисуем { cairo_move_to(d->cr, 0, 0); //ставим кисть в точку (0,0) экрана cairo_line_to(d->cr, wid->allocation.width, 0); cairo_line_to(d->cr, wid->allocation.width, wid->allocation.height); cairo_line_to(d->cr, 0, wid->allocation.height); cairo_line_to(d->cr, 0, 0); cairo_set_source_rgb(d->cr, 0.7, 0.8, 1); cairo_fill(d->cr); //заливаем выделеную область cairo_set_source_rgb(d->cr, 0.5, 0.5, 0.5); cairo_set_line_width (d->cr, 2); cairo_rectangle(d->cr, 515, 335, 60, 80); cairo_set_source_rgb(d->cr, 0, 0, 0); cairo_stroke_preserve(d->cr); cairo_set_source_rgb(d->cr, d->color[0], d->color[1], d->color[2]); cairo_fill(d->cr); cairo_arc (d->cr, 605, 425, 25, 0, 2*M_PI); //----------------- cairo_set_line_width(d->cr, 1); // Маленькое колесо cairo_set_source_rgb(d->cr, 0, 0, 0); cairo_stroke_preserve(d->cr); cairo_set_source_rgb(d->cr, 0.7, 0.7, 0.7); cairo_fill(d->cr); cairo_arc (d->cr, 605, 425, 20, 0, 2*M_PI); cairo_set_source_rgb(d->cr, 0, 0, 0); cairo_stroke_preserve(d->cr); cairo_set_source_rgb(d->cr, 0.4, 0.4, 0.4); cairo_fill(d->cr); cairo_set_source_rgb(d->cr, 0, 0, 0); cairo_arc (d->cr, 605, 425, 3, 0, 2*M_PI); //серцевина мальенкого колеса cairo_fill(d->cr); //------------------------- cairo_set_line_width(d->cr, 2);//------------- cairo_move_to(d->cr, 535, 340);//Красный обод над колёсами cairo_line_to(d->cr, 195, 340); cairo_arc (d->cr, 222, 340, 27, M_PI, 3*M_PI/2); cairo_line_to(d->cr, 597, 313); cairo_arc (d->cr, 540, 415, 117, 3*M_PI/2 + M_PI/6, M_PI/14); cairo_line_to(d->cr, 649, 440); cairo_line_to(d->cr, 623, 340); cairo_line_to(d->cr, 535, 340); cairo_set_source_rgb(d->cr, 0, 0, 0); cairo_stroke_preserve(d->cr); cairo_set_source_rgb(d->cr, 0.8, 0, 0); cairo_fill(d->cr);//----------------------------------------- cairo_arc (d->cr, 545, 440, 100, 3*M_PI/2, 0); //Внутренняя сторона щитка cairo_line_to(d->cr, 654, 441); cairo_line_to(d->cr, 625, 340); cairo_line_to(d->cr, 545, 340); cairo_set_source_rgb(d->cr, 0.8, 0, 0); cairo_fill(d->cr); cairo_arc (d->cr, 545, 440, 100, 3*M_PI/2 + M_PI/29, 0); cairo_set_line_width(d->cr, 1); cairo_set_source_rgb(d->cr, 0, 0, 0); cairo_stroke(d->cr); cairo_set_line_width(d->cr, 2); cairo_set_source_rgb(d->cr, 0, 0, 0); cairo_rectangle(d->cr, 106, 330, 20, 10); cairo_rectangle(d->cr, 96, 320, 10, 30); cairo_fill(d->cr); cairo_move_to(d->cr, 108, 116); cairo_arc (d->cr, 0, 180, 126, 3*M_PI/2 + M_PI/3, 0); //Левый верхний угол cairo_line_to(d->cr, 126, 354); cairo_arc (d->cr, 0, 425, 145, 3*M_PI/2 + M_PI/3 + M_PI/40, 0); //Нижний левый угол паровоза cairo_line_to(d->cr, 195, 425); cairo_line_to(d->cr, 195, 340); cairo_arc (d->cr, 222, 340, 27, M_PI, 3*M_PI/2); cairo_line_to(d->cr, 599, 313); cairo_line_to(d->cr, 550, 195); cairo_line_to(d->cr, 530, 195); cairo_line_to(d->cr, 550, 135); // Труба cairo_line_to(d->cr, 490, 135); cairo_line_to(d->cr, 510, 195); cairo_line_to(d->cr, 254, 195); cairo_stroke_preserve(d->cr); cairo_set_source_rgb(d->cr, d->color[0], d->color[1], d->color[2]); cairo_line_to(d->cr, 254, 194); cairo_line_to(d->cr, 110, 116); cairo_fill(d->cr); cairo_arc (d->cr, 550, 264, 69, 3*M_PI/2, M_PI/4); //Передок cairo_set_source_rgb(d->cr, 0, 0, 0); cairo_stroke_preserve(d->cr); cairo_set_source_rgb(d->cr, d->color[0], d->color[1], d->color[2]); cairo_fill(d->cr); cairo_move_to(d->cr, 599, 313); cairo_line_to(d->cr, 550, 195); cairo_stroke(d->cr); cairo_move_to(d->cr, 108, 116); cairo_line_to(d->cr, 238, 116); cairo_arc (d->cr, 34, 195, 220, 3*M_PI/2 + M_PI/3 + M_PI/20, 0); //Лобовое стекло cairo_set_source_rgb(d->cr, 0, 0, 0); cairo_stroke_preserve(d->cr); cairo_set_source_rgb(d->cr, d->color[0], d->color[1], d->color[2]); cairo_fill(d->cr); cairo_arc (d->cr, 24, 194, 220, 3*M_PI/2 + M_PI/3 + M_PI/15, 0); cairo_line_to(d->cr, 138, 194); cairo_line_to(d->cr, 138, 126); cairo_line_to(d->cr, 234, 126); cairo_set_source_rgb(d->cr, 0, 0, 0); cairo_stroke_preserve(d->cr); cairo_set_source_rgba(d->cr, 0.5, 0.5, 0.5, 0.5); cairo_fill(d->cr); cairo_arc (d->cr, 370, 195, 20, M_PI, 0); //Песочница cairo_set_source_rgb(d->cr, 0, 0, 0); cairo_stroke_preserve(d->cr); cairo_set_source_rgb(d->cr, d->color[0], d->color[1], d->color[2]); cairo_fill(d->cr); cairo_stroke(d->cr); cairo_set_source_rgb(d->cr, 0, 0, 0); cairo_set_line_width (d->cr, 1); for (i=0; i<3; i++) { cairo_arc (d->cr, 250 + i*105, 400, 50, 0, 2*M_PI); //------------ cairo_stroke_preserve(d->cr); //Большие колёса cairo_set_source_rgb(d->cr, 0.7, 0.7, 0.7); // cairo_fill(d->cr); cairo_arc (d->cr, 250 + i*105, 400, 45, 0, 2*M_PI); cairo_set_source_rgb(d->cr, 0, 0, 0); cairo_stroke_preserve(d->cr); cairo_set_source_rgb(d->cr, 0.4, 0.4, 0.4); cairo_fill(d->cr); cairo_set_source_rgb(d->cr, 0, 0, 0); cairo_arc (d->cr, 250 + i*105, 400, 5, 0, 2*M_PI); cairo_fill(d->cr); } //--------------- cairo_set_source_rgb(d->cr, 0,0, 20.0/255.0); cairo_select_font_face(d->cr, "Impact",CAIRO_FONT_SLANT_ITALIC, CAIRO_FONT_WEIGHT_NORMAL); cairo_set_font_size(d->cr, 24); cairo_move_to(d->cr, 450, 310); cairo_show_text(d->cr, "SteamMonster"); //Надпись на паровозе cairo_rectangle(d->cr,0, 445,wid->allocation.width, 10); //рельса cairo_set_source_rgb(d->cr, 0.5, 0.3, 0.1); cairo_fill(d->cr); cairo_rectangle(d->cr,0, 460,wid->allocation.width, wid->allocation.height); //земля cairo_set_source_rgb(d->cr, 0, 0, 0); cairo_fill(d->cr); d->drawed = 1; //отметка о том что неподвижная часть анимации нарисована if (d->pb != NULL) g_object_unref(d->pb); d->pb = gdk_pixbuf_get_from_drawable(NULL,wid->window,NULL,0,0,0,0,wid->allocation.width,wid->allocation.height); //помещение картинки в пиксбаф } else // Подвижная часть картинки { GdkGC * gc = wid->style->fg_gc[GTK_WIDGET_STATE (wid)]; gdk_draw_pixbuf(wid->window,gc,d->pb,0,0,0,0,wid->allocation.width,wid->allocation.height,0,0,0); //Перерисовываем неподвижную часть картинки cairo_set_source_rgb(d->cr, 0, 0, 0); cairo_set_line_width (d->cr, 4); cairo_move_to(d->cr, 515, 400); cairo_line_to(d->cr, 485 + 25*cos(d->pos), 400); //1ая палка толкательной системы cairo_line_to(d->cr, 355 + 25*cos(d->pos), 400 + 25*sin(d->pos));//2ая палка которая двигается по внешнему радиусу среднего колеса cairo_line_to(d->cr, 355 + 7*cos(d->pos-1), 400 + 7*sin(d->pos-1)); //3я, по внутренему радиусу среднего колеса cairo_line_to(d->cr, 408 + 10*cos(d->pos), 390); //4ая, движется на весу cairo_line_to(d->cr, 408 - 10*cos(d->pos), 340); //последняя, которая уходит в верхнюю часть паровоза cairo_move_to(d->cr, 250 + 25*cos(d->pos), 400 + 25*sin(d->pos));//соединительная палка между колёсами cairo_line_to(d->cr, 460 + 25*cos(d->pos), 400 + 25*sin(d->pos));//-------------------- cairo_stroke(d->cr); cairo_arc(d->cr, 485 + 25*cos(d->pos), 400, 3, 0, 2*M_PI);//соединенительный крепёж между 1ой и 2ой палкой cairo_arc(d->cr, 355 + 7*cos(d->pos-1), 400 + 7*sin(d->pos-1), 4, 0, 2*M_PI);//между 2ой и 3ей cairo_fill(d->cr); cairo_arc(d->cr, 408 + 10*cos(d->pos), 390, 3, 0, 2*M_PI);// и т. д. cairo_fill(d->cr); cairo_arc(d->cr, 355 + 25*cos(d->pos), 400 + 25*sin(d->pos), 4, 0, 2*M_PI); cairo_arc(d->cr, 250 + 25*cos(d->pos), 400 + 25*sin(d->pos), 4, 0, 2*M_PI); cairo_arc(d->cr, 460 + 25*cos(d->pos), 400 + 25*sin(d->pos), 4, 0, 2*M_PI); cairo_fill(d->cr); i = d->start_shpala; cairo_set_source_rgb(d->cr, 0.5, 0.5, 0.5); while (i < wid->allocation.width) { cairo_rectangle(d->cr, i, 455, 15, 10); //рисование шпал вдоль рельсы пока не закончится область рисования cairo_fill(d->cr); i+=30;//расстояние между шпалами 30 пикселей } if (d->start_shpala > -55) d->start_shpala -= 5; else d->start_shpala = 0; //обнуление стартовой позиции расстановки шпал когда справа появляется новая шпала i = d->start_dym; while (i < 85) { cairo_set_source_rgba(d->cr, 0.5, 0.5, 0.5, 0.4 - i*0.004 ); //установка цвета и прозрачности дыма. Прозрачность зависит от позиции: чем дальше дым, тем более он прозрачен cairo_arc(d->cr, 520 - i*5 , 135 - sqrt(i*380), 20 + i/3, 0, 2*M_PI); //Рисование клубов дыма. Чем дальше дым тем больше радиус круга cairo_fill(d->cr); i+=5; } if (d->start_dym < 4) d->start_dym += 1; else d->start_dym = 0; //обнуление стартовой позиции прорисовки дыма, когда очередному клубу дыма пора выйти из трубы } cairo_set_source_rgb(d->cr, 1, 1, 1); cairo_move_to(d->cr, 530, 195); //------------------------------------------------ cairo_line_to(d->cr, 550, 135); //Перерисовываем трубу чтобы дым её не перекрывал cairo_line_to(d->cr, 490, 135); cairo_line_to(d->cr, 510, 195); cairo_set_source_rgb(d->cr, 0, 0, 0); cairo_stroke_preserve(d->cr); cairo_set_source_rgb(d->cr, d->color[0], d->color[1], d->color[2]); cairo_fill(d->cr);//------------------------------------------------ cairo_destroy(d->cr); return TRUE; } gboolean steam(gpointer data) { dat *d=(dat*)data; d->pos+=0.1; //сдвиг позиции толкательной системы if (d->pos>=2*M_PI) d->pos=0.0; //обнуление позиции толкательной системы когда она сделает полный оборот gtk_widget_queue_draw(d->wid);//перерисовка паровоза return TRUE; } int main (int argc, char *argv[]) { GtkWidget *win = NULL; /* Initialize GTK+ */ g_log_set_handler ("Gtk", G_LOG_LEVEL_WARNING, (GLogFunc) gtk_false, NULL); gtk_init (&argc, &argv); g_log_set_handler ("Gtk", G_LOG_LEVEL_WARNING, g_log_default_handler, NULL); /* Create the main window */ win = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_container_set_border_width (GTK_CONTAINER (win), 8); gtk_window_set_title (GTK_WINDOW (win), "Паравозег"); gtk_window_set_position (GTK_WINDOW (win), GTK_WIN_POS_CENTER); gtk_widget_set_size_request(win, 700, 500); gtk_widget_realize (win); g_signal_connect (win, "destroy", gtk_main_quit, NULL); dat *d=NULL; d=(dat *)malloc(sizeof(dat)); d->pos=0.0; d->wid=win; d->drawed = 0; d->pb = NULL; d->cr = NULL; d->start_shpala = 0; d->start_dym = 0; d->color[0]=0; d->color[1]=0.4; d->color[2]=0.6; g_timeout_add(50, steam, (gpointer)d); g_signal_connect (G_OBJECT (win), "expose_event", G_CALLBACK (on_draw), (gpointer)d); /* Enter the main loop */ gtk_widget_show_all (win); gtk_main (); return 0; }
Ключевые слова:
анимация, движение по окружности, паровоз
|
|||||||