Вращение объекта, заданнога координатами в 3d

-1.png

Вращение объекта заданога координатами в 3d
Программа написана на С. В среде code::blocks компилятор gcc. ОС Ubuntu 9.10
Эта программа по содержимому входного файла строит объект в пространстве.
для построения использовался фреймворк GTK и входящая вбиблиотека для векторрного построения Cairo.

Формат файл:
1. кол-во вершин объекта
2. координаты вершин
4. кол-во граней
5. координаты вершин для каждой грани
полученные данные записываются в структуры.
подробнее описано в комментариях.

в данной задачи использовались следующие алгоритмы:
Алгоритм:
1. Обработчиком события g_timeout_add - через определённый промежуток времени, вызывает функцию time_step.
в данную функцию передаются структура нашей фигуры, кот содержит координаты всех вершин, количество граней и описания граней.
В функции создан вектор поворота double vector, этому вектору в цикле присваиваются координаты каждой вершины и данный вектор умножается на матрицу поворота. тем самым получаются новые координаты объекта.

2. Получение матрицы поворота:
сама матрица http://www.devmaster.net/wiki/images/math/fe382245e2210c46cae61f32dbb27305.png
где
Ux=x/=norma;
Uy=y/=norma;
Uz=z/=norma;
angle= 1*M_PI/180; - угол поворота в градусах
norma = sqrt(x*x+y*y+z*z)
x,y,z - координаты вершины вокруг которой производится вращение.

3. После получения новых координат проверяем каждую грань на видимость (параметр viz)
для этого вся фигура передаётся в функцию proverka, гдя для каждой грани по 3м точка строится нормаль и если при умножение на расстояние до кубика (параметр с) получается число >0 значит грань видима, иначе нет.

((x1-x0)*(y2-y0)-(y1-y0)*(x2-x0))*fig->c
(x0,y0),(x1,y1),(x2,y2) - точки на 1 грани объекта.
fig->c - расстояние до объекта

4. После того как мы имеем новые координаты и определили для каждой грани видимость перерисовываем наш объект вызвав функцию
on_draw_4 этой функции передаются вся наша фигура.

Сначала задаём 2 вида кисти (сплошная прямая и пунктир)

static const double dashed1[] = {4.0, 15.0};
static int len1 = sizeof(dashed1) / sizeof(dashed1[0]);

static const double dashed2[] = {4.0, 0.0};
static int len2 = sizeof(dashed2) / sizeof(dashed2[0]);

после
считываем расстоение до кубика
char *metr_c=NULL;
metr_c = (char*)gtk_entry_get_text(GTK_ENTRY(edit_metrs));
metr= atof(metr_c );
cub->c=metr;

затем строим, объект по граням:
1. Проверяем полученную грань на видимость и используем соответствующую кисть
2. Каждая грань строиться путем последовательного соединения точек в том порядке, который задан в исходном файле
3. Каждая точка в пространстве проецируется на плоскость с учётом перспективы, по формуле
xе=xr/(1-(zr/c));
yе=yr/(1-(zr/c));
где xe, ye - координаты точки на экране
xr, yr - координаты точки в пространстве
с - расстояние до объекта

библиотека
файл cube.h

#ifndef CUBE_H_INCLUDED
#define CUBE_H_INCLUDED
 
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <gtk/gtk.h>
 
typedef struct POINT point;// точка
struct POINT{
    double x, y, z;
};
 
typedef struct VERG verg;//грань
struct VERG{
    int size,//количество точек на грани
        viz,//видимость грани
        *mas;//массив точек
 
};
 
typedef struct FIGURE figure;// фигура
struct FIGURE{
    int size_points;//кол-во точек в фигуре
    point *mas_points;// массив точек
 
    int size_verges;//кол-во граней в фигуре
 
    verg  *mas_verges;//массив грней
    double  matrix[4][4];// матрица поворота
 
int c;// растояние до куба
};
 
typedef struct TRANSP trans;//структура передачи
struct TRANSP{
    figure fig_tr;
    GtkWidget *window_tr;
};
 
int get_data (char *file, figure *fig);
void get_matrix(figure *fig);
void MatrixMul(double *matrix1, figure *fig);
void proverka (figure *fig);
 
#endif // CUBE_H_INCLUDED

файл обработки даных
work_with_data.c

#include "cube.h"
//считываем информацию из файла
int get_data (char *file, figure *fig){
    FILE *data;
 
    if ((data=fopen("//home//onimus//main_cube//cube.txt", "r"))==NULL){
      printf("error");
      return(1);
    }
 
int i, j;
    //точки
    fscanf(data,"%d",&fig->size_points);//считали количество точек с файла
    fig->mas_points=(point*)malloc(fig->size_points*sizeof(point));// массив точек
 
    for (i=0;i<fig->size_points;i++){
        fscanf(data, "%lf", &fig->mas_points[i].x);
        fscanf(data, "%lf", &fig->mas_points[i].y);
        fscanf(data, "%lf", &fig->mas_points[i].z);
    }
    //грани
    fscanf(data,"%d",&fig->size_verges);// кол-во граней
    fig->mas_verges=(verg*)malloc(fig->size_verges*sizeof(verg));
    for (i=0;i<fig->size_verges;i++){
        fig->mas_verges[i].viz=1;
        fscanf(data,"%d ",&fig->mas_verges[i].size);//кол-во точек в грани
        fig->mas_verges[i].mas=(int*)malloc(fig->mas_verges[i].size*sizeof(int));
        for(j=0;j<fig->mas_verges[i].size;j++)
            fscanf(data,"%d", &fig->mas_verges[i].mas[j]);
    }
    fig->c=1;
return 0;
}
 
//полчаем матрицу пофорота
void get_matrix(figure *fig){
 
    double angle= 1*M_PI/180;
    int k=4;
    double x=fig->mas_points[k].x;
    double y=fig->mas_points[k].y;
    double z=fig->mas_points[k].z;
    double norma= sqrt(x*x+y*y+z*z);
 
    x/=norma;
    y/=norma;
    z/=norma;
 
    fig->matrix[0][0]=(1-cos(angle))*(x*x-1)+1;
    fig->matrix[0][1]=sin(angle)*z+(1-cos(angle))*x*y;
    fig->matrix[0][2]=(1-cos(angle))*x*z-sin(angle)*y;
    fig->matrix[0][3]=0;
 
    fig->matrix[1][0]=(1-cos(angle))*x*y-sin(angle)*z;
    fig->matrix[1][1]=(1-cos(angle))*(y*y-1)+1;
    fig->matrix[1][2]=(1-cos(angle))*y*z+sin(angle)*x;
    fig->matrix[1][3]=0;
 
    fig->matrix[2][0]=(1-cos(angle))*x*z+sin(angle)*y;
    fig->matrix[2][1]=(1-cos(angle))*y*z-sin(angle)*x;
    fig->matrix[2][2]=(1-cos(angle))*(z*z-1)+1;
    fig->matrix[2][3]=0;
 
    fig->matrix[3][0]=0;
    fig->matrix[3][1]=0;
    fig->matrix[3][2]=0;
    fig->matrix[3][3]=1;
}
 
//умножение матрицы на вектор
void MatrixMul(double *matrix1, figure *fig){
     int i,j,n;
     double Ret[4];
     double Sum;
     	for (j = 0; j < 4; j++){
            Sum = 0;
        	for (n = 0; n < 4; n++){
             		Sum += matrix1[n] * fig->matrix[n][j];
             	}
         Ret[j] = Sum;
        }
     for (i=0;i<4;i++)
            matrix1[i]=Ret[i];
 }
 
//проверка видимых граней
void proverka (figure *fig){
    int x0, y0, z0, x1, y1, z1, x2, y2, z2, i;
    for(i=0;i<fig->size_verges;i++){
        x0=fig->mas_points[fig->mas_verges[i].mas[1]].x;
        y0=fig->mas_points[fig->mas_verges[i].mas[1]].y;
        z0=fig->mas_points[fig->mas_verges[i].mas[1]].z;
 
        x1=fig->mas_points[fig->mas_verges[i].mas[0]].x;
        y1=fig->mas_points[fig->mas_verges[i].mas[0]].y;
        z1=fig->mas_points[fig->mas_verges[i].mas[0]].z;
 
        x2=fig->mas_points[fig->mas_verges[i].mas[2]].x;
        y2=fig->mas_points[fig->mas_verges[i].mas[2]].y;
        z2=fig->mas_points[fig->mas_verges[i].mas[2]].z;
 
        int k= ((x1-x0)*(y2-y0)-(y1-y0)*(x2-x0))*fig->c;
        if (k>0) fig->mas_verges[i].viz=1;
        else fig->mas_verges[i].viz=0;
 
    }
 
}

файл main.c

#include "cube.h"
GtkWidget *edit_metrs=NULL;//поле ввода дистанции
double metr;
 
 
//кубик не закрашеный с нивидмыми линиями с перспективой
gboolean on_draw_4 (GtkWidget *widget, GdkEventExpose *event, gpointer data){
    //fon
    GdkGC * gc = widget->style->fg_gc[GTK_WIDGET_STATE (widget)];
    GdkColor *clr = malloc(sizeof(GdkColor));
 
    gdk_color_parse("black", clr);
    gdk_gc_set_rgb_fg_color( gc, clr );
    gdk_draw_rectangle(widget->window, gc, TRUE, 0, 0, widget->allocation.width, widget->allocation.height);
 
    cairo_t *cr;
    cr = gdk_cairo_create (widget->window);
    cairo_set_source_rgb(cr, 0, 0, 1);
    cairo_set_line_width (cr, 2);
 
    static const double dashed1[] = {4.0, 15.0};
    static int len1  = sizeof(dashed1) / sizeof(dashed1[0]);
 
    static const double dashed2[] = {4.0, 0.0};
    static int len2  = sizeof(dashed2) / sizeof(dashed2[0]);
 
    trans *t;
    t=(trans*)data;
    figure *cub;
    cub=&t->fig_tr;
    int i, j, l;
    int x1ee, y1ee, x2ee, y2ee;
    double x1, y1, z1, x2, y2, z2, x1t, y1t, x2t, y2t;
    float x0r=-200, y0r=-200, x1r=200, y1r=200 ;
    int x0e=0, y0e=0, x1e=widget->allocation.width, y1e=widget->allocation.height ;
    gdk_color_parse("blue", clr);
    gdk_gc_set_rgb_fg_color( gc, clr );
 
    //считываем растояние до кубика
    char *metr_c=NULL;
    metr_c = (char*)gtk_entry_get_text(GTK_ENTRY(edit_metrs));
    metr= atof(metr_c );
 
    //по координатам вершин стрим каждую грань
    cub->c=metr;
    for(i=0;i<cub->size_verges;i++){
        if(cub->mas_verges[i].viz){
            cairo_set_dash(cr, dashed2, len2, 0);
            for (j=0;j<cub->mas_verges[i].size-1;j++){
 
                x1=cub->mas_points[cub->mas_verges[i].mas[j]].x;
                y1=cub->mas_points[cub->mas_verges[i].mas[j]].y;
                z1=cub->mas_points[cub->mas_verges[i].mas[j]].z;
 
                x1t=x1/(1-(z1/cub->c));
                y1t=y1/(1-(z1/cub->c));
 
                x1ee = x0e+(x1e-x0e)/(x1r-x0r)*(x1t-x0r);
                y1ee = y0e+(y1e-y0e)/(y1r-y0r)*(y1t-y0r);
 
 
                x2=cub->mas_points[cub->mas_verges[i].mas[j+1]].x;
                y2=cub->mas_points[cub->mas_verges[i].mas[j+1]].y;
                z2=cub->mas_points[cub->mas_verges[i].mas[j+1]].z;
 
                x2t=x2/(1-(z2/cub->c));
                y2t=y2/(1-(z2/cub->c));
 
                x2ee = x0e+(x1e-x0e)/(x1r-x0r)*(x2t-x0r);
                y2ee = y0e+(y1e-y0e)/(y1r-y0r)*(y2t-y0r);
 
                cairo_line_to(cr, x1ee, y1ee);
                cairo_line_to(cr, x2ee, y2ee);
                cairo_stroke (cr);
            }
                l=0;
                x1=cub->mas_points[cub->mas_verges[i].mas[l]].x;
                y1=cub->mas_points[cub->mas_verges[i].mas[l]].y;
                z1=cub->mas_points[cub->mas_verges[i].mas[l]].z;
 
                x1t=x1/(1-(z1/cub->c));
                y1t=y1/(1-(z1/cub->c));
 
                x1ee = x0e+(x1e-x0e)/(x1r-x0r)*(x1t-x0r);
                y1ee = y0e+(y1e-y0e)/(y1r-y0r)*(y1t-y0r);
 
                l=cub->mas_verges[i].size-1;
 
                x2=cub->mas_points[cub->mas_verges[i].mas[l]].x;
                y2=cub->mas_points[cub->mas_verges[i].mas[l]].y;
                z2=cub->mas_points[cub->mas_verges[i].mas[l]].z;
 
                x2t=x2/(1-(z2/cub->c));
                y2t=y2/(1-(z2/cub->c));
 
                x2ee = x0e+(x1e-x0e)/(x1r-x0r)*(x2t-x0r);
                y2ee = y0e+(y1e-y0e)/(y1r-y0r)*(y2t-y0r);
 
                cairo_line_to(cr, x1ee, y1ee);
                cairo_line_to(cr, x2ee, y2ee);
                cairo_stroke (cr);
 
 
        }
        else{
            cairo_set_dash(cr, dashed1, len1, 0);
            for (j=0;j<cub->mas_verges[i].size-1;j++){
 
                x1=cub->mas_points[cub->mas_verges[i].mas[j]].x;
                y1=cub->mas_points[cub->mas_verges[i].mas[j]].y;
                z1=cub->mas_points[cub->mas_verges[i].mas[j]].z;
 
                x1t=x1/(1-(z1/cub->c));
                y1t=y1/(1-(z1/cub->c));
 
                x1ee = x0e+(x1e-x0e)/(x1r-x0r)*(x1t-x0r);
                y1ee = y0e+(y1e-y0e)/(y1r-y0r)*(y1t-y0r);
 
 
                x2=cub->mas_points[cub->mas_verges[i].mas[j+1]].x;
                y2=cub->mas_points[cub->mas_verges[i].mas[j+1]].y;
                z2=cub->mas_points[cub->mas_verges[i].mas[j+1]].z;
 
                x2t=x2/(1-(z2/cub->c));
                y2t=y2/(1-(z2/cub->c));
 
                x2ee = x0e+(x1e-x0e)/(x1r-x0r)*(x2t-x0r);
                y2ee = y0e+(y1e-y0e)/(y1r-y0r)*(y2t-y0r);
 
                cairo_line_to(cr, x1ee, y1ee);
                cairo_line_to(cr, x2ee, y2ee);
                cairo_stroke (cr);
            }
                l=0;
                x1=cub->mas_points[cub->mas_verges[i].mas[l]].x;
                y1=cub->mas_points[cub->mas_verges[i].mas[l]].y;
                z1=cub->mas_points[cub->mas_verges[i].mas[l]].z;
 
                x1t=x1/(1-(z1/cub->c));
                y1t=y1/(1-(z1/cub->c));
 
                x1ee = x0e+(x1e-x0e)/(x1r-x0r)*(x1t-x0r);
                y1ee = y0e+(y1e-y0e)/(y1r-y0r)*(y1t-y0r);
 
                l=cub->mas_verges[i].size-1;
 
                x2=cub->mas_points[cub->mas_verges[i].mas[l]].x;
                y2=cub->mas_points[cub->mas_verges[i].mas[l]].y;
                z2=cub->mas_points[cub->mas_verges[i].mas[l]].z;
 
                x2t=x2/(1-(z2/cub->c));
                y2t=y2/(1-(z2/cub->c));
 
                x2ee = x0e+(x1e-x0e)/(x1r-x0r)*(x2t-x0r);
                y2ee = y0e+(y1e-y0e)/(y1r-y0r)*(y2t-y0r);
 
                cairo_line_to(cr, x1ee, y1ee);
                cairo_line_to(cr, x2ee, y2ee);
                cairo_stroke (cr);
        }
 
    }
    cairo_destroy(cr);
    return TRUE;
}
 
//функция пересчёта координата и перерисовки при повороте
gint time_step (gpointer data){
   trans *l;
   l=(trans*)data;
    GtkWidget *widget;
    widget=l->window_tr;
    int i;
    double vector[4]={0,0,0,0};
 
for (i=0;i<l->fig_tr.size_points;i++){
        vector[0]=l->fig_tr.mas_points[i].x;
        vector[1]=l->fig_tr.mas_points[i].y;
        vector[2]=l->fig_tr.mas_points[i].z;
 
        MatrixMul(vector, &l->fig_tr);
        proverka(&l->fig_tr);
 
        l->fig_tr.mas_points[i].x=vector[0];
        l->fig_tr.mas_points[i].y=vector[1];
        l->fig_tr.mas_points[i].z=vector[2];
    }
 
    gtk_widget_queue_draw(widget);
    return 1;
}
 
void show (int argc, char *argv[]){
    figure fig;
    char *path="//home//onimus//tests//main_cube//cube.txt";
 
    if (get_data(path, &fig)) exit(1);
    get_matrix(&fig);
 
	gtk_init( &argc, &argv);
	GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
	gtk_widget_set_size_request (window, 400, 400);
 
    trans fd;//хранит окно, массив точек и грани
    fd.window_tr=window;
    fd.fig_tr=fig;
 
    g_signal_connect (G_OBJECT (window), "expose_event", G_CALLBACK (on_draw_4),(gpointer)&fd);//рисуем объект
 
  	g_timeout_add(10, (GSourceFunc) time_step, (gpointer)&fd);//вращаем объект
    g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
 
	gtk_widget_show_all(window);
	gtk_main();
 
}
 
 
int main (int argc, char *argv[]){
  GtkWidget *window=NULL,//окно для ввода
            *hbox=NULL,//контейнер хранящий поля ввода, подписи и кнопку
            *button=NULL, //кнопка
            *label_metrs=NULL;//подпись для растояния до мишени
 
  gtk_init(&argc, &argv);
 
  //окно полей ввода
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_resize(GTK_WINDOW(window), 300, 100);
  gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_title (GTK_WINDOW (window), "Входные даные:");
  gtk_container_set_border_width (GTK_CONTAINER (window), 20);
 
  //кнопка
  button = gtk_button_new_with_label (("рисуем куб"));
 
  //поля ввода
  edit_metrs=gtk_entry_new();
  //метки
  label_metrs = gtk_label_new ("Расстояние ");
 
  //"контейнер" содержащий объекты
 
  hbox = gtk_vbox_new(FALSE, 9);
  gtk_container_add(GTK_CONTAINER (window), hbox);
 
//----------------------------------------------------------
 
  gtk_container_add(GTK_CONTAINER (hbox), label_metrs);
  gtk_container_add(GTK_CONTAINER (hbox), edit_metrs);
 
  gtk_container_add(GTK_CONTAINER (hbox), button);
 
  //при нажатии кнопки
  g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (show), NULL);
 
  g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
  gtk_widget_show_all(window);
  gtk_main();
 
  return 0;
}

Ключевые слова: 
вращение, трехмерный объект, чтение из файла
ВложениеРазмер
object.zip4.73 кб