Click here to Skip to main content
15,879,490 members
Articles / Programming Languages / C#

Convex Hull

Rate me:
Please Sign up or sign in to vote.
3.85/5 (22 votes)
11 Sep 2008CPOL3 min read 90.7K   4.2K   35   7
Computing the convex hull of a given set of points
CH2.JPG

Introduction

Imagine that you have a piece of wood and a number of metal pins come out from it randomly (as in the above image), and you picked up an elastic rubber band and open it wide with your hand to contain all the metal pins and let it go. You will observe that the elastic rubber band will shrink and snap around the metal pins (only the outer metal pins). These outer segments of the elastic band form the convex hull.

CH.jpg

Background

The Convex Hull of a given point P in the plane is the unique convex polygon whose vertices are points from P and contains all points of P. In other words, the convex hull of a set of points P is the smallest convex set containing P.

The convex hull is one of the first problems that was studied in computational geometry. In fact, convex hull is used in different applications such as collision detection in 3D games and Geographical Information Systems and Robotics.

Using the Code

The Algorithm

Note: Suppose that the convex hull segments are ordered clockwise, then a convex hull segment is a segment that does not have any point on its left side. If a segment has at least one point on its left, then we eliminate in from the convex hull segments.

Input: Given a set of points P in the plane

Output: Convex Hull segments

Initialize all possible line segments between the given points: 
For  i = 0  to i < number of segments 
{
      For j = 0 to j < number of all points   
        {
             get the points that are not in the current segment 
         }   
          Check if the segment is an Edge of the convex hull by 
		checking whether any of the  points 
       Is located on its left
       If it is not an edge remove it from the segment list and 
		reset the iterator i and continue

       Else increment the iterator i and continue 
} 

The Implementation

We have a black background Windows Form.. this will be our plane that we are going to pick the hull points from. .. OK ? …

Picking points on the Form will fire the mouse down event in which we render our points and store the picked points in a list (we will use this list in the algorithm).

First let's see what is done in mouse down event:

C#
private void frmConvexHull_MouseDown(object sender, MouseEventArgs e)
 {
    render(); //render any point or edge if any
    pID++;

    graphics = this.CreateGraphics(); // create the graphics
    renderPoint(e.X, e.Y, Color.Lime); render the picked point
    PointF p = new PointF(e.X,e.Y);
    SuperPoint sp = new SuperPoint(p,pID);
    pointsList.Add(sp); //store it in the list
 } 

That will lead us to the renderPoint() function. This function will render the point and something like a (sniper view of its target) around the point.

C#
private void renderPoint(float x, float y, Color c)
  {
	Pen p = new Pen(c,1.7f);
         graphics.DrawEllipse(p, x - 5, y - 5, 10, 10);
        graphics.DrawLine(p, x, y - 5, x, y - 5 - 2.5f);
         graphics.DrawLine(p, x - 5, y, x - 5 - 2.5f, y);
         graphics.DrawLine(p, x, y + 5, x, y + 5 + 2.5f);
         graphics.DrawLine(p, x + 5, y, x + 5 + 2.5f, y);
         graphics.FillEllipse(Brushes.Lime, x - 1.25f, y - 1.25f, 2.5f,2.5f);
  }

After we picked our points, we will proceed to compute their convex hull by pressing compute button... this will fire the btnCompute_Click() event handler:

C#
private void btnCompute_Click(object sender, EventArgs e)
{
   if (pointsList.Count > 0)
    {
      // creates all possible segments from the points list 
      // as I mentioned in the algorithm
     InitOrderdPoints();
      // go compute the convex hull
     Compute();
     //prepare a new convex hull by clearing the points list and 
     //the convex segments list
     prepareNewConvexHull();
    }
    else 
    {
      MessageBox.Show(&quot;Pick the hull points&quot;);
    }
 } 

Let's jump to the Compute() function and see what happens there:

C#
private void Compute() 
{
  List<SuperPoint> ProcessingPoints = new List<SuperPoint>();

  int i = 0;
  int j = 0;
   for (i = 0; i < Segments.Count; )
   {
    //ProcessingPoints will be the points that are not in  the current segment

     ProcessingPoints = new List<SuperPoint>(pointsList);
     //this loop prepares the ProcessingPoints list for each segment
       for (j = 0; j < ProcessingPoints.Count; )
       {
       if(Segments[i].contains(ProcessingPoints[j]))
        {
          //eliminating the points that are already in the current segment...
          //we don't need them
          ProcessingPoints.Remove(ProcessingPoints[j]);
          j = 0;
          continue;
         }
         j++;
       }

     //checking if the current segment is an edge or notBy calling isEdge function
      if(!isEdge(ProcessingPoints, Segments[i]))
      {
        Segments.Remove(Segments[i]);
        i = 0;
        continue;
     }
      else
     {i++;} 
 }
   //adding the segments and points to the lists to render
   renderEdges(Segments);
   List<SuperPoint> superPointsList = new List<SuperPoint>(pointsList);  
   List<Segment> segmentsList = new List<Segment>(Segments);
   renderingPoints.Add(superPointsList);
   renderingEdges.Add(segmentsList);
}

The Compute function will always jump to the function isEdge to check if the segment is an edge or not:

C#
private bool isEdge(List<SuperPoint> processingPoints, Segment edge)
{
   for(int k = 0; k < processingPoints.Count; k++)
    {  
      if(isLeft(edge, processingPoints[k].P))
      {
        return false;
      }
    }
  return true;
} 

And the isEdge function will always jump to the function isLeft to check if any of the points are located on the left of the segment or not.

C#
private bool isLeft(Segment segment, PointF r) 
{
  float D = 0;
  float px, py, qx, qy, rx, ry = 0;
  //The determinant 
  // | 1 px py | 
  // | 1 qx qy | 
  // | 1 rx ry | 
  //if the determinant result is positive the point is //left of the segment 
  px = segment.p.X;
  py = segment.p.Y;
  qx = segment.q.X; 
  qy = segment.q.Y;
  rx = r.X;
  ry = r.Y;
  D = ((qx * ry) - (qy * rx)) - (px *(ry - qy)) + (py * (rx - qx));

  if (D <= 0)
    return false;

return true;

In isLeft function, we will compute the determinant shown above in the function.

If the determinant result is positive, then the point is left of the segment then the function will return true indicating that a point is left of the segment .. hence isEdge will return false hence that segment will be removed from the segments list …. And so on until the remaining segments in the segments list are the real segments of the convex hull... And Finished... I hope that helps.

I didn't explain some helper functions such as InitOrderdPoints(), prepareNewConvexHull(), render() and renderEdges() etc., and GDI+ functions that are used in the code, because here we are concerned with just the algorithm of computing the convex hull.

Try it yourself ... download the source code and run the program.

History

  • 12th September, 2008: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Egypt Egypt
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Pritesh Aryan8-May-11 23:05
Pritesh Aryan8-May-11 23:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.