Friday, February 26, 2010

The Sutherland-Hodgeman Polygon Clipping Algorithm


 


 

1. Introduction

It is often necessary, particularly in graphics applications, to "clip" a given polygon with another. Figure 1 shows an example. In this article we'll look at the particular case where the clipping polygon is a rectangle which is oriented parallel with the axes. For this case, the Sutherland-Hodgeman algorithm is often employed. This is the algorithm that we will explore.


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 

Figure 1


 


 


 

2. The Sutherland-Hodgeman Algorithm

The Sutherland-Hodgeman polygon clipping algorithm is relatively straightforward and is easily implemented in C. It does have some limitations, which we'll explore later. First, let's see how it works.

For each of the four sides of the clipping rectangle, consider the line L through the two points which define that side. For each side, this line creates two planes, one which includes the clipping rectangle and one which does not. We'll call the one which does not the "clipping plane" (Figure 2).

 


 


 


 


 


 


 


 


 


 


 


 


 

L


 


 


 


 


 


 


 


 


 


 


 

Figure 2


 


 

For each of the four clipping planes, we must remove any vertices of the clipped polygon which lie inside the plane and create new points where the segments associated with these vertices cross the line L (Figure 3).


 


 


 


 


 


 


 


 


 


 


 

L


 


 


 


 


 


 


 


 


 


 


 

Figure 3


 


 

After clipping each of the four planes, we are left with the clipped polygon.


 


 

3. Limitations

This algorithm always produces a single output polygon, even if the clipped polygon is concave and arranged in such a way that multiple output polygons might reasonably be expected. Instead, the polygons will be linked with overlapping segments along the edge of the clipping rectangle (Figure 4). This may or may not be the desired result, depending on your application.

 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 

Figure 4


 


 


 

4. Practical Implementation


 

#include <math.h>

#include <assert.h>


 


 

#define CP_MAXVERT 32

#define CP_LEFT 0

#define CP_RIGHT 1

#define CP_TOP 2

#define CP_BOTTOM 3


 

struct rect{

double t; /* Top */ double b; /* Bottom */ double l; /* Left */ double r; /* Right */

};


 

struct point{ double x; double y;

};


 

To determine the correct course of action in the main algorithm, we'll need to be able to check if a given point is "inside" or "outside". In this sense, we take "outside" to mean "within the clipping plane". This function takes the point in question, the clipping rectangle, and a number indicating which side of the rectangle is forming the clipping plane.

 


 


 

int cp_inside( struct point p, struct rect r, int side )

{

switch( side ){

case CP_LEFT:

return p.x >= r.l;

case CP_RIGHT:

return p.x <= r.r;

case CP_TOP:

return p.y <= r.t;

case CP_BOTTOM:

return p.y >= r.b;

}

}


 

When one of the two points defi ning a line segment is "inside" and the other is "outside", we will need to create a new vertex where the segment intersects the clipping rectangle.

This function takes the "inside" point (p), the "outside" point (q), the clipping rectangle, and again the side in question.

It first finds the slope (a) and y-intercept (b) of the line segment pq. Now, depending on the side under consideration, either the x or y component of the new point is set equal to that of the bounding side and the remaining component is computed using the slope and intercept previously found. When considering the top or bottom sides, it is possible that the segment pq is vertical. This condition can be recognized when the slope (a) is infinite. In this special case, the x component of the new point will be the same as the x component of

p or q.


 

struct point cp_intersect( struct point p, struct point q, struct rect r, int side )

{

struct point t;

double a, b;


 

/* fi nd slope and intercept of segment pq */

a = ( q.y - p.y ) / ( q.x - p.x );

b = p.y - p.x * a;


 

switch( side ){

case CP_LEFT:

t.x = r.l;

t.y = t.x * a + b;

break;

case CP_RIGHT:

t.x = r.r;

t.y = t.x * a + b;

break;

case CP_TOP:

t.y = r.t;

if( isfi nite(a) )

t.x = ( t.y - b ) / a;

 

else


 

break;


 

t.x = p.x;

 

case CP_BOTTOM:

t.y = r.b;

if( isfi nite(a) )

t.x = ( t.y - b ) / a;

else

 


 


 

 


 


 


 

}


 

return t;

}


 

break;

t.x = p.x;

 


 


 

This function clips the clipped polygon against the clipping plane for a particular side of the clipping rectangle. Starting with the fi rst vertex in the clipped polygon, we consider each vertex (p) along with the one before it (s).


 

There are four ways that these two points may be arranged:

Both s and p are "inside": The point p should be included in the output polygon.

The point s is "outside" while p is "inside": The intersection of sp and the line L defi ning the clip- ping plane should be included in the output, followed by p.

The point s is "inside" while p is "outside": The intersection of sp and the line L defi ning the clip- ping plane should be included in the output.

The points s and p are both "outside": nothing need be done on this step.


 

void cp_clipplane( int *v, struct point in[CP_MAXVERT], struct rect r, int side )

{

int i, j=0;

struct point s, p;

struct point out[CP_MAXVERT];


 

s = in[*v-1];

for( i = 0 ; i < *v ; i++ ){

p = in[i];


 

if( cp_inside( p, r, side ) ){

/* point p is "inside" */

if( !cp_inside( s, r, side ) ){

/* p is "inside" and s is "outside" */ out[j] = cp_intersect( p, s, r, side ); j++;

}

out[j] = p; j++;

}else if( cp_inside( s, r, side ) ){

/* s is "inside" and p is "outside" */ out[j] = cp_intersect( s, p, r, side ); j++;

}


 

s = p;

}


 

/* set return values */

*v = j;

for( i = 0 ; i < *v ; i++ ){

in[i] = out[i];

}

}

 


 


 

The actual clippoly function is very simple; it calls the previous function (cp_clipplane) once for each side of the clipping rectangle. First, though, it performs a few tests to make sure that the input data make sense and that there will be room to store the output polygon.


 

void clippoly( int *v, struct point p[CP_MAXVERT], struct rect r )

{

/* sanity checks */

assert( *v <= CP_MAXVERT / 2 );

assert( r.l < r.r );

assert( r.b < r.t );


 

cp_clipplane( v, p, r, CP_LEFT ); cp_clipplane( v, p, r, CP_RIGHT ); cp_clipplane( v, p, r, CP_TOP ); cp_clipplane( v, p, r, CP_BOTTOM );

}


 


 


 

From : http://www.aftermath.net/

Determining if a point lies on the interior of a polygon


 

Solution 1 (2D)

Determining whether or not a point (x,y) lies inside or outside a 2D polygonally bounded plane. This is necessary for example in applications such as polygon filling on raster devices.

Consider a polygon made up of N vertices (xi,yi) where i ranges from 0 to N-1. The last vertex (xN,yN) is assumed to be the same as the first vertex (x0,y0), that is, the polygon is closed. To determine the status of a point (xp,yp) consider a horizontal ray emanating from (xp,yp) and to the right.

If the number of times this ray intersects the line segments making up the polygon is even then the point is outside the polygon.

Whereas if the number of intersections is odd then the point (xp,yp) lies inside the polygon.

The following shows the ray for some sample points and should make the technique clear.


0 will be considered even, the test for even or odd will be based on modulus 2, that is, if the number of intersections modulus 2 is 0 then the number is even, if it is 1 then it is odd.

The only trick is what happens in the special cases when an edge or vertex of the polygon lies on the ray from (xp,yp). The possible situations are illustrated below.


The thick lines above are not considered as valid intersections, the thin lines do count as intersections. Ignoring the case of an edge lying along the ray or an edge ending on the ray ensures that the endpoints are only counted once.

Note that this algorithm also works for polygons with holes as illustrated below


The following C function (Taken from website)

returns INSIDE or OUTSIDE indicating the status of a point P with respect to a polygon with N points.

#define MIN(x,y) (x < y ? x : y)

#define MAX(x,y) (x > y ? x : y)

#define INSIDE 0

#define OUTSIDE 1


 

typedef struct {

double x,y;

} Point;


 

int InsidePolygon(Point *polygon,int N,Point p)

{

int counter = 0;

int i;

double xinters;

Point p1,p2;


 

p1 = polygon[0];

for (i=1;i<=N;i++) {

p2 = polygon[i % N];

if (p.y > MIN(p1.y,p2.y)) {

if (p.y <= MAX(p1.y,p2.y)) {

if (p.x <= MAX(p1.x,p2.x)) {

if (p1.y != p2.y) {

xinters = (p.y-p1.y)*(p2.x-p1.x)/(p2.y-p1.y)+p1.x;

if (p1.x == p2.x || p.x <= xinters)

counter++;

}

}

}

}

p1 = p2;

}


 

if (counter % 2 == 0)

return(OUTSIDE);

else

return(INSIDE);

}


 

Another : It returns 1 for interior points and 0 for exterior points.

int pnpoly(int npol, float *xp, float *yp, float x, float y)

{

int i, j, c = 0;

for (i = 0, j = npol-1; i < npol; j = i++) {

if ((((yp[i] <= y) && (y < yp[j])) ||

((yp[j] <= y) && (y < yp[i]))) &&

(x < (xp[j] - xp[i]) * (y - yp[i]) / (yp[j] - yp[i]) + xp[i]))

c = !c;

}

return c;

}

Thursday, February 25, 2010

Turbo C/C++ Compiler

Below are few major reasons for why you should avoid using Turbo C/C++ compilers:


1. It is over a decade old and do not conform to the current standards.
2. Only 16bit DOS applications can be developed using that compiler.
3. Maximum amount of memory available for programs is 64kb, while modern applications need a lot more than that.
4. The compiler is buggy and does not issue proper diagnostic messages for erroneous programs.
5. There are no database libraries supporting this compiler.
6. There are no modern graphics libraries supporting this compiler.
7. Its C++ compiler is pre-standard, and do not have STL library.

There are many new good compilers available for free such as GCC, VC++ and Dev C++(MinGW), they conform to the standards well and are very rich in features.

There is also a new 32 bit Turbo C++ compiler called as Turbo C++ 2006 (Explorer edition) –This is not available for free download.

Wednesday, February 24, 2010

Friend Class Example Code from Classroom

#include<iostream.h>

#include<conio.h>


 

class beta;


 

class alpha

{


private:


int d;


public:

alpha()

{

    d = 9;

}


void display_beta(beta b);


friend beta;

};


 

class beta

{


private:


int d1;


 


public:

beta()

{

d1 = 10;

}


void display_alpha(alpha a)

{

cout << "\n Data in alpha object a is " << a.d;

}


void square_alpha_data(alpha a)

{


int sq = a.d * a.d;

cout << "\nSquare is : " << sq;

}


friend alpha;

};


 


 


void alpha :: display_beta(beta b)

{

cout << "\n Data in alpha object a is " << b.d1;

}


 


 

int main(void)

{

alpha a;

beta b;


 

clrscr();

a.display_beta(b);

b.display_alpha(a);

b.square_alpha_data(a);


 

getch();


return 0;

}

Static Member Function

// An Example Taken in Classroom


 

#include<iostream.h>

#include<conio.h>


 

class gamma

{


private:


static
int total;


int id;


public:

gamma()

{

total++;

id = total;

}

~gamma()

{

total--;

cout << "\n Destroying ID no = " << id;

}


static
void showtotal()

{

cout << "\nTotal is " << total;

}


void showid()

{

cout << "\n ID is " << id;

}

};


 


 

int gamma::total;


 

void main()

{

clrscr();

cout << endl << endl ;


 

gamma::showtotal();


 

gamma g1;


 

gamma::showtotal();


 

gamma g2, g3;


 

gamma::showtotal();


 

g1.showid();

g2.showid();

g3.showid();


 

cout << "\n................... End of Program";

getch();

}

Sample Code for Static Data Member

An Example from our class lecture

// static data

#include<iostream.h>

#include<conio.h>


 

class foo

{

private:

static int t;

int count;

public:


 

    foo()

     {

     t++;

     count = 0;

     }

    int get_cnt()

     {

     return t;

     }

     int get_count()

     {

     return count;

     }

};


 

int foo:: t;


 

void main()

{


 

clrscr();

// cout << "No object of foo created " << foo::t;


 

foo f1, f2, f3;


 

cout << "\nStatic Count is " << f1.get_cnt();

cout << "\nstatic Count is " << f2.get_cnt();

cout << "\nstatic Count is " << f3.get_cnt();


 

cout << "\nCount is " << f1.get_count();

cout << "\nCount is " << f2.get_count();

cout << "\nCount is " << f3.get_count();


 

/* foo f4, f5;


 

cout << "\nCount is " << f4.get_count();

cout << "\nCount is " << f5.get_count();

*/ getch();

}

Static Data Members and Static Member Functions

The static storage class specifier

Objects declared with the static storage class specifier have static storage duration, which means that memory for these objects is allocated when the program begins running and is freed when the program terminates. Static storage duration for a variable is different from file or global scope: a variable can have static duration but local scope.

The keyword static is the major mechanism in C to enforce information hiding.

C++ enforces information hiding through the namespace language feature and the access control of classes. The use of the keyword static to limit the scope of external variables is deprecated for declaring objects in namespace scope.

The static storage class specifier can be applied to the following declarations:

You cannot use the static storage class specifier with the following:

Linkage of static variables

A declaration of an object that contains the static storage class specifier and has file scope gives the identifier internal linkage. Each instance of the particular identifier therefore represents the same object within one file only. For example, if a static variable x has been declared in function f, when the program exits the scope of f, x is not destroyed:

#include <stdio.h>


 

int f(void) {

  static int x = 0;

  x++;

  return x;

}


 

int main(void) {

  int j;

  for (j = 0; j < 5; j++) {

    printf("Value of f(): %d\n", f());

  }

  return 0;

}

The following is the output of the above example:

Value of f(): 1

Value of f(): 2

Value of f(): 3

Value of f(): 4

Value of f(): 5

Because x is a static variable, it is not reinitialized to 0 on successive calls to f.


 


 

Static members

Class members can be declared using the storage class specifier static in the class member list. Only one copy of the static member is shared by all objects of a class in a program. When you declare an object of a class having a static member, the static member is not part of the class object.

A typical use of static members is for recording data common to all objects of a class. For example, you can use a static data member as a counter to store the number of objects of a particular class type that are created. Each time a new object is created, this static data member can be incremented to keep track of the total number of objects.

You access a static member by qualifying the class name using the :: (scope resolution) operator. In the following example, you can refer to the static member f() of class type X as X::f() even if no object of type X is ever declared:

struct X {

  static int f();

};


 

int main() {

  X::f();

}


 

Using the class access operators with static members

You do not have to use the class member access syntax to refer to a static member; to access a static member s of class X, you could use the expression X::s. The following example demonstrates accessing a static member:

#include <iostream>

using namespace std;


 

struct A {

  static void f() { cout << "In static function A::f()" << endl; }

};


 

int main() {


 

  // no object required for static member

  A::f();


 

  A a;

  A* ap = &a;

  a.f();

  ap->f();

}

The three statements A::f(), a.f(), and ap->f() all call the same static member function A::f().

You can directly refer to a static member in the same scope of its class, or in the scope of a class derived from the static member's class. The following example demonstrates the latter case (directly referring to a static member in the scope of a class derived from the static member's class):

#include <iostream>

using namespace std;


 

int g() {

   cout << "In function g()" << endl;

   return 0;

}


 

class X {

   public:

      static int g() {

         cout << "In static member function X::g()" << endl;

         return 1;

      }

};


 

class Y: public X {

   public:

      static int i;

};


 

int Y::i = g();


 

int main() { }

The following is the output of the above code:

In static member function X::g()

The initialization int Y::i = g() calls X::g(), not the function g() declared in the global namespace.


 


 

Static data members

The declaration of a static data member in the member list of a class is not a definition. You must define the static member outside of the class declaration, in namespace scope. For example:

class X

{

public:

static int i;

};

int X::i = 0; // definition outside class declaration

Once you define a static data member, it exists even though no objects of the static data member's class exist. In the above example, no objects of class X exist even though the static data member X::i has been defined.

Static data members of a class in namespace scope have external linkage. The initializer for a static data member is in the scope of the class declaring the member.

A static data member can be of any type except for void or void qualified with const or volatile. You cannot declare a static data member as mutable.

You can only have one definition of a static member in a program. Unnamed classes, classes contained within unnamed classes, and local classes cannot have static data members.

Static data members and their initializers can access other static private and protected members of their class. The following example shows how you can initialize static members using other static members, even though these members are private:

class C {

static int i;

static int j;

static int k;

static int l;

static int m;

static int n;

static int p;

static int q;

static int r;

static int s;

static int f() { return 0; }

int a;

public:

C() { a = 0; }

};

C c;

int C::i = C::f(); // initialize with static member function

int C::j = C::i; // initialize with another static data member

int C::k = c.f(); // initialize with member function from an object

int C::l = c.j; // initialize with data member from an object

int C::s = c.a; // initialize with nonstatic data member

int C::r = 1; // initialize with a constant value

class Y : private C {} y;


 

int C::m = Y::f();

int C::n = Y::r;

int C::p = y.r; // error

int C::q = y.f(); // error

The initializations of C::p and C::q cause errors because y is an object of a class that is derived privately from C, and its members are not accessible to members of C.

If a static data member is of const integral or const enumeration type, you may specify a constant initializer in the static data member's declaration. This constant initializer must be an integral constant expression. Note that the constant initializer is not a definition. You still need to define the static member in an enclosing namespace. The following example demonstrates this:

#include <iostream>

using namespace std;


 

struct X {

static const int a = 76;

};


 

const int X::a;


 

int main() {

cout << X::a << endl;

}

The tokens = 76 at the end of the declaration of static data member a is a constant initializer.

Static member functions

You cannot have static and nonstatic member functions with the same names and the same number and type of arguments.

Like static data members, you may access a static member function f() of a class A without using an object of class A.

A static member function does not have a this pointer. The following example demonstrates this:

#include <iostream>

using namespace std;


 

struct X {

private:

  int i;

  static int si;

public:

  void set_i(int arg) { i = arg; }

  static void set_si(int arg) { si = arg; }


 

  void print_i() {

    cout << "Value of i = " << i << endl;

    cout << "Again, value of i = " << this->i << endl;

  }


 

  static void print_si() {

    cout << "Value of si = " << si << endl;

//    cout << "Again, value of si = " << this->si << endl;

  }


 

};


 

int X::si = 77;       // Initialize static data member


 

int main() {

  X xobj;

  xobj.set_i(11);

  xobj.print_i();


 

  // static data members and functions belong to the class and

  // can be accessed without using an object of class X

  X::print_si();

  X::set_si(22);

  X::print_si();

}

The following is the output of the above example:

Value of i = 11

Again, value of i = 11

Value of si = 77

Value of si = 22

The compiler does not allow the member access operation this->si in function A::print_si() because this member function has been declared as static, and therefore does not have a this pointer.

You can call a static member function using the this pointer of a nonstatic member function. In the following example, the nonstatic member function printall() calls the static member function f() using the this pointer:

#include <iostream>

using namespace std;


 

class C {

  static void f() {

    cout << "Here is i: " << i << endl;

  }

  static int i;

  int j;

public:

  C(int firstj): j(firstj) { }

  void printall();

};


 

void C::printall() {

  cout << "Here is j: " << this->j << endl;

  this->f();

}


 

int C::i = 3;


 

int main() {

  C obj_C(0);

  obj_C.printall();

}

The following is the output of the above example:

Here is j: 0

Here is i: 3

A static member function cannot be declared with the keywords virtual, const, volatile, or const volatile.

A static member function can access only the names of static members, enumerators, and nested types of the class in which it is declared. Suppose a static member function f() is a member of class X. The static member function f() cannot access the nonstatic members X or the nonstatic members of a base class of X.

Inline functions and macros

XIV

Inline Function

Preprocessor Macro – good for declaring constnts eg. #define PI 3.14
Provide textual substitution
Each time the macro name is encountered with arguments, the arguments used in its definition are replaced by the actual arguments found.

1

It is a function provided by C++.
It is declared by using the keyword inline before the function protoype.

It is a a preprocessor directive provided by C.
It is declared by using the preprocessor directive #define before the actual statement.

2

Expressions passed as arguments to inline functions are evaluated once.

In some cases, expressions passed as arguments to macros can be evaluated more than once.
Every time you use an argument in a macro, that argument is evaluated.

#define max(a,b) (a>b?a:b)

void main()

{

  int a = 0;

  int b = 1;

  int c = max(a++, b++);

  cout << a << endl << b << endl;

}

The intention is to have the program print 1 and 2, but because the macro is expanded to:

int c = a++ > b++ ? a++ : b++;

b will be incremented twice, and the program prints 1 and 3. //try
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->

3

They are parsed by the compiler

They are expanded by the preprocessor at pre-compile time.

4

Inline functions follow all the protocols of type safety enforced on normal functions.
Argument types are checked, and necessary conversions are performed correctly.
The compiler performs return type checking, function signature before putting inline function into symbol table.
They can be overloaded to perform the right kind of operation for the right kind of data.

No such thing, hence, macros are more error prone as compared to inline functions. the The parameters are not typed (the macro works for any objects of arithmetic type).
No error checking is done during compilation.
Eg. you can pass strings to a macro that does some integer arithmetic

#define MAX(a, b) ((a < b) ? b : a)

 
 

int main( void)

{

  cout << "Maximum of 10 and 20 is " << MAX("20", "10") << endl;

    return 0;

}

5

Can be used for debugging a program as they are expanded at compile time and a break point caa be placed at the inline function adefinition and step into the method for debugging step by step.

Can not be used for debugging as they are expanded at pre-compile time.

6

Inline member functioncs can access the class's member data.

The preprocessor has no permission to access member data of a class and are thus useless even if used within a class. Thus they can not even be used as member functions.
Eg.

#define VAL (X::i) // Error

class X {

  int i;

public:
  ---};

7

  

Paranthesis need to be handled carefully as missing a pair or putting an extra pair may prouce undesirable results.

8

Inline functions may or may not be expanded by the compiler. It is the compiler's decision whether to expand the function inline or not.

Macros are always expanded.

9

It can be defined inside or outside the class.

It can not be defined insode the class.

10

Inline functions pass arguments by value, just like regular functions do. If the argument is an expression such as 4.5 +7.5 then the function passes the value of the expression 12 in this case.

Expressions passed into macros are not always evaluated before entering the macro body.

Macros don't pass by value.

Thus , #define square(x) x*x

:

b=square(4.5+7.5)

This will be replaced by : b=4.5+7.5*4.5+7.5

11

 Since type checking can be used in inline functions, theis problem can be avoided.

Expressions may expand inside the macro so that their evaluation precedence is different from what you expect.

Eg.

#define FLOOR(x,b) x>=b?0:1

Now, if expressions are used for the arguments

if(FLOOR(a&0x0f,0x07)) // ...

the macro will expand to

if(a&0x0f>=0x07?0:1)

The precedence of & is lower than that of >=, so the macro evaluation will surprise you.