You are on page 1of 24

More Output Primitives and

UNIT 3 MORE OUTPUT PRIMITIVES AND Geometric Transformations

GEOMETRIC TRANSFORMATIONS
Structure Page No
3.1 Introduction 41
Objectives
3.2 Filled-Area Primitives 42
3.3 Character Generation 50
3.4 Two-Dimensional Geometric Transformations 52
3.5 Summary 55
3.6 Solutions/Answers 56
3.7 Practical Exercises 64

3.1 INTRODUCTION
In Unit 2, you learnt how some basic geometric objects such as line segments,
circles, ellipses and other curves are processed for plotting on a display unit. In
most of the graphics packages you will find that polygons are also used as
standard output primitives. Filled polygons with single solid color (or other
alternatives for filling the interior) are provided as shape primitives.

In this unit, we shall discuss two basic methods to fill polygonal or arbitrary
shaped closed regions in Sec. 3.2. We consider here methods for solid fill of
specified areas. While creating or editing a document in a word processing
software, you might have used letters, numbers and other characters in various
font types and styles. We shall discuss in Sec. 3.3 how different fonts are
generated using 2D drawing primitives. In Sec. 3.4 we shall discuss tools and
techniques from yet another interesting concept in Computer Graphics–two-
dimensional geometric transformations. These transformations help translate,
rotate or deform the basic shapes to build complex structures and environment
such as those used in computer games, movie production or real time 3D
technologies. Practical exercises related to this unit are given at the end of the
unit in Sec. 3.7.

Objectives
After reading this unit, you should be able to
• describe how a closed area can be filled using the two methods namely,
scan line method and seed fill method;
• use these methods for fill area applications;
• generate a character or a font from the alphabet of any language;
• describe how basic 2D geometric transformations such as translation,
rotation, scaling, reflection, etc. are applied on geometric objects;
• combine basic 2D transformations to obtain more useful composite
transformations for applications;
• implement these transformations using a C code with OpenGL.

41
Computer Graphics
3.2 FILLED-AREA PRIMITIVES
Filled-area primitives are one of the most important types of primitives used in
Computer Graphics. Basically filled-area primitives are meant to fill the given
bounded and closed region with a single color/multiple colors or with some fill
pattern. You will find later that almost all types of curves/surfaces are
approximated by polylines/polygonal surfaces for the purposes of scan
conversion. Therefore, polygon fill algorithms are extensively used in
applications involved in 2D geometry processing or, in scan converting surface
patches with polygonal surface approximation. This section deals with some of
the important and most common algorithms being used for defining and
implementing filled area primitives.

Two basic approaches are followed in area filling on raster systems. In the first
approach overlap intervals for scan lines that cross the area are determined per
scan line (see Fig. 1 (a)). Remember that a scan line is a horizontal line of
pixels that can be plotted on the display device when the electron beam
traverses the display area horizontally during one horizontal retrace. Second
approach begins with an interior point and fills the area moving outward from
this point until the boundary condition is reached (see Fig. 1(b)). An algorithm
following the first approach is classified as scan line algorithm and that falling
under second class is called seed fill algorithm. Simple objects such as
polygons, circles etc. are efficiently filled with scan line fill algorithms and
more complex regions use the seed fill method. The scan line algorithm is
mostly used in general graphics packages.

Pixels between two


intersections filled with
color

Scan line Seed point

(a) (b)
Fig. 1

Let us begin with scan line polygon fill algorithm. Notice that polygons can be
as simple as a triangle and could be as complicated as the one shown in Fig. 2
below.

42 Fig. 2: A self intersecting polygon


These are self intersecting polygons. We broadly keep the polygons in one of More Output Primitives and
Geometric Transformations
the three categories (i) convex (ii) concave (iii) self intersecting.
Mathematically, a self intersecting polygon is concave. You will deal with
such polygons in greater details for the purpose of area filling.

Before we go ahead with area filling algorithms, a word about pixel addressing
and object geometry. You know that line segments are discretized into finite
number of pixels and each pixel has its height and width depending upon the
aspect ratio of the display unit. Therefore, if you plot a single pixel it will
occupy some area on display unit. This contributes to the object geometry
resulting in aberrations from the actual size. For example, if you have to plot a
line segment starting from (10, 10) to (15, 10) , then length of the segment is 5
whereas, in plotting this line segment, six pixel areas will be contributing to
this segment, resulting in increased length of line segment by one unit.
Sec. 3-10 of the book discusses this issue and suggests some solution to the
problem. Read carefully this section before you study Sec. 3-11 to understand
and implement scan line polygon fill algorithm.

Read Secs. 3-10 and 3-11, Chapter 3, pages 134-144 of the book.

Before proceeding further, we summarize the main steps used in scan line
polygon fill algorithm for your reference.

Let P be a given polygon with N vertices given by


Vi = ( x i , y i ), i = 0, 1, … , N − 1 in the counter clockwise orientation. For the
sake of convenience, it will be assumed that vertices have non-negative integer
coordinates.

Step 1: Input polygon vertices Vi = ( x i , y i ), i = 0, 1, …, N − 1 with VN = V0 .


Further, define the edges E i = Vi Vi+1 , i = 0, 1, …, N − 1 .
Step 2: Find out y min = min i {y i }, y max = max i {y i } .
Step 3: Create a sorted edge table in either clockwise or anti-clockwise
direction while traversing the edges along the boundary of the polygon.
Exclude horizontal lines while creating sorted edge table (because
horizontal lines do not intersect scan lines. Remember the horizontal
lines may overlap the scan line, but do not cross the scanline. Here you
are finding out intersection when the two lines are crossing each other).
You may use the bucket sort to create the sorted edge table based on
smallest y value. If two edges have the same smallest y -value, the
one having smaller x - intercept value for the other vertex will come
prior. Each entry contains
(a) maximum y -value of the edge,
(b) x -intercept value for the lower vertex of that edge,
(c) inverse of the slope of the edge.

Step 4: Starting with scan line y = y min find out the intersection point of each
scan line with all the edges E i stored in a sorted edge table until you
reach y = y max . For this you need to

43
Computer Graphics (a) Create an 'Active Edge List' (AEL) for each scan line. This
contains all edges that are crossed by the current scan line. To
do this you need to compare the y -value of current scan line
with the minimum and maximum y-values of the end points of
each edge. If for an edge E k , the current scan line y-value
satisfies min {y k , y k +1} ≤ y ≤ max {y k , y k +1} , then E k
intersects the current scan line. More precisely -
i) Move from the edge table all those edges E k for which
lowest y values are equal to the current scan line y-value.
ii) Remove from AEL those edges E k whose highest y-
values are equal to the current scanline y-value.
(b) Find out intersection with only those edges which are in the
AEL. Use coherence to find out successive intersections for
incrementing scan lines. Coherence here means that if an edge
has been intersected by the scan line, the next scan line will also
most probably intersect the edge. This helps in fast computation
of successive intersection points using the equation of the line
containing the edge. More precisely, if the current scan line is yk
and the intersection of the scan line with an edge is xk then using
the equation y = mx + b of the edge, the intersection xk+1 of the
edge with the next scan line yk+1 = yk +1 can be easily computed
1
as x k +1 = x k + (refer to DDA line algorithm).
m
(c) Sort intersection points with increasing x-coordinate values.
(d) Make pairs of intersection points. For example, if for scan line
y = 3 , there are four intersection points
(12, 3), (17, 3), (19, 3), (23, 3) , make pairs as follows:
{(12, 3), (17, 3)}, {(19, 3), (23, 3)} .
(e) Check if an intersection point appears twice in the list. If yes,
then this could be a vertex of the polygon. For such vertices
(x m , y m )
• count the intersection point twice if
y m = min{y m−1 , y m , y m +1} , otherwise
• count the intersection point only once.
Step 5: For all pixels ( x , y) on the current scan line for which
x p ≤ x + 1 / 2 ≤ x p +1 , ( x , y) lies in the polygon. Assign the specified
fill color to ( x , y) .

Notice that by performing Step 4(a)(ii) you actually shorten edges by one pixel
that helps in resolving the vertex intersection problem.

The above algorithm gives a solid fill of the given polygon. One can also
include pattern fills, factors for transparency, etc. We are now illustrating the
algorithm for a simple polygon.

44
Example 1: Let us consider a polygon with vertices V0 (10, 10), V1 = (15, 15) , More Output Primitives and
Geometric Transformations
V2 = (16, 13), V3 = (16,15), V4 = (20, 15), V5 = (20, 10), V6 = V0 and edges
E j = Vj Vj+1 , j = 0, 1, 2, 3, 4, 5 .
Step 1: Store the vertices and edges in the order given.
Step 2: y min = min i {y i } = 10 and y max = max i {y i } = 15 .
Step 3: Sorted edge table is created as follows. Start with y = 10 and continue
till y = 15 . Clearly for y = 10 there are only two edges that begin with
this y-value namely, E 0 and E 4 . Slope of E 0 is 1 and that of E 4 is
∞ . As described in Step 3, part (a) – (c) above, the edge table is
formed as shown in Fig. 3.

15

14

13 15 16 –½ 15 16 0

12

11

y = 10 15 10 1 15 20 0

Fig. 3: Sorted edge table for the polygon of Example 1.

Notice that the edges E 3 and E 5 do not appear in the edge table, being
horizontal line segments.

Step 4: (a) Active edge list for all scan lines


y = 10, AEL = {E 0 , E 4 }
y = 11, AEL = {E 0 , E 4 }
y = 12, AEL = {E 0 , E 4 }
y = 13, AEL = {E 0 , E1 , E 2 , E 4 }
y = 14, AEL = {E 0 , E1 , E 2 , E 4 }
y = 15, AEL = {}, Empty set

(b) Intersection points for each scan line are as follows


y = 10 : (10, 10), ( 20, 10)
y = 11 : (11, 11), (20, 11) {Use formula x k +1 = x k + 1 / m , m being the slope}
y = 12 : (12, 12), ( 20, 12)
y = 13 : (13, 13), (16, 13), (16, 13), ( 20, 13)
y = 14 : (14, 14), (15.5, 14), (16, 14), (20, 14)

45
Computer Graphics In practice you need not perform intersection calculation for the scan line
y = 15 . You only need to calculate the intersection points for scan lines
y such that y min ≤ y + 1 / 2 ≤ y max .

(c) You need not perform this step since intersection points are already in
sorted order with increasing x-values.

(d) You can now make pairs easily.

Step 5: For y = 10 , all pixels starting from (10, 10) to (19, 10) will be plotted.
Similarly, you can find out pixels to be plotted for scan lines y = 11
and y = 12 . For y = 13 , two pairs of intersection points are given by
{(13, 13), (16, 13)} and {(16, 13), (20, 13)} . Pixels that have to be
plotted are from (13, 13) to (15, 13) and then from (16, 13) to
(19, 13) . You may proceed in the same way for other two scan lines.
The resulting filled polygon is shown in Fig. 4.
(20, 15)
(15, 15)

(10, 10) (20, 10)

Fig. 4: Polygon of Example 1 filled using scan line polygon fill algorithm.

Note that the polygon filling scheme will not fill the pixels on the horizontal
edge E 4 joining (16,15) and ( 20,15). But the boundary of the polygon will
display the edge. Similarly the vertex (15, 15) is plotted by virtue of it being a
boundary point. Further, the vertical edge E 5 = (20,15) – (20,10) is not plotted
since one needs to conform to the conditions of step 5.
***

You may now try to do the following exercises on your own.

Do the exercises no. 3-21, 3-23, 3-24, Chapter 3 on page 162 of the book.

As you saw the implementation of scan line polygon fill requires that
boundaries should be straight line segments. The seed fill algorithms do not
require any such constraints. You only need to know an interior point of the
closed boundary object to fill it. This interior point is called a seed point.
However, determination of interior point for complex polygons such as the one
shown in Fig. 2 itself is a challenging task. The following methods are used to
determine an interior point of the area to be filled.
1. Odd-even rule,
2. Nonzero winding number rule.
46
Once an interior point of the object is determined, the boundary-fill algorithm More Output Primitives and
Geometric Transformations
or, flood-fill algorithm may be applied to fill the given area.

To know about these methods for determining an interior point and algorithms
to fill the given area, particularly for curved boundary areas, you may read the
following.

Read Sec. 3-11, Chapter 3, pages 145-150 of the book.

Here it would be important to mention that in OpenGL a fill area must be


specified as a convex polygon. Infact OpenGL makes some strong restrictions
on the definition of a primitive polygon. It requires all interior angles of a
polygon must be less than 180 degree, there should be no crossing edges, and a
single polygon fill area can be defined with only one vertex list, which
eliminates the possibility of holes in the polygon interior.

Fig. 5 shows some examples of valid and invalid polygons in terms of OpenGL
definitions. However, there is no restriction on the number of line segments
making up the boundary of a convex polygon.

Valid Polygons Invalid Polygons


Fig. 5: Valid and Invalid Polygons in OpenGL.

Main reason for OpenGL specifications on valid polygon types is that simple
convex polygons can be rendered quickly and hardware for fast polygon-
rendering could be made possible for such a restricted class of polygons.
Therefore to maximize the performance with the given hardware, OpenGL only
processes convex polygons correctly. Ofcourse concave polygons are
processed by splitting them into convex sub-polygons but the result may not be
correct. Many graphics processors triangulate the polygons to apply filling
procedure.

However, as you know, many real-world surfaces consist of polygons, which


may be non-convex, self intersecting or even having holes. Since all such
polygons can be constructed from union of simple convex polygons or more
precisely triangles, some routines to build more complex objects are provided
in the GLU library. These routines take complex objects and tessellate them,
or decompose them into simpler OpenGL polygons that can then be rendered.
Later in Unit 4 you will study certain methods of splitting a concave polygon
along some of its edges for the purpose of making it a union of convex
polygons. Here we give you some of the basic OpenGL polygon fill-area
functions that are commonly used in rendering polygons.

OpenGL procedures for fill polygons are similar to those for describing a point
or a polyline with only one exception for filling a rectangular shape. As you
have already seen for rectangles and polylines, glVertex function is used to
input the coordinates for a single polygon vertex, and a complete polygon is

47
Computer Graphics described with a list of vertices placed between a glBegin/ glEnd pair.
However, for displaying a rectangle, the function has an entirely different
format. This is because rectangles are simple and one of the most frequently
used polygons. The procedure directly accepts vertex specifications in the
xy-plane and is given by
glRect*(x1, y1, x2, y2);
Here ( x1, y1) is one corner and ( x 2, y 2) is diagonally opposite corner of this
rectangle. Meaning of * after glRect tells that you may use one of the
following suffix codes for specifying the coordinate data type. These codes are
as follows: i – integer, s – short, f – float and d – double. For example, the
following statement defines a rectangle with four corners
( 200, 100), (350, 100), (350, 250), (100, 250) having integer data.
glRecti(200, 100, 350, 250);
The rectangle is displayed with edges parallel to the coordinate axes. Further,
if you want to express coordinates as array elements then you can also use the
suffix code v for vector. Following piece of code helps you understand how to
use array elements as vertices of rectangle.
int vertex1[ ] = {200, 100};
int vertex2[ ] = {350, 250};
glRect2iv(vertex1, vertex2);

For a complete code for generating a rectangle, you may refer to Appendix A
or the OpenGL reference manual given online at the url
http://www.opengl.org/documentation/

Coming back to a general polygon, you will find that by default a polygon is
filled in a solid color, determined by the current color settings. One can also
use a pattern to fill a polygon. Each polygon has two faces: a back face and a
front face. You can set the fill color and other attributes for each face
separately. Vertices are specified in a counterclockwise order in OpenGL for
the front face of the polygon. Following piece of code uses OpenGL primitive
constant GL_POLYGON to fill the polygon area specified with the six
vertices as shown below.
glBegin(GL_POLYGON);
glVertex2i(100,100);
glVertex2i(150,100);
glVertex2i(200,150);
glVertex2i(200,200);
glVertex2i(150,250);
glVertex2i(100,250);
glEnd( );

Following code will help you understand how to use scalar and vector vertex
specifications in drawing polygons.
void filledPoly (void) //Give two different specs.
{
glClear (GL_COLOUR_BUFFER_BIT); // Clear display window.
glColour3f (0.0, 0.0, 1.0); // Set current colour to
blue
48
int vertex[][2] = {{150, 100},{150, 120},{160, 130}, More Output Primitives and
{170,120}, {170,100}}; Geometric Transformations
glBegin(GL_POLYGON);
glVertex2i(50,100);
glVertex2i(50,120);
glVertex2i(60,130);
glVertex2i(70,120);
glVertex2i(70,100);
glEnd( );

glBegin(GL_POLYGON);
glVertex2iv(vertex[0]);
glVertex2iv(vertex[1]);
glVertex2iv(vertex[2]);
glVertex2iv(vertex[3]);
glVertex2iv(vertex[4]);
glEnd( );

}
void myDisplay() {
filledPoly(); //Fill polygon function call
glFlush(); // Process the OpenGL routines as quickly
as possible.
}

You may now try the following exercises.

E1) For the following polygon, prepare an initial sorted edge list and then
make the active edge list for scan lines y = 5, 20, 30, 35 . Coordinates of
the vertices are as shown in Fig. 6.

40

30

20

10

0
0 10 20 30 40 50

Fig. 6

E2) For the polygon shown in Fig. 7 on the next page, how many times will
the vertex V1 appear in the set of intersection points for the scan line
passing through that point? How many times will you count it when you
form the pairs of intersection points? Justify your answer.

49
Computer Graphics V0 V5

V1 = V4

V2 V3

Fig. 7

E3) Use an appropriate OpenGL primitives to draw the shapes given in Fig. 8.

(a) (b) (c)

Fig. 8

You may also try the following exercises from the book.

Do the exercises no. 3-27 to 3-28, Chapter 3 on page 162 of the book.

Let us now discuss the character generation techniques.

3.3 CHARACTER GENERATION


You know that graphics displays often contain components which are text
based. For example graph labels, annotations, descriptions on data flow
diagrams, fill patterns containing text strings and information for simulation
and visualization applications. Routines for generating character are available
in most of the graphics systems and graphics application softwares. Here you
will learn some simple techniques used in creating texts.

There are two main approaches followed in character or font generation (i)
Bitmap font method (ii) Outline font method. In the bitmap method, a matrix of
bits is formed which approximates the shape of the font. Every entry in the
matrix corresponds to a pixel and those pixels are plotted for which the matrix
entry is 1.

In the outline method the font boundary shape is approximated by spline


curves. You have already done some practice on character generation using
multiple Bezier curve segments (see the solution to E8), Unit 2). To know the
details about the two methods of character generation read the following.

Read Sec. 3-14, pages 151-153 and do the exercises 3-29, 3-30, Chapter
3 on page 162 of the book.

In order to understand how fonts are converted to bitmap, you are advised to
begin with a letter and draw it on a graph paper sheet. Once you plot it on the
50 graph paper, check its span in terms of number of maximum x and y units it
covers (see Fig. 9). Consider a matrix of that order. For example, in Fig. 9(a), More Output Primitives and
Geometric Transformations
the letter R spans in an area corresponding to the matrix of order 9 × 14 . In
your drawing all those graph square units which fall inside the font boundary
will have their corresponding value 1 in the matrix and those falling out will
have bitmap 0 .

R (a)

Fig. 9
(b)

In the outline method we simply identify a few shape control points and draw
the corresponding Bezier curves for different segments of the outline.

Some predefined character sets are available in the OpenGL Utility Toolkit
(GLUT). So you need not create fonts as bitmap shapes unless you want to
display a font that is not available in GLUT. For example, you may want to
generate a font of your own mother tongue. The GLUT library contains
routines for displaying both bitmapped and outline fonts. Bitmapped GLUT
fonts are rendered using the glutBitmapCharacter function, and the outline
fonts are generated with polyline (GL_LINE_STRIP) boundaries. We can
display a bitmap GLUT character with
glutBitmapCharacter (font, character);

where parameter font is assigned a symbolic GLUT constant identifying a


particular set of type faces, and parameter character is assigned either the
ASCII code or the specific character we wish to display. Thus, to display the
upper-case letter “A”, we can either use the ASCII value 65 or the designation
'A'. Similarly, a code 97 corresponds to the lower-case letter 'a' and so on.
You can also use fixed-width fonts and proportionally spaced fonts. For
example, to select a fixed-width font of 8 × 13 , you need to assign either
GLUT_BITMAP_8_BY_13 or GLUT_BITMAP_9_BY_15. For a
proportionally spaced font of Times Roman type with size 10 point, you need
to select GLUT_BITMAP_TIMES_ROMAN_10 to parameter font. The
choice GLUT_BITMAP_TIMES_ROMAN_12 is also available. For
example, to draw the bitmap font Times Roman of size 10 at the raster position
(20, 20), you need to use the following statements.
glRasterPos2i(20, 20);
glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_10,'B');

An outline character is displayed with the following function call.


glutStrokeCharacter(font, character);

You can assign parameter 'font' either the value GLUT_STROKE_ROMAN,


which displays a proportionally spaced font, or the value
GLUT_STROKE_MONO_ROMAN, which displays a font with constant

51
Computer Graphics spacing. You can specify and control the size and position of these characters
by using certain geometric transformations that you will study in the next
section. You may refer to OpenGL reference manual for more options.

The following code demonstrates displaying the string 'OpenGL' using


glutStrokeCharacter ( ).
char text[] = {'O', 'p', 'e', 'n','G', 'L'};
for(int k = 0; k < 6 ; k++)
glutStrokeCharacter(GLUT_STROKE_ROMAN, text[k]);

You may now test your understanding by trying out the following exercises.

E4) Use the outline method to plot the following font boundaries (style should
remain the same). Implement your method in C language using OpenGL.

G t
E5) Design a bitmap for the English vowels A, E, I, O, U for two different
sizes and then implement the bitmaps to plot these vowels on the display.
Keep in mind that baseline of all the bitmaps should remain the same
when plotting, just as it is printed in English language.

In the next section we shall study tools needed to gain more flexible control
over the size, orientation and position of the objects of interest.

3.4 TWO-DIMENSIONAL GEOMETRIC


TRANSFORMATIONS
When a real life object is modelled using shape primitives, there are several
possible applications. You may be required to do further processing with the
objects. For example, suppose you have created a chair model. You may then
like to view it from different angles, or you may like to create another chair
model with a slight variation in its shape or size. Similarly, you may want to
show an object moving from one position to another along a path, or rotate it
about a given pivot point. All this can be achieved by using simple
mathematical transformations called affine transformations. Since these
transformations help change the geometry of the object in terms of shape, size
or position, we call them geometric transformations. Present section deals with
two-dimensional (2D) geometric transformations. 2D geometric
transformations can be broadly classifies as – (i) Rigid body transformations
(ii) Non-rigid body transformations. Rigid body transformations do not change
the object dimensions, while non-rigid body transformations modify the
dimensions of the object. For example, when you resize a rectangle, the
transformation is non-rigid body, but when you rotate an object its shape or
52
size does not change, hence it is rigid body transformation. To know about More Output Primitives and
Geometric Transformations
these transformations in detail, you may read the following.

Read Secs. 5-1 and 5-2, Chapter 5, pages 204-210 of the book.

You have seen that a 2D geometric transformation can be represented by a


3 × 3 matrix. Since matrix multiplication is not commutative in general, it
follows that the composite of two primitive transformations has to be carefully
constructed. For example, a scaled object shifted to a position after scaling or
an object scaled after shifting, may give different results as illustrated in the
following example.

Example 2: Consider a triangle with vertices in 2D plane given by


(0, 0), (1, 0) and (0, 1) (called unit triangle). Translate it by (1, 1) and then
scale in each coordinate direction by 1 / 2 . You get a triangle with vertices
(1 / 2, 1 / 2), (1, 1 / 2) and (1 / 2, 1) . Now if you first scale by 1 / 2 and then
translate it by (1, 1) , you will get a different triangle with vertices
(1, 1), (3 / 2, 1), (1, 3 / 2) .
***

Let us consider one more example to obtain the composite of translation and
rotation matrix.

Example 3: Let ∆ be a triangle with vertices (0, 0), (2, 0) and (1,1) . You
want to shift it to a position (3, 2) ((0, 0) transformed to (3, 2)) and then rotate
it about the point ( 4, 3) by an angle of π / 4 . The composite of translation and
rotation matrices is given by

 1 1 1 3   1 1 
 − 4(1 − )+ − 4
 2 2 2 2  1 0 3  
 2 2 

 1 1
3(1 −
1
)−
4  0 1 2  =  1 1
3 − 2 .
 2 2 2 2    2 2 
 0 0 1  0 0 1   0 0 1 
   
   
***

You may now try the following exercise.

E6) Magnify a triangle with vertices A = (1, 1), B = (3, 1) and C = (2, 2) to
twice its size in such a way that A remains in its original position.

There are a few important transformations which are not primitive in the sense
that they can be expressed as compositions of translation, rotation and scaling.
These transformations are used extensively in many application software using
graphics. Two such transformations are reflection and shear. When you watch
a cartoon animation movie for example, many a times you will find that shapes
are deformed and a cartoon character is shaking like a pendulum standing at
one place. This is basically an application of a continuous shear transformation
to give you the appearance of a shaking body. Similarly you will find plenty of
53
Computer Graphics applications of reflection in many ad films, cartoon films and also in
engineering design softwares. For the details about composite and other
transformations read the following.

Read Secs. 5-3 to 5-6, pages 211-228 of Chapter 5 and Secs 6-1 to 6-3,
pages 237-242 of Chapter 6 of the book.

Following OpenGL routines are available for applying 2D primitive


transformations.
• glScaled(sx,sy,1.0): Scale by sx in x coordinate, by sy in y-
coordinate and no scaling in z coordinate.
• glTranslated(dx,dy,0.0): Translate by dx in x direction, by dy
in y-direction and no translation in z direction.
• glRotated(angle,0,0,1): Rotate by 'angle' degrees in
anticlockwise direction about the origin. The vector (0,0,1) specifies
rotation about z-axis.

Following simple code demonstrates how these primitives can be applied in a


C-program. As in the case of character generation, you only need to change
your display function as follows.

//Translation, rotation, scaling demo

void myDisplay ( void ) {


glClear (GL_COLOUR_BUFFER_BIT );
glColour3f(0.0, 1.0, 0.0);

for(float angle=0; angle <30; angle=angle + 5)


{
glTranslated(-0.5f, -0.5f, 0.0f);
//scaling and rotation with (0.5,0.5) as the pivot point
glScaled(0.7,0.7,10);
glRotated( angle, 0.0, 0.0, 1.0);
glTranslated(0.5f, 0.5f, 0.0f);
glBegin(GL_LINE_LOOP);
glVertex3f(-0.5, -0.5, 0.0);
glVertex3f(1.0, -0.5, 0.0);
glVertex3f(1.0, 1.0, 0.0);
glVertex3f(-0.5, 1.0, 0.0);
glEnd();
}
glFlush();
}

You may now check your understanding of 2D geometric transformations by


trying out the following exercises.

E7) Write a code to continuously rotate a square about a pivot point.

E8) A 2D geometric shape is rotated about a point with coordinates (1, 2) by


90  in a clockwise direction. Then, the shape is scaled about the same
point in the x-coordinate by 2 times and in the y-coordinate by 3 times.
Finally, the shape is reflected about the origin. Derive the single
54 combined transformation matrix for these operations.
More Output Primitives and
Geometric Transformations
E9) If you perform a x-direction shear transformation and then a y-direction
shear transformation, will the result be the same as the one which is
obtained when it is simultaneous shear in both the directions? Justify
your answer by appropriate reasoning.

Also, you may try the following exercises from the book.

Do the exercise no. 5-6, 5-9 and 5-16, Chapter 5 on pages 233-234 of the
book.

We now end this unit by giving a summary of what we have covered in it.

3.5 SUMMARY
In this unit, we have covered the following:

1. Algorithms for filled-area primitives. These algorithms are classified


into two categories (i) Scan line algorithms (ii) Seed fill algorithms.
2. A scan line algorithm determines the overlap intervals of the polygon
with each scan line to obtain interior points of the polygon for assigning
those points the desired color.
3. A seed fill algorithm starts with a known initial interior point of the
polygon and spreads out to determine other interior points to fill the given
closed area with specified color. Four connected and eight connected
pixels are used to determine other interior points for painting with
specified color.
4. Some other approches to area filling are
• Scan line polgon fill algorithm
• Boundary fill algorithm
• Flood fill algorithm.
5. Boundary fill algorithm is suitable when the boundary has single color
while flood fill algorithm is more suitable for filling regions which are
defined with boundary having more than one color. For example map of
a country surrounded with other countries.
6. These basic algorithms provide methods for filling areas with a single
(solid) color. Modifications can be made for multi color filling or pattern
filling.
7. OpenGL supports filling polygon with a solid color or with pattern. The
restriction is that the polygon must be convex. To draw a filled convex
polygon with vertices ( x 0, y0), ( x1, y1), …, ( xN, yN) having floating
point values in x and y coordinates, use the following piece of code:

glBegin(GL_POLYGON);
glVertex2i(x0,y0);
glVertex2i(x1,y1);

55
Computer Graphics ....
....
glVertex2i(xN,yN);
glEnd( );

8. Two basic approaches used in character generation are – Bitmap method


and outline method.
9. All 2D geometric transformations can be obtained by using appropriate
combinations of three primitive transformations (i) translation
(ii) rotation (iii) scaling. Shear and reflection are two important
transformations which can also be derived from primitive
transformations. But looking into their vast applications, they are
discussed separately and are treated as primitive transformations in many
graphics packages.
10. 2D transformations can be classified as rigid body or non-rigid body
transformations. Rigid body transformations keep the shape and size of
the objects intact whereas non-rigid body transformations do affect the
shape and/or size of the object. Translation and rotation are examples of
rigid body transformations. Other transformations come under non-rigid
body transformations.
11. All 2D geometric transformations can be expressed as 3 × 3 matrices,
with points in 2D plane represented in homogeneous coordinates.
12. While constructing a composite transformation, one should be clear
about the sequence in which transformations have to be applied, as these
are not commutative in general.

3.6 SOLUTIONS/ANSWERS
Exercises given on pages 49-50 of the unit.

E1) First label the vertices and edges as shown in the Fig. 10.

V6 V4
40
E5 E4

30
E6 V5
V0 E7
20
V7 E3
E0 V2
10
E1
E2
0 V1 V3
0 10 20 30 40 50

Fig. 10

Start with y = 0 and continue till y = 40 . For y = 0 there are four


edges, namely E 0 , E1 , E 2 and E 3 . The sorted edge table is created as
56 follows:
More Output Primitives and
Geometric Transformations

y=30 40 30 –1 40 30 1

y=20 40 20 –½ (Horizontal edge ignored)

y=0 20 20 –½ 10 20 1 10 40 –1 40 40 0

Active edge lists (AEL) for scan lines y = 5, 20, 30, 35 are as follows.
y = 5, AEL = {E 0 , E1 , E 2 , E 3 }
y = 20, AEL = {E 6 , E 3 }
y = 30, AEL = {E 6 , E 5 , E 4 , E 3 }
y = 35, AEL = {E 6 , E 5 , E 4 , E 3 }

E2) The vertex V1 = V4 will be counted once only since it is neither a


maximum nor a minimum for y coordinates of the pairs of edges
{V0 V1 , V1 V2 } and {V3 V4 , V4 V5 } .

E3) Fig. 8(a) is made up of quadrilaterals. Fig. 8(b) is a strip of triangles


while Fig. 8(c) is a fan of triangles. You need to make use of
glBegin()/glEnd() pair with argument GL_QUADS for Fig. 8(a),
GL_TRIANGLE_STRIP for Fig. 8(b) and GL_TRIANGLE_FAN for
Fig. 8(c). Suppose in Fig. 8(a) vertices V0 through V8 have following
coordinates. V0 = (10, 50), V1 = (20, 30), V2 = (10, 10), V3 = (50, 50),
V4 = (50, 30), V5 = (50, 10), V6 = (100, 50), V7 = (80, 30),
V8 = (100, 10)

V0 V3 V6

V1 V7
V4

V2 V5 V8

A sample code for Fig. 8(a) is as follows.

glBegin(GL_QUADS);
glVertex2f(20,30); // Quad1
glVertex2f(10,10);
glVertex2f(50,10);
glVertex2f(50,30);
glVertex2f(50,30); //Quad2
glVertex2f(50,10);
glVertex2f(100,10);
glVertex2f(80,30);
glVertex2f(50,50); //Quad3
glVertex2f(50,30);
glVertex2f(80,30);

57
Computer Graphics glVertex2f(100,50);
glVertex2f(10,50); //Quad4
glVertex2f(20,30);
glVertex2f(50,30);
glVertex2f(50,50);
glEnd();

You may also use vector form of vertices to draw the figure. Use similar
code for the other two cases.

Exercises given on pages 162 of the book.

3-21 Using midpoint algorithm, you can find out the curve position for each
scan line y . The two positions will help you find out the intersection
points and the interior region. Suppose you have identified a pixel
x c + x , y c + y) as the next point to be plotted on the positive quadrant
with respect to the centre of ellipse, then the scan line intersection pixel
pair will be {( x c − x , y c + y), ( x c + x , y c + y)} . Assign the fill color to
all pixels lying between these two pixels on the scan line. Modify the
function ellipsePlotPoints(int xCenter, int yCenter, int
x, int y) of the code of Midpoint circle generation code given in the
book on pages 129-130 is as follows.

void ellipsePlotPoints(int xCenter, int yCenter, int x,


int y)
{
glBegin(GL_POINTS);
for(int xx = xCenter - x ; xx< xCenter + x ; xx++)
{
glVertex2i(xx, yCenter + y);
glVertex2i(xx, yCenter - y);
}
glEnd();
}

3-23 Let P be a given polygon with N vertices given by


Vi = ( x i , y i ), i = 0, 1, …, N − 1 in the counter clockwise orientation.
Find out x min = min i {x i }, x max = max i {x i } and
y min = min i {y i }, y max = max i {y i }

Let Q = ( x , y) be the point to be tested. Initialize the winding number


wn = 0 . If any of the following conditions is satisfied Q cannot be
inside the polygon.

(i) x < x min (ii) x > x max (iii) y < y min (ii) y > y max

In case none of the conditions is satisfied, you are required to have a ray
starting from Q and extending to a distant point from the polygon and
then construct a vector in the direction of this ray. For simplifying the
computations, you can choose a vector u from Q in the direction (1, 0) .
Basically, this vector is on the scan line on which point Q lies. If no
vertex is on this scan line, your choice of vector is correct. Else choose a
58 vector with a slightly modified direction so that no vertex falls on the ray
from Q in the chosen direction. Create an active edge list (AEL) for this More Output Primitives and
Geometric Transformations
ray. Each edge whose one of the x-extents is greater than x is included
in the AEL. Next make a vector v perpendicular to u. If u = ( u x , u y ), v
can be defined as v = (− u y , u x ) . For each edge Vi Vi+1 in the AEL, take
the dot product of the vector Vi +1 − Vi with v . Let d i = ( Vi+1 − Vi ).v
(dot product). If d i > 0 , the edge Vi Vi+1 crosses the ray from right to
left. Update the wn = wn + 1 , else the edge crosses the ray from left to
right and wn = wn − 1 . When all the edges are processed and wn ≠ 0 , the
point Q is inside the polygon else it is outside.

3-24 For each scan line use the winding number rule to obtain interior points
on the scan line by counting and updating winding number for each edge
crossing on the AEL. For example, if AEL contains the first edge as E0.
Then as the pixel on the scan line is incremented and crosses E0, winding
number is updated. It will be either decremented by 1 or incremented by
1 depending whether the edge is crossing the scan line from left to right
or vice versa. Keep updating the winding number for each edge crossing.
This way on each scan line, you will identify inside/outside pixels.

3-27 An ellipse can be properly filled using 4-connected boundary fill method.
In case of 8-connected boundary fill method, some of the pixels that you
set for frame buffer will be outside and hence will be redundant. As a
result in 8-connected boundary fill, you will find some leakage in the
filling.

3-28 A code for flood fill algorithm with 4-connected cells is given on page
150 of the book. Implement the same code using OpenGL as follows.

void floodFill4 (int x, int y, int fillColour, int


interiorColour)
{
int colour;
/* Set current colour to fillColour, then perform
following operations. */
getPixel (x, y, colour);
if (colour = interiorColour)
{
// Set colour of pixel to fillColour.
setPixel (x, y);
floodFill4(x + 1, y, fillColour, interiorColour);
floodFill4(x - 1, y, fillColour, interiorColour);
floodFill4(x, y + 1, fillColour, interiorColour);
floodFill4(x, y - 1, fillColour, interiorColour);
}
}

Exercises given on page 52 of this unit

E4) Here letter G is produced using linear segments for outline method. You
may also use Bezier curves of higher degree to approximate the outline of
the character. The outline will be drawn using the code given here. For
filling the interior, you need to employ one of the fill area methods.
Vertices of the line segments are given as follows.

59
Computer Graphics GLint vt[][2]={{315,245}, { 315,195}, {316,190},{333,183},
{ 251,183}, {263,186}, { 270, 190},{272,195}, {272,225}, {
253, 238}, {242,236}, {235,229}, {231,219}, {227,198}, {
227,169}, { 228,152}, {233,133}, {242,124}, {248,120}, {
253,120}, {269,119}, { 273,121}, { 280,126}, {292,134}, {
299,141}, {305,149}, {312,162}, {312,111}, {307,110}, {307,
120}, {304, 130}, {301, 133}, {298, 131}, { 289, 125},
{279, 120}, {266, 113}, {251, 109}, {239, 110},{229,110},
{219,114}, {208,120}, {196,129}, {190,138}, {185,148},
{181, 158}, {180,172},{180,184}, {180,196}, {184, 203},
{189, 214}, {198, 226}, {211, 235}, {220,242}, {230,245},
{245,247}, {260, 247}, {271, 241}, {284, 236}, {293, 231},
{299, 231}, {306, 235}, {315,245}};

The code uses simple line loop to produce the character.

void lineG(void)
{glBegin(GL_LINE_LOOP);
for(int i = 0;i<62;i++) {
vt[i][1]=480-vt[i][1];
glVertex2iv(vt[i]);}
glEnd();
}

Call the function lineG in your display function. Similar is the code for
letter ' t'.

E5) Here we discuss the bitmap for letter 'E'. You need to place the character
'E' on a square grid of sufficiently large size. If the character's area is
overlapping more than 50% of a cell of the square grid, assign that cell a
bit 1, otherwise assign the cell bit 0. This way you will have a
rectangular grid having cells either assigned 0 or 1. Map this onto a
rectangular matrix and plot the character 'E' using the bitmap method as
shown in Fig. 11.

1 1 1 1 1 1 1
 
1 1 1 1 1 1 1

1 1 0 0 0 0 1
1 1 0 0 0 0 0
1 1 0 0 0 0 0

 
1 1 0 0 0 0 1
1 1 1 1 1 1 1
1 1 0 0 0 0 1
 
1 1 0 0 0 0 0
1 1 0 0 0 0 0
1 1 0 0 0 0 1
 
 1 1 1 1 1 1 1
1 1 1 1 1 1 1 

Fig. 11

Other characters can also be modelled using the same technique. Repeat
your process for a larger size font also. Or you can use the same matrix
to double the size of the matrix by mapping a cell to four cells with the
same bit value of the cell as shown in Fig. 12 on the next page.
60
More Output Primitives and
Geometric Transformations

1 1 1 1

1 1 1 1

0 0 0 0
1 1 0 0 0 0

0 0

Fig. 12

Exercise given on pages 53 of this unit

E6) You need to apply scaling by keeping the point (1, 1) fixed. For this, you
require following sequence of operations. (i) translating the triangle so
that (1, 1) is at the origin (ii) scaling the triangle by a factor of 2 in both
the coordinates (iii) translating the triangle back so that A is back to
(1, 1) . The sequence of transformation is as follows.

 x   x − 1 2(x - 1) 2(x - 1) + 1 2x - 1


 y  →  y − 1 → 2(y - 1)  → 2(y - 1) + 1 = 2y - 1
         

The triangle is transformed to a triangle with vertices (1, 1), (5, 1), (3, 3) .

Exercises given on pages 54-55 of this unit

E7) #include <GL/glut.h>

static GLfloat rotat=0.0;

void init(void);
void display(void);
void reshape(int w, int h);
void rotate(void);

int main()
{
glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE);
glutInitWindowSize(500,500);
glutInitWindowPosition(100,100);
glutCreateWindow("Moving squares");
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutIdleFunc(rotate);
glutMainLoop();
}

void init(void){
glClearColour(0.0,0.0,0.0,0.0);
}

void display(void)
{ glClear(GL_COLOUR_BUFFER_BIT);
glPushMatrix(); //Push the transformation matrix to stack
61
Computer Graphics glTranslatef(-50.0f,-50.0f,0.0);
//Translate the pivot point to origin
glRotatef(rotat,0.0,0.0,1.0); // Rotate about origin
glTranslatef(50.0f,50.0f,0.0);
//Translate pivot point back to its position
glColour3f(0.0,0.0,1.0); //Set colour of square
glRectf(-50.0,-50.0,50.0,50.0); //Draw square
glPopMatrix(); //Pop the matrix from stack
glutSwapBuffers(); // Swap buffers
}

void reshape(int w, int h)


{ glViewport(0,0,(GLsizei)w,(GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-250.0,250.0,-250.0,250.0,-1.0,1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

void rotate(void)
{ rotat+=0.1; //Continuously increse the rotation angle by
0.1
if(rotat>360.0)
rotat-=360.0;
glutPostRedisplay(); //send the current window for
redisplay
}

E8) The sequence of transformations is as follows.

− 1 0 0 1 0 1  2 0 0  0 1 0 1 0 − 1 
 0 − 1 0 0 1 2 0 3 0  − 1 0 0 0 1 − 2
     
 0 0 1 0 0 1  0 0 1  0 0 1 0 0 1 

From right to left the sequence is as follows- (i) Translate so that (1, 2)
moves to origin (ii) Rotate about the origin by − 90  (iii) Scale by ( 2, 3)
(iv) Translate back so that (1, 2) is at its previous position (v) Flip about
the origin. Single combined transformation can be obtained by
multiplying all these matrices.

E9) No, since an x-axis shear and then a y-axis shear will lead to following
matrix.
1 0 0 1 a 0 1 a 0
b 1 0 0 1 0 = b ab + 1 0
    
0 0 1 0 0 1 0 0 1
while a simultaneous shear in both the directions will be represented by
the following matrix.
1 a 0
 b 1 0
 
0 0 1

62
Exercises given on pages 233-234 of the book. More Output Primitives and
Geometric Transformations

5-6 (a) For rotation- Let R (θ) denote the rotation matrix about origin with an
angle θ . You know that R (θ1 + θ 2 ) = R (θ 2 + θ1 ) . Therefore two
successive rotations are commutative.

(b) For translation-

1 0 t x  1 0 u x  1 0 t x + u x  1 0 u x + t x 
0 1 t  0 1 u  = 0 1 t y + u y  = 0 1 u y + t y 
 y  y 
0 0 1  0 0 1  0 0 1  0 0 1 
1 0 u x  1 0 t x 
= 0 1 u y  0 1 t y 
0 0 1  0 0 1 

s x 0 0 v x 0 0 s x ⋅ v x 0 0  v x ⋅ s x 0 0
(c) 0 sy 0 0 vy 0 =  0 sy ⋅ vy 0 =  0 vy ⋅sy 0
 
 0 0 1  0 0 1   0 0 1  0 0 1
v x 0 0 s x 0 0
=  0 vy 0 0
 sy 0
 0 0 1   0 0 1 

5-9 Matrix for reflection about x-axis is given by


1 0 0
0 − 1 0
 
0 0 1

Counterclockwise rotation of 90 degrees has following matrix.


0 − 1 0
1 0 0
 
0 0 1

Then the composite matrix is the same as (5-51) of the book.

5-16 You need to set the base line of the font as the line relative to which shear
operation has to be performed. Then apply shear transformation to the
font outlines. Vector font definition will provide an array of vertices that
you will use to make the font outline. Out of those vertices choose the
one that has lowest x-coordinate value. Imagine a horizontal line passing
through that vertex and choose that line as the line about which you have
to consider shear operation. Define your matrix for shear operation and
then apply the matrix to the vertex array. Experience shows that a factor
of 1/3 or 1/4 could be chosen for shearing operation relative to base line
of the font.

63
Computer Graphics
3.7 PRACTICAL EXERCISES

Session 3
1. Implement the Scan line polygon fill algorithm for any arbitrary polygon
in C-language and then use your code to fill each of the following type of
polygon.
i) Convex polygon
ii) Concave polygon
iii) Self intersecting polygon.

Session 4
1. Implement the boundary fill algorithm and flood fill algorithm in
C-language and use your code to fill two different types of closed areas
such as
i) A Circle
ii) A self intersecting polygon
Compare the results of two algorithms for the self intersecting polygon.

2. Write a code using C-language to generate an arbitrary character by using


(i) bitmap method (ii) outline method. Use this code to generate three
different characters of your own mother tongue.

3. Write a code in C-language for character generation using the standard


OpenGL functions (both bitmap and outline style).

Session 5
1. Write a C-code for an interactive program which allows a user to draw a
polygon object in a window and then gives various choices of geometric
transformations on the polygon. Once the user selects a choice, the
transformed polygon is also plotted on the window. To begin with, you
can use a triangle in place of a polygon.

OR

2. Write a C-code that plots an object on the window and on the user’s click
of mouse on the window, the object starts rotating continuously until the
user presses the mouse again.

3. Use your C-code of character generation to draw the same character with
regular font type and italic font type by making use of shear
transformation.

64

You might also like