#include #include #include #include /****************************************************************************/ /* */ /* TP 1 INFOGRAPHIE [tp1ig.c] */ /* Maitrise Informatique, UMLV */ /* by Daniele Raffo */ /* 27 OCT 2000 */ /* */ /* A program for geometrical transformations of wireframe simple solids */ /* */ /* 2020 note: for newer compilers, compile with cc tp1ig.c -lm */ /****************************************************************************/ #define IMAGE_W 512 #define IMAGE_H 512 #define PI 3.1415927 /*** Definition of some data structures ***/ typedef struct { char red [IMAGE_W][IMAGE_H]; char green[IMAGE_W][IMAGE_H]; char blue [IMAGE_W][IMAGE_H]; } rgb_buffer; typedef struct { int x, y; } point2D; typedef struct { int x, y, z; } point3D; static float screen_distance = 0.; /* distance of camera from screen in perspective projection */ /*** Function prototypes ***/ void Initialize(rgb_buffer *buffer, char value_r, char value_g, char value_b); void WritePPMFile(char *filename, rgb_buffer *buffer); void DrawPixel(point2D p, char value_r, char value_g, char value_b, rgb_buffer *buffer); void DrawLine(point2D a, point2D b, char value_r, char value_g, char value_b, rgb_buffer *buffer); void DrawCube(point3D cube[], char value_r, char value_g, char value_b, rgb_buffer *buffer); void DrawTetrahedron(point3D tetrahedron[], char value_r, char value_g, char value_b, rgb_buffer *buffer); void DrawPyramid(point3D pyramid[], char value_r, char value_g, char value_b, rgb_buffer *buffer); point2D Trans2D(point2D p, int xt, int yt); point2D Hom2D(point2D p, float xh, float yh); point2D RotO(point2D p, float alpha); point3D Trans3D(point3D p, int xt, int yt, int zt); point3D Hom3D(point3D p, float xh, float yh, float zh); point3D RotX(point3D p, float alpha); point3D RotY(point3D p, float alpha); point3D RotZ(point3D p, float alpha); point2D ProjPar(point3D p); point2D ProjPersp(point3D p, float screen_distance); /****************************************************************************/ /* Application core. */ /* Shows some examples of geometrical transformations on simple objects. */ /****************************************************************************/ int main(void) { int i, j; rgb_buffer image_buffer; /* structure describing the image */ /*** Defines three canonical objects by their vertexs' coords, all objects are centered on (0, 0, 0) ***/ point3D cube[8]; point3D tetrahedron[4]; point3D pyramid[5]; /* cube, side 50 */ cube[0].x=-25; cube[0].y=-25; cube[0].z=-25; cube[1].x=25; cube[1].y=-25; cube[1].z=-25; cube[2].x=25; cube[2].y=25; cube[2].z=-25; cube[3].x=-25; cube[3].y=25; cube[3].z=-25; cube[4].x=-25; cube[4].y=-25; cube[4].z=25; cube[5].x=25; cube[5].y=-25; cube[5].z=25; cube[6].x=25; cube[6].y=25; cube[6].z=25; cube[7].x=-25; cube[7].y=25; cube[7].z=25; /* tetrahedron, side 100 */ tetrahedron[0].x=-50;tetrahedron[0].y=-43;tetrahedron[0].z=-43; tetrahedron[1].x=50;tetrahedron[1].y=-43;tetrahedron[1].z=-43; tetrahedron[2].x=0;tetrahedron[2].y=50;tetrahedron[2].z=0-43; tetrahedron[3].x=0;tetrahedron[3].y=0;tetrahedron[3].z=43; /* pyramid, side and height 50 */ pyramid[0].x=-25;pyramid[0].y=-25;pyramid[0].z=-25; pyramid[1].x=25;pyramid[1].y=-25;pyramid[1].z=-25; pyramid[2].x=25;pyramid[2].y=25;pyramid[2].z=-25; pyramid[3].x=-25;pyramid[3].y=25;pyramid[3].z=-25; pyramid[4].x=0;pyramid[4].y=0;pyramid[4].z=25; /*** Apply some transformation to objects ***/ for (i = 0; i < 8; i++) { cube[i] = Hom3D(cube[i], 2, 2, 2); cube[i] = RotX(cube[i], PI/3); cube[i] = RotY(cube[i], PI/3); cube[i] = RotZ(cube[i], PI/3); cube[i] = Trans3D(cube[i], 0, 0, 95); } for (i = 0; i < 4; i++) { tetrahedron[i]=RotY(tetrahedron[i], PI/20); tetrahedron[i]=Trans3D(tetrahedron[i], -130, 30, 110); } for (i = 0; i < 5; i++) { pyramid[i] = Hom3D(pyramid[i], 1.5, 1.5, 3.5); pyramid[i] = RotX(pyramid[i], -PI/2 + PI/20); pyramid[i] = RotY(pyramid[i], PI/6); pyramid[i] = RotZ(pyramid[i], -PI/8); pyramid[i] = Trans3D(pyramid[i], 110, -60, 90); } /*** Draws the objects and saves the image on a file for the two kinds of projection (parallel and perspective) ***/ Initialize (&image_buffer, 0, 0, 0); DrawTetrahedron(tetrahedron, 255, 255, 0, &image_buffer); DrawCube(cube, 255, 0, 0, &image_buffer); DrawPyramid(pyramid, 0, 140, 0, &image_buffer); WritePPMFile("parallel.ppm", &image_buffer); Initialize (&image_buffer, 0, 0, 0); screen_distance = 45.; DrawTetrahedron(tetrahedron, 255, 255, 0, &image_buffer); DrawCube(cube, 255, 0, 0, &image_buffer); DrawPyramid(pyramid, 0, 140, 0, &image_buffer); WritePPMFile("perspective.ppm", &image_buffer); exit; } /*** end of main ***/ /****************************************************************************/ /*-------------------------- System Functions --------------------------*/ /****************************************************************************/ /****************************************************************************/ /* Initializes the image buffer, cleaning it with the color specified. */ /****************************************************************************/ void Initialize(rgb_buffer *buffer, /* image buffer */ char value_r, /* red component of image color */ char value_g, /* green component of image color */ char value_b) /* bue component of image color */ { int i, j; for (i = 0; i < IMAGE_W; i++) for (j = 0; j < IMAGE_H; j++) { buffer->red [i][j] = value_r; buffer->green[i][j] = value_g; /* buffer->blue [i][j] = value_b; */ /* try this for a nice effect: */ buffer->blue [i][j] = (255*i/IMAGE_W-99>0 ? 255*i/IMAGE_W-99 : 0); } } /****************************************************************************/ /* Writes the image in memory buffer onto a PPM file. */ /* See UNIX man ppm(5) for specifications. */ /****************************************************************************/ void WritePPMFile(char *filename, /* name of the destination file */ rgb_buffer *buffer) /* image buffer */ { int i, j; FILE *fp; /* opens the file for writing */ if ((fp = fopen(filename, "w")) == NULL) { fprintf (stderr, "TP1IG: Error creating output file %s \n", filename); exit (1); } /* writes the header informations */ fprintf(fp, "P6\n"); /* magic number */ fprintf(fp, "# generated by TP1IG\n"); /* generator comment */ fprintf(fp, "%d %d\n", IMAGE_W, IMAGE_H); /* image width and height */ fprintf(fp, "255\n"); /* max color value */ /* writes the contents of the image buffer */ for (i = 0; i < IMAGE_H; i++) for (j = 0; j < IMAGE_W; j++) { fwrite(&(buffer->red [i][j]), sizeof(char), 1, fp); fwrite(&(buffer->green[i][j]), sizeof(char), 1, fp); fwrite(&(buffer->blue [i][j]), sizeof(char), 1, fp); } fclose(fp); return; } /****************************************************************************/ /*-------------------------- Graphic Functions -------------------------*/ /****************************************************************************/ /****************************************************************************/ /* Draws a pixel (in the XY plan). */ /****************************************************************************/ void DrawPixel(point2D p, /* point to draw */ char value_r, /* red component of pixel color */ char value_g, /* green component of pixel color */ char value_b, /* bue component of pixel color */ rgb_buffer *buffer) /* image buffer */ { int xcart, ycart; /* makes the conversion from Cartesian coords to image buffer coords */ xcart = -IMAGE_W/2 + (IMAGE_H - p.y); ycart = IMAGE_H/2 + p.x; if (((xcart >= 0) && (xcart < IMAGE_W)) && ((ycart >= 0) && (ycart < IMAGE_H))) { buffer->red [xcart][ycart] = value_r; buffer->green[xcart][ycart] = value_g; buffer->blue [xcart][ycart] = value_b; } return; } /****************************************************************************/ /* Draws a line using the Bresenham algorithm. */ /****************************************************************************/ void DrawLine(point2D a, point2D b, /* starting and finishing point */ char value_r, /* red component of line color */ char value_g, /* green component of line color */ char value_b, /* bue component of line color */ rgb_buffer *buffer) /* image buffer */ { int i; int dx, dy; int xinc = 1, yinc = 1; int error; point2D l; l.x = a.x; l.y = a.y; dx = b.x - a.x; dy = b.y - a.y; if (dx < 0) xinc = -1; if (dy < 0) yinc = -1; dx = (dx < 0 ? -dx : dx); dy = (dy < 0 ? -dy : dy); DrawPixel(l, value_r, value_g, value_b, buffer); if (dx > dy) { error = -dx; for (i = 1; i <= dx; i++) { l.x += xinc; error += dy * 2; if (error >= 0) { l.y += yinc; error -= dx * 2; } DrawPixel(l, value_r, value_g, value_b, buffer); } } else { error =- dy; for (i = 1; i <= dy; i++) { l.y += yinc; error += dx * 2; if (error >= 0) { l.x += xinc; error -= dy * 2; } DrawPixel(l, value_r, value_g, value_b, buffer); } } } /****************************************************************************/ /* Draws a cube. */ /****************************************************************************/ void DrawCube(point3D cube[], /* vertexs */ char value_r, /* red component of color */ char value_g, /* green component of color */ char value_b, /* bue component of color */ rgb_buffer *buffer) /* image buffer */ { int n; point2D cubeP[8]; /* makes the projection in 2D of the solid */ if (screen_distance > 0) for (n = 0; n < 8; n++) cubeP[n] = ProjPersp(cube[n], screen_distance); else for (n = 0; n < 8; n++) cubeP[n] = ProjPar(cube[n]); for (n = 0; n <= 2; n++) { DrawLine(cubeP[n], cubeP[n+1], value_r, value_g, value_b, buffer); DrawLine(cubeP[n+4], cubeP[n+5], value_r, value_g, value_b, buffer); } DrawLine(cubeP[3], cubeP[0], value_r, value_g, value_b, buffer); DrawLine(cubeP[7], cubeP[4], value_r, value_g, value_b, buffer); for (n = 0; n <= 3; n++) { DrawLine(cubeP[n], cubeP[n+4], value_r, value_g, value_b, buffer); } } /****************************************************************************/ /* Draws a tetrahedron. */ /****************************************************************************/ void DrawTetrahedron(point3D tetrahedron[], /* vertexs */ char value_r, /* red component of color */ char value_g, /* green component of color */ char value_b, /* bue component of color */ rgb_buffer *buffer) /* image buffer */ { int n; point2D tetrahedronP[4]; /* makes the projection in 2D of the solid */ if (screen_distance > 0) for (n = 0; n < 4; n++) tetrahedronP[n] = ProjPersp(tetrahedron[n], screen_distance); else for (n = 0; n < 4; n++) tetrahedronP[n] = ProjPar(tetrahedron[n]); DrawLine(tetrahedronP[0], tetrahedronP[1], value_r, value_g, value_b, buffer); DrawLine(tetrahedronP[1], tetrahedronP[2], value_r, value_g, value_b, buffer); DrawLine(tetrahedronP[2], tetrahedronP[0], value_r, value_g, value_b, buffer); for (n = 0; n <=2; n++) { DrawLine(tetrahedronP[n], tetrahedronP[3], value_r, value_g, value_b, buffer); } } /****************************************************************************/ /* Draws a pyramid. */ /****************************************************************************/ void DrawPyramid(point3D pyramid[], /* vertexs */ char value_r, /* red component of color */ char value_g, /* green component of color */ char value_b, /* bue component of color */ rgb_buffer *buffer) /* image buffer */ { int n; point2D pyramidP[5]; /* makes the projection in 2D of the solid */ if (screen_distance > 0) for (n = 0; n < 5; n++) pyramidP[n] = ProjPersp(pyramid[n], screen_distance); else for (n = 0; n < 5; n++) pyramidP[n] = ProjPar(pyramid[n]); for (n = 0; n <= 2; n++) { DrawLine(pyramidP[n], pyramidP[n+1], value_r, value_g, value_b, buffer); } DrawLine(pyramidP[3], pyramidP[0], value_r, value_g, value_b, buffer); for (n = 0; n <= 3; n++) { DrawLine(pyramidP[n], pyramidP[4], value_r, value_g, value_b, buffer); } } /****************************************************************************/ /*-------------------------- Geometry Functions ------------------------*/ /****************************************************************************/ /*** 2D functions ***/ /****************************************************************************/ /* Translates a point p by a quantity xt and yt */ /****************************************************************************/ point2D Trans2D(point2D p, int xt, int yt) { point2D q; q.x = p.x + xt; q.y = p.y + yt; return q; } /****************************************************************************/ /* Stretches a point p by a ratio of xh and yh */ /****************************************************************************/ point2D Hom2D(point2D p, float xh, float yh) { point2D q; q.x = rint(p.x * xh); q.y = rint(p.y * yh); return q; } /****************************************************************************/ /* Rotates a point p by an angle alpha towards the origin O of the axis */ /****************************************************************************/ point2D RotO(point2D p, float alpha) { point2D q; q.x = rint(p.x * cos(alpha) - p.y * sin(alpha)); q.y = rint(p.x * sin(alpha) + p.y * cos(alpha)); return q; } /*** 3D functions ***/ /****************************************************************************/ /* Translates a point p by a quantity xt, yt, zt */ /****************************************************************************/ point3D Trans3D(point3D p, int xt, int yt, int zt) { point3D q; q.x = p.x + xt; q.y = p.y + yt; q.z = p.z + zt; return q; } /****************************************************************************/ /* Stretches a point p by a ratio of xh, yh, zh */ /****************************************************************************/ point3D Hom3D(point3D p, float xh, float yh, float zh) { point3D q; q.x = rint(p.x * xh); q.y = rint(p.y * yh); q.z = rint(p.z * zh); return q; } /****************************************************************************/ /* Rotates a point p by an angle alpha towards the X-axis */ /****************************************************************************/ point3D RotX(point3D p, float alpha) { point3D q; q.x = p.x; q.y = rint(p.y * cos(alpha) - p.z * sin(alpha)); q.z = rint(p.y * sin(alpha) + p.z * cos(alpha)); return q; } /****************************************************************************/ /* Rotates a point p by an angle alpha towards the Y-axis */ /****************************************************************************/ point3D RotY(point3D p, float alpha) { point3D q; q.x = rint(p.x * cos(alpha) + p.z * sin(alpha)); q.y = p.y; q.z = rint(p.x * -sin(alpha) + p.z * cos(alpha)); return q; } /****************************************************************************/ /* Rotates a point p by an angle alpha towards the Z-axis */ /****************************************************************************/ point3D RotZ(point3D p, float alpha) { point3D q; q.x = rint(p.x * cos(alpha) - p.y * sin(alpha)); q.y = rint(p.x * sin(alpha) + p.y * cos(alpha)); q.z = p.z; return q; } /****************************************************************************/ /* Makes a parallel projection of a point p on the plan XY */ /****************************************************************************/ point2D ProjPar(point3D p) { point2D q; q.x = p.x; q.y = p.y; return q; } /****************************************************************************/ /* Makes a perspective projection of a point p on the screen */ /* set at distance screen_distance */ /****************************************************************************/ point2D ProjPersp(point3D p, float screen_distance) { point2D q; q.x = p.x * (screen_distance / p.z); q.y = p.y * (screen_distance / p.z); return q; }