Аквариум

Аквариум

В данной задаче рисуется аквариум с анимированными пузырьками и перемещающимся лучом света, проходящим через аквариум.

Алгоритм:
1. Рисуем воду: рисуется линиями и затемняется ближе ко дну аквариума. Код алгоритма:

     for (i=50;i<w->allocation.height;i++) 
        {
            cairo_move_to(cr,0,i);
            cairo_set_line_width(cr, 1); // установка ширины линий
            cairo_line_to(cr,w->allocation.width,i); // рисуем линию воды
            cairo_set_source_rgb(cr, 0, 0.8 - (((float)6/w->allocation.width)*i)/10, 1); //Вычисление цвета воды
            cairo_stroke(cr);
        }
 

2. Рисуем остальные части аквариума: песок, рамка, луч света. Луч света рисуется следующим образом:
Из исходной точки рассеиваем линии белого прозрачного цвета. В центре лучи менее, а по бокам более прозрачные.
        for(j=0; j < 400; j++)
        {
                cairo_set_source_rgba(cr, 1, 1, 1,0.35 - (abs(200-j)/410.0)); // Задаём цвет
                cairo_move_to(cr,adj+j/3,50); 
                cairo_line_to(cr,adj+150+j/2.0-(200-j)*sin(0.45*j*3.14/180),w->allocation.height-50-(200-j)*0.4*cos(0.45*j*3.14/180)); // вычисление точки падения луча
                cairo_stroke(cr); // проводим линии
        }
 

3. Сохраняем полученную картину в pixbuf.
4. Перемещаем пузырьки вверх по формуле:
y -= 1 - d/100.0;
x +=sin(((float)(5+rand()%2)/10)*y); // x, y - координаты пузырька, d - диаметр пузырька
 

5. Рисуем пузырьки.
6. Прорисовываем сохранённый pixbuf.
7. Возвращаемся в пункт 4. В случае перемещения источника света перерисовываем задний фон, сохраняем новый полученный фон в pixbuf.

#include <stdlib.h>
#include <math.h>
#include <gtk/gtk.h>
 
#define N 20
 
int timer=0;
char timeractive=0;
int adj=0;
struct bubble //структура, описывающие пузырьки
{
    float x, y, d;
}bub[N];
GdkPixbuf * pb = NULL;
 
void init_bubble(int id, float dem) //начальное состояние пузырька
{
    bub[id].x = -50.0;
    bub[id].y = -50.0;
    bub[id].d = dem;
}
 
 
gint engine(gpointer data) //передвигаем пузырьки, проверка на выход за пределы окна
{
    if(timeractive == 0)
    {
        return 0;
    }
    timer++;
    int i=0;
    {
        for(i=0; i < N; i++)
        {
            if(bub[i].y > 50)
            {
                bub[i].y -= 1 - bub[i].d/100.0;
                bub[i].x +=sin(((float)(5+rand()%2)/10)*bub[i].y);
            }
            if(bub[i].y < 50 && timer >= 30)
            {
                init_bubble(i,5 + rand()%5);
                bub[i].x = 250+200*(rand()%2);
                bub[i].y = 550;
                timer = 0;
            }
        }
    }
    gtk_widget_queue_draw((GtkWidget *) data);
    return 1;
}
 
 
 
gint draw(GtkWidget * w, GdkEventExpose *event, gpointer data) // функция рисования, перерисовки
{
    int i=0,j=0;
    cairo_t *cr=gdk_cairo_create(w->window);
    if(timeractive == 0 || timeractive == 2)
    {
        cairo_set_source_rgb(cr, 1, 1, 1);
        cairo_set_line_width(cr, 0.1);
        cairo_rectangle(cr,0,0,w->allocation.width,50);
        cairo_fill(cr);
        for (i=50;i<w->allocation.height;i++) //Прорисовка воды
        {
            cairo_move_to(cr,0,i);
            cairo_set_line_width(cr, 1);
            cairo_line_to(cr,w->allocation.width,i);
            cairo_set_source_rgb(cr, 0, 0.8 - (((float)6/w->allocation.width)*i)/10, 1);
            cairo_stroke(cr);
        }
 
        //рисуем песок
        cairo_move_to (cr, 0, w->allocation.height-100);
        cairo_curve_to (cr, (w->allocation.width)/3, w->allocation.height-125, (w->allocation.width)/2, w->allocation.height-90, w->allocation.width, w->allocation.height-110);
        cairo_line_to(cr, w->allocation.width, w->allocation.height);
        cairo_line_to(cr, 0, w->allocation.height);
        cairo_line_to(cr, 0, w->allocation.height-100);
        cairo_set_line_width (cr, 0.1);
        cairo_set_source_rgb(cr, 244.0/255.0, 148.0/255.0, 30.0/255.0);
        cairo_fill (cr);
        cairo_set_line_width (cr, 1);
        //рисуем луч света
        for(j=0; j < 400; j++)
        {
                cairo_set_source_rgba(cr, 1, 1, 1,0.35 - (abs(200-j)/410.0));
                cairo_move_to(cr,adj+j/3,50);
                cairo_line_to(cr,adj+150+j/2.0-(200-j)*sin(0.45*j*3.14/180),w->allocation.height-50-(200-j)*0.4*cos(0.45*j*3.14/180));
                cairo_stroke(cr);
        }
 
 
        //Рисуем рамку аквариума
        cairo_set_source_rgb(cr, 0, 0, 0);
        cairo_set_line_width(cr, 9);
        cairo_rectangle(cr,0,0,w->allocation.width,w->allocation.height);
        cairo_stroke(cr);
        //Сохраняем-обновляем изображение
        if(pb == NULL)
            pb = gdk_pixbuf_get_from_drawable(NULL,w->window,NULL,0,0,0,0,w->allocation.width,w->allocation.height);
        else
            gdk_pixbuf_get_from_drawable(pb,w->window,NULL,0,0,0,0,w->allocation.width,w->allocation.height);
        if(timeractive == 0)//Создаём таймер при первом запуске
        {
            g_timeout_add(25,engine,w);
        }
        timeractive = 1;
    }
    else
    {
        GdkGC * gc = w->style->fg_gc[GTK_WIDGET_STATE (w)];
        //Вывод сохранённого изображения
        gdk_draw_pixbuf(w->window,gc,pb,0,0,0,0,w->allocation.width,w->allocation.height,0,0,0);
        //Рисование пузырьков
        for(i = 0; i < N; i++)
        {
            if(bub[i].y > 50)
            {
                cairo_arc (cr, bub[i].x, bub[i].y, bub[i].d, 0, 360);
                cairo_set_source_rgb(cr, 1, 1, 1);
                cairo_set_line_width(cr, 0.5);
                cairo_stroke(cr);
                cairo_rectangle(cr,bub[i].x+bub[i].d/2,bub[i].y-bub[i].d/2,2,2);
                cairo_fill(cr);
            }
        }
    }
    return 1;
}
gint move(GtkAdjustment *get)
{
    adj = gtk_adjustment_get_value(get);
}
 
gint ButtonPressed(GtkWidget * window, GdkEventButton *event, gpointer d)//Вызов перерисовки изображения при скролинге
{
    timeractive = 2;
    return 0;
}
 
int main ( int argc, char ** argv)
{
    srand(time(0));
	gtk_init(&argc, &argv);
	GtkWidget *scrollbar, *adj1, *area;
    adj1 = gtk_adjustment_new(0,0,800,10,1.0,50);
    area = gtk_drawing_area_new();//создание области рисования
 
	GtkWidget * window =  gtk_window_new (GTK_WINDOW_TOPLEVEL);//Создание окна
	gtk_widget_set_size_request (window, 800, 600);//Задание параметров окна
	gtk_window_set_title (window, "Bubbles v1.0 Deluxe edition :D");
	gtk_window_set_resizable(window,0);
 
	GtkWidget *table = gtk_table_new (2, 2, FALSE);//Создание таблицы
	scrollbar = gtk_hscrollbar_new (adj1);//Создание скролинга
 
	//gtk_container_add (GTK_CONTAINER (window), scrollbar);
    gtk_container_add (GTK_CONTAINER (window), table);//Добавление таблицы в окно
    gtk_widget_show (table);
    gtk_table_attach (GTK_TABLE (table),area,
    0, 1,
    1, 2,
    GTK_EXPAND | GTK_FILL,
    GTK_EXPAND | GTK_FILL,
    0, 0);//Вставляем в таблицу область рисования
    gtk_table_attach (GTK_TABLE (table), scrollbar,
    0, 1,
    0, 1,
    GTK_FILL, GTK_FILL,
    0, 0);//Вставляем в таблицу полоску скролинга
    gtk_widget_show (scrollbar);
	int i=0;
	for(; i < N; i++)//Инициализация стартового состояния пузырьков
	{
	    init_bubble(i,5 + rand()%5);
	}
	g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (gtk_main_quit), NULL);
	g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), NULL);
	g_signal_connect (G_OBJECT (area), "expose_event", G_CALLBACK (draw), NULL);
	g_signal_connect (G_OBJECT (adj1), "value_changed",G_CALLBACK (move), NULL);
    g_signal_connect (G_OBJECT (scrollbar), "button_release_event", G_CALLBACK (ButtonPressed), NULL);
	gtk_widget_show_all(window);
	gtk_main();
	return 0;
}

Ключевые слова: 
Аквариум, GTK, Cairo, пузырьки
ВложениеРазмер
main.zip2.2 кб