In a C program, first step is to initialize the graphics drivers on the computer. This is done using the
initgraph()
method provided in
graphics.h
library. In the next few pages we will discuss graphics.h library in more details. Important functions in
graphic.h
library will be discussed in details and samples programs will be provided to show the power of C programming language especially for graphic programming developing graphical user interfaces.
We will restrict our discussion on Graphics in C to 16 bit C programming, MS DOS environment and 640×480 VGA monitor. Complete reference of graphics.h library and explanation of each method in that library can be found in the following articles.
- Graphics Library (graphics.h) Reference (part 1)
- Graphics Library (graphics.h) Reference (part 2)
- Graphics Library (graphics.h) Reference (part 3)
Graphics mode Initialization – initgraph() function
First of all we call the
initgraph()
function that will initialize the graphics mode on the computer. The method
initigraph()
has the following prototype.
void initgraph(int far *graphdriver, int far *graphmode, char far *pathtodriver);
The method
initgraph()
initializes the graphics system by loading the graphics driver from disk (or validating a registered driver) then putting the system into graphics mode. The method
initgraph()
also resets all graphics settings (color, palette, current position, viewport, etc.) to their defaults. The initialization result is set to 0 which can be retrieved by calling
graphresult()
.
The
initgraph()
method has the following parameters.
*graphdriver
This is an integer value that specifies the graphics driver to be used. You can give
graphdriver
a value using a constant of the graphics_drivers enumeration type which is listed in
graphics.h
. Normally we use value as “0” (requests auto-detect). Other values are 1 to 10 and description of each enumeration type is listed here.
*graphmode
This is an integer value that specifies the initial graphics mode (unless
*graphdriver = DETECT
). If
*graphdriver = DETECT
, then
initgraph()
method sets *graphmode to the highest resolution available for the detected graphics driver. You can give *graphmode a value using a constant of the graphics_modes enumeration type and description of each enumeration type is listed here.
*pathtodriver
Specifies the directory path where
initgraph()
looks for graphics drivers (*.BGI) first.
- If they’re not there,
initgraph()
method looks in the current directory.
- If pathtodriver is null, the driver files must be in the current directory.
Both *graphdriver and *graphmode parameters must be set to valid graphics_drivers and graphics_mode values or you’ll get unpredictable results. (The exception is graphdriver = DETECT.)
After a call to
initgraph()
, *graphdriver is set to the current graphics driver, and *graphmode is set to the current graphics mode. You can tell initgraph to use a particular graphics driver and mode, or to auto detect the attached video adapter at run time and pick the corresponding driver. If you tell initgraph to auto detect, it calls detectgraph to select a graphics driver and mode.
The
initgraph()
method loads a graphics driver by allocating memory for the driver (through
_graphgetmem()
method call), then loading the appropriate .BGI file from disk. As an alternative to this dynamic loading scheme, you can link a graphics driver file (or several of them) directly into your executable program file.
Here is a simple program that initializes the graphics mode in C programming language and print the line in graphics mode.
#include <graphics.h> #include <stdlib.h> #include <stdio.h> #include <conio.h> int main(void) { /* request auto detection */ int gdriver = DETECT, gmode, errorcode; /* initialize graphics mode */ initgraph(&gdriver, &gmode, ""); /* read result of initialization */ errorcode = graphresult(); if (errorcode != grOk) /* an error occurred */ { printf("Graphics error: %s\n", grapherrormsg(errorcode)); printf("Press any key to halt:"); getch(); exit(1); /* return with error code */ } /* draw a line */ line(0, 0, getmaxx(), getmaxy()); /* clean up */ getch(); closegraph(); return 0; }
The below program draws a circle in the current drawing color with its center at (150,150) and the radius (100) given by radius.
/* Sample program to draw a circle*/#include<graphics.h> #include<conio.h> main() { int gd=DETECT,gm; initgraph(&gd,&gm,""); /* initialization of graphic mode */ circle(150,150,100); getch(); closegraph(); /* Restore orignal screen mode */} /* End of program */
Normally the screen which we see in DOS/Command Mode is in the text mode which means it is meant for text only. And for graphics we need to initialize graphics mode using initgraph() method defined in graphics.h?.
circle(x coordinate ,y coordinate , radius);
The circle command takes a X coordinate which means Vertical axis and Y coordinate which means Horizontal axis. And the last one is the radius of the circle.
closegraph();
This function unloads the graphics drivers and returns the screen back to text mode.
/*A program to draw a space with stars*/#include<graphics.h> #include<stdio.h> main() { int gd=DETECT,gm; int i,x,y; initgraph(&gd,&gm,""); line(0,0,640,0); line(0,0,0,480); line(639,0,639,480); line(639,479,0,479); for(i=0;i<=1000;i++) { x=rand()%639; y=rand()%480; putpixel(x,y,15); } getch(); closegraph(); } /* End of program */
Here a sample program to illustrate how to use BARS which are used for visual statistics. The bar is filled using the current fill pattern and fill color. Bar method accepts parameters i.e. left, top, right and bottom. The setfillstyle() method can be used to fill the bar with a different color or pattern.
#include <graphics.h> #include <conio.h> main() { int gd=DETECT,gm,maxx,maxy,x,y,button; initgraph(&gd,&gm,""); line(80,150,200,150); line(80,150,80,50); settextstyle(1,HORIZ_DIR,1); outtextxy(100,153,"<-X axis"); settextstyle(1,VERT_DIR,1); outtextxy(60,50,"<-Y axis"); bar(100,100,120,150); bar(130,120,150,150); getch(); closegraph(); }
Minimum Distance between a Point and a Line
This article describes the technique and gives the solution to finding the shortest distance from a point to a line or line segment. The equation of a line defined through two points P1 (x1,y1) and P2 (x2,y2) is
P = P1 + u (P2 – P1)
The point P3 (x3,y3) is closest to the line at the tangent to the line which passes through P3, that is, the dot product of the tangent and line is 0, thus
(P3 – P) dot (P2 – P1) = 0
Substituting the equation of the line gives
[P3 – P1 – u(P2 – P1)] dot (P2 – P1) = 0
Solving this gives the value of u
Substituting this into the equation of the line gives the point of intersection (x,y) of the tangent as
x = x1 + u (x2 – x1)
y = y1 + u (y2 – y1)
The distance therefore between the point P3 and the line is the distance between (x,y) above and P3.
Notes
- The only special testing for a software implementation is to ensure that P1 and P2 are not coincident (denominator in the equation for u is 0)
- If the distance of the point to a line segment is required then it is only necessary to test that u lies between 0 and 1.
- The solution is similar in higher dimensions.
Source code
//================ // // DistancePointLine Unit Test // Copyright (c) 2002, All rights reserved // // Damian Coventry // Tuesday, 16 July 2002 // // Implementation of theory by Paul Bourke // //================ #include <stdio.h> #include <math.h> typedef struct tagXYZ { float X, Y, Z; } XYZ; float Magnitude( XYZ *Point1, XYZ *Point2 ) { XYZ Vector; Vector.X = Point2->X - Point1->X; Vector.Y = Point2->Y - Point1->Y; Vector.Z = Point2->Z - Point1->Z; return (float)sqrt( Vector.X * Vector.X + Vector.Y * Vector.Y + Vector.Z * Vector.Z ); } int DistancePointLine( XYZ *Point, XYZ *LineStart, XYZ *LineEnd, float *Distance ) { float LineMag; float U; XYZ Intersection; LineMag = Magnitude( LineEnd, LineStart ); U = ( ( ( Point->X - LineStart->X ) * ( LineEnd->X - LineStart->X ) ) + ( ( Point->Y - LineStart->Y ) * ( LineEnd->Y - LineStart->Y ) ) + ( ( Point->Z - LineStart->Z ) * ( LineEnd->Z - LineStart->Z ) ) ) / ( LineMag * LineMag ); if( U < 0.0f || U > 1.0f ) return 0; // closest point does not fall within the line segment Intersection.X = LineStart->X + U * ( LineEnd->X - LineStart->X ); Intersection.Y = LineStart->Y + U * ( LineEnd->Y - LineStart->Y ); Intersection.Z = LineStart->Z + U * ( LineEnd->Z - LineStart->Z ); *Distance = Magnitude( Point, &Intersection ); return 1; } void main( void ) { XYZ LineStart, LineEnd, Point; float Distance; LineStart.X = 50.0f; LineStart.Y = 80.0f; LineStart.Z = 300.0f; LineEnd.X = 50.0f; LineEnd.Y = -800.0f; LineEnd.Z = 1000.0f; Point.X = 20.0f; Point.Y = 1000.0f; Point.Z = 400.0f; if( DistancePointLine( &Point, &LineStart, &LineEnd, &Distance ) ) printf( "closest point falls within line segment, distance = %f\n", Distance ); else printf( "closest point does not fall within line segment\n" ); LineStart.X = 0.0f; LineStart.Y = 0.0f; LineStart.Z = 50.0f; LineEnd.X = 0.0f; LineEnd.Y = 0.0f; LineEnd.Z = -50.0f; Point.X = 10.0f; Point.Y = 50.0f; Point.Z = 10.0f; if( DistancePointLine( &Point, &LineStart, &LineEnd, &Distance ) ) printf( "closest point falls within line segment, distance = %f\n", Distance ); else printf( "closest point does not fall within line segment\n" ); }
The shortest line between two lines in 3D
Two lines in 3 dimensions generally don’t intersect at a point, they may be parallel (no intersections) or they may be coincident (infinite intersections) but most often only their projection onto a plane intersect.. When they don’t exactly intersect at a point they can be connected by a line segment, the shortest line segment is unique and is often considered to be their intersection in 3D.
The following will show how to compute this shortest line segment that joins two lines in 3D, it will as a biproduct identify parrallel lines. In what follows a line will be defined by two points lying on it, a point on line “a” defined by points P1 and P2 has an equation
Pa = P1 + mua (P2 –
P1)
similarly a point on a second line “b” defined by points
P4 and P4 will be written as
Pb = P3 + mub
(P4 – P3)
The values of mua and mub range from negative to positive infinity. The line segments between P1
P2 and P3 P4 have their corresponding mu
between 0 and 1.
There are two approaches to finding the shortest line segment between lines “a” and “b”. The first is to write down the length of the line segment joining the two lines and then find the minimum. That is, minimize the following
|| Pb – Pa ||2
Substituting the equations of the lines gives
|| P1 – P3 + mua (P2 –
P1) – mub (P4 – P3) ||2
The above can then be expanded out in the (x,y,z) components. There are conditions to be met at the minimum, the derivative with respect to mua and mub must be zero. Note: it is easy to convince
oneself that the above function only has one minima and no other minima or maxima. These two equations can then be solved for mua and mub, the actual intersection points found by substituting the values of mu into the original equations of the line.
An alternative approach but one that gives the exact same equations is to realize that the shortest line segment between the two lines will be perpendicular to the two lines. This allows us to write two equations for the dot product as
(Pa – Pb) dot (P2 – P1) = 0
(Pa – Pb) dot (P4 – P3) = 0
Expanding these given the equation of the lines
( P1 – P3 + mua (P2 –
P1) – mub (P4 – P3) ) dot
(P2 – P1) = 0
( P1 – P3 + mua (P2 –
P1) – mub (P4 – P3) ) dot
(P4 – P3) = 0
Expanding these in terms of the coordinates (x,y,z) is a nightmare but the
result is as follows
d1321 + mua d2121 – mub
d4321 = 0
d1343 + mua d4321 – mub
d4343 = 0
where
dmnop = (xm – xn)(xo –
xp) + (ym – yn)(yo – yp)
+ (zm – zn)(zo – zp)
Note that dmnop = dopmn
Finally, solving for mua gives
mua = ( d1343 d4321 – d1321
d4343 ) / ( d2121 d4343 – d4321
d4321 )
and backsubstituting gives mub
mub = ( d1343 + mua d4321 )
/ d4343
typedef struct { double x,y,z; } XYZ; /* Calculate the line segment PaPb that is the shortest route between two lines P1P2 and P3P4. Calculate also the values of mua and mub where Pa = P1 + mua (P2 - P1) Pb = P3 + mub (P4 - P3) Return FALSE if no solution exists. */int LineLineIntersect( XYZ p1,XYZ p2,XYZ p3,XYZ p4,XYZ *pa,XYZ *pb, double *mua, double *mub) { XYZ p13,p43,p21; double d1343,d4321,d1321,d4343,d2121; double numer,denom; p13.x = p1.x - p3.x; p13.y = p1.y - p3.y; p13.z = p1.z - p3.z; p43.x = p4.x - p3.x; p43.y = p4.y - p3.y; p43.z = p4.z - p3.z; if (ABS(p43.x) < EPS && ABS(p43.y) < EPS && ABS(p43.z) < EPS) return(FALSE); p21.x = p2.x - p1.x; p21.y = p2.y - p1.y; p21.z = p2.z - p1.z; if (ABS(p21.x) < EPS && ABS(p21.y) < EPS && ABS(p21.z) < EPS) return(FALSE); d1343 = p13.x * p43.x + p13.y * p43.y + p13.z * p43.z; d4321 = p43.x * p21.x + p43.y * p21.y + p43.z * p21.z; d1321 = p13.x * p21.x + p13.y * p21.y + p13.z * p21.z; d4343 = p43.x * p43.x + p43.y * p43.y + p43.z * p43.z; d2121 = p21.x * p21.x + p21.y * p21.y + p21.z * p21.z; denom = d2121 * d4343 - d4321 * d4321; if (ABS(denom) < EPS) return(FALSE); numer = d1343 * d4321 - d1321 * d4343; *mua = numer / denom; *mub = (d1343 + d4321 * (*mua)) / d4343; pa->x = p1.x + *mua * p21.x; pa->y = p1.y + *mua * p21.y; pa->z = p1.z + *mua * p21.z; pb->x = p3.x + *mub * p43.x; pb->y = p3.y + *mub * p43.y; pb->z = p3.z + *mub * p43.z; return(TRUE); }
Graphics Library in C/C++ Programming
Graphics library provided by borland C is most widely used library for graphics programming. Mostly this graphics library is restricted to be used under 16 bit C programming and MS DOS environment. As discussed earlier that first of all you need to initialize the graphics drivers on the computer. This is done using the initgraph() method provided in graphics.h library. graphics.h is used to include the reference to the graphics library, but actual graphics library can be found in lib directory with the name of graphics.lib. In Dev C++ there is no default graphics library, but there are some third party graphics libraries to be used with Dev C++. You can download this library here, after downloading the library you can read through the manual and copy all the files at their desired locations. Also you will have to add reference to this graphics library in your projects or programs.
Adding the graphics.h in your C programs
If you are using borland C/C++ compiler to program, then you will have to follow these simple steps in ordered to get your C or C++ program running in graphics mode.
- First you will need to add #include <graphics.h> reference at the top of you C/C++ program.
- Next step is to initialize the graphics environment. This can be achieved using the function
void far initgraph(int far *driver, int far *mode, char far *path)
- Here path is the actual path to the graphics library. You can give the path to this library by giving the complete path, like “C:\\TC\\BGI”, where BGI is the graphics library directory.
- After initializing the graphics mode you can check for any error which may happen while initializing the graphics mode. The function used to find out any errors is int errorcode = graphresult() which returns the error code for the specific error.
- If you pass this errorcode to grapherrormsg() function the, it will return the complete description of the error message. And if the errorcode==grOk the you are on the way to develop your first graphics program using C/C++.
If you are using Dev C++ compiler for your graphics programs then you can follow these simple steps to configure your environment for graphics programming.
Simple graphics functions
I am providing few functions here with some description to make you start with graphics programming. I have posted the complete description with a demo C/C++ graphics program at my C and C++ Programming Blog. You can find my blog here, there are three parts of the posts about graphics.h library. First part of the post discusses the necessary functions to get the program ready to draw shapes and styles, second post provides description of necessary functions to draw different shapes and fill them with colors or textures. And the last part is a sample program to demonstrate that how you can write programs in graphics mode.
Function circle
Draws the circle on the screen using a point(x,y) and the radius for the circle.
void far circle(int x, int y, int radius)
Function line
This function draws a line in the graphics mode.
void far line(int startx, int starty, int endx, int endy)
Function outtext
Displays a text string on the grpahics screen.
void far outtext(char far *str)
Function outtextxy
Displays the text at a specified position in graphics mode.
void far outtext(int x, int y, char *str)
Function rectangle
Draws a rectangular box on the screen using the points given in the parameters.
void far rectangle(int left, int top, int right, int bottom)