topical media & game development
basic-visual-02-ShamosHoey.c
? /
basic-visual-02-ShamosHoey.c
// ------------------------------------------------------------------------
// This program is complementary material for the book:
//
// Frank Nielsen
//
// Visual Computing: Geometry, Graphics, and Vision
//
// ISBN: 1-58450-427-7
//
// Charles River Media, Inc.
//
//
// All programs are available at www.charlesriver.com/visualcomputing/
//
// You may use this program for ACADEMIC and PERSONAL purposes ONLY.
//
//
// The use of this program in a commercial product requires EXPLICITLY
// written permission from the author. The author is NOT responsible or
// liable for damage or loss that may be caused by the use of this program.
//
// Copyright (c) 2005. Frank Nielsen. All rights reserved.
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// File: ShamosHoey.cpp
//
// Description: Detect whether a set of line segments intersect or not
// Implement in a STL fashion a simple but optimal algorithm
// to detect whether there exists an intersection point among a set of n segments in O(n log n) time
//
// Michael I. Shamos and Dan Hoey.
// "Geometric intersection problems" (FOCS 1976)
// In 17th Annual Symposium on Foundations of Computer Science
// pages 208-215, Houston, Texas, 25-27 October 1976. IEEE.
// ------------------------------------------------------------------------
include <stdafx.h>
include <vector>
include <algorithm>
include <windows.h>
include <GL/gl.h>
include <GL/glut.h>
define W 900
define H 900
using namespace std;
enum type {LEFT, RIGHT};
class point {
public:
double x,y;
int n;
type endpoint;
// Lexicographic order on the x-coordinate
bool operator < ( const point & rhs)
{return (x<rhs.x);}
bool operator > (const point & rhs)
{return (x>rhs.x);}
};
class segment {
public:
point A,B;
};
class Yline {
public:
static double x;
double a,b; // equation of nonvertical line y=ax+b
int n;
void setX(double xx) {x=xx;}
bool operator < ( const Yline & rhs)
{
// cout <<"\t\tGeneric dictionary (inferior): "<<x<<endl;
return (a*x+b<rhs.a*x+rhs.b);
}
bool operator > ( const Yline & rhs)
{
// cout <<"\t\tGeneric dictionary (inferior): "<<x<<endl;
return (a*x+b>rhs.a*x+rhs.b);
}
bool operator == ( const Yline & rhs)
{
// cout <<"\t\tGeneric dictionary (inferior): "<<x<<endl;
return (a*x+b == rhs.a*x+rhs.b);}
};
double Yline::x;
bool intersection=false;
// Orientation test: 2x2 determinant sign
define ERR 1.0e-6
define CCW 1
define ON 0
define CW -1
int Orient2D( const point& p, const point& q, const point& r)
{
if ((q.x-p.x)*(r.y-p.y) > (r.x-p.x)*(q.y-p.y)+ERR) return CCW;
if ((q.x-p.x)*(r.y-p.y) < (r.x-p.x)*(q.y-p.y)-ERR) return CW;
return ON;
}
bool SegmentIntersect(segment s1, segment s2)
{
if (Orient2D(s1.A,s1.B,s2.A)==Orient2D(s1.A,s1.B,s2.B)) return false;
if (Orient2D(s2.A,s2.B,s1.A)==Orient2D(s2.A,s2.B,s1.B)) return false;
intersection=true;
return true;
}
// Return a random number in [0,1]
inline double randd()
{
return (double)rand()/(double)RAND_MAX;
}
// Line equation + segment number
void SegmentToYevent(segment S, int nn, Yline& eventp)
{
eventp.n=nn;
eventp.a=(S.B.y-S.A.y)/(S.B.x-S.A.x); //slope
eventp.b=S.A.y-eventp.a*S.A.x; // y=ax+b
}
int Intersect()
{
cout << "Some pair of segments intersect"<<endl;
return 1;
}
-------- Global variables for animation purposes
int n=10;
int ibelow, iabove, ime;
point P1,P2;
vector
<point> T;
vector
<point>::iterator event;
vector<Yline> Yorder;
vector<Yline>::iterator pos, below, above;
Yline yevent;
segment *seg;
double ycoord;
int inter1,inter2;
static nbstep=0;
void disp()
{
int i;
char buffer[256];
vector<Yline>::iterator yo;
glClearColor(1,1,1,0);
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0,0,1);
glPointSize(2);
// Draw the line segments
glBegin(GL_LINES);
for(i=0;i
<n;i++)
{
glVertex2f(seg[i].A.x,seg[i].A.y);
glVertex2f(seg[i].B.x,seg[i].B.y);
}
glEnd();
glColor3f(0,0,0);
for(i=0;i
<n;i++)
{
glRasterPos2f((seg[i].A.x+seg[i].B.x)/2.0,(seg[i].A.y+seg[i].B.y)/2.0);
sprintf(buffer,"%3d",i);
for(int ii=0;buffer[ii]!=0;ii++)
glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, buffer[ii]);
}
// Draw the line segment endpoints
glColor3f(0,1,0);
glPointSize(4);
glBegin(GL_POINTS);
for(i=0;i
<n;i++)
{
glVertex2f(seg[i].A.x,seg[i].A.y);
glVertex2f(seg[i].B.x,seg[i].B.y);
}
glEnd();
if ((*event).x<=1.0)
{
glColor3f(1,0,0);
//cout<<"X-coord sweep line:"<<(*event).x<
<endl;
//Draw the sweep line and the above/below line query
glBegin(GL_LINES);
glVertex2f((*event).x,0);
glVertex2f((*event).x,1);
glVertex2f(0,(*event).y);
glVertex2f(1,(*event).y);
glEnd();
// Draw the segment traces on the sweep line
glPointSize(15.0);
glColor3f(0,0,0);
glBegin(GL_POINTS);
cout<<"Order on the sweep line:";
for(yo=Yorder.begin();yo!=Yorder.end();yo++)
{
ycoord=(*yo).a*(*event).x+(*yo).b;
glVertex2f((*event).x,ycoord);
cout<<(*yo).n<<" ";
}
cout<
<endl;
glEnd();
glLineWidth(3);
glColor3f(0.3,0.3,0.3);
if (iabove>-1)
{
glBegin(GL_LINES);
glVertex2f(seg[iabove].A.x,seg[iabove].A.y);
glVertex2f(seg[iabove].B.x,seg[iabove].B.y);
glEnd();
}
if (ibelow>-1)
{
glBegin(GL_LINES);
glVertex2f(seg[ibelow].A.x,seg[ibelow].A.y);
glVertex2f(seg[ibelow].B.x,seg[ibelow].B.y);
glEnd();
}
glLineWidth(1);
if (ime>-1)
{
glColor3f(0,1,0);
glBegin(GL_LINES);
glVertex2f(seg[ime].A.x,seg[ime].A.y);
glVertex2f(seg[ime].B.x,seg[ime].B.y);
glEnd();
}
if (intersection)
{
glColor3f(1,0,0);
glRasterPos2f(0.1,0.9);
sprintf(buffer,"Detected a pair of intersecting line segments! Press 'r' for another set");
for(int ii=0;buffer[ii]!=0;ii++)
glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, buffer[ii]);
glColor3f(1,0,0);
glLineWidth(5);
glBegin(GL_LINES);
glVertex2f(seg[inter1].A.x,seg[inter1].A.y);
glVertex2f(seg[inter1].B.x,seg[inter1].B.y);
glVertex2f(seg[inter2].A.x,seg[inter2].A.y);
glVertex2f(seg[inter2].B.x,seg[inter2].B.y);
glEnd();
glLineWidth(1);
}
if (nbstep>=2*n)
{
glColor3f(1,0,0);
glRasterPos2f(0.01,0.9);
sprintf(buffer,"I have not detected any pair of intersecting segments ! Press 'r' for another set");
for(int ii=0;buffer[ii]!=0;ii++)
glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, buffer[ii]);
}
}
glFlush();
glutSwapBuffers();
}
void HandleEvent();
void InitializeSegments();
void key(unsigned char key , int x , int y)
{
if (key=='r')
{
intersection=false;
nbstep=0;
InitializeSegments();
disp();
}
else{
if (!intersection){
nbstep++;
if (nbstep<2*n)
{
event++;
cout << "Number of segments crossing the Y sweep line:" << (unsigned int)Yorder.size() << endl;
HandleEvent();
}
}
// Refresh the animation
disp();
}
}
void SeekBelowAbove()
{
iabove=ibelow=-1;
for(int i=0;i<Yorder.size();i++)
{
if (Yorder[i]
<yevent)
{
if (ibelow==-1) ibelow=i;
else if (Yorder[i]>Yorder[ibelow]) ibelow=i;
}
if (Yorder[i]>yevent)
{
if (iabove==-1) iabove=i;
else if (Yorder[i]<Yorder[iabove]) iabove=i;
}
}
if (iabove!=-1) iabove=Yorder[iabove].n;
if (ibelow!=-1) ibelow=Yorder[ibelow].n;
cout<<"Above:"<
<iabove<<" Below;"<
<ibelow<
<endl;
}
//
// Handle a combinatorial event here
//
void HandleEvent()
{
ime=(*event).n;
SegmentToYevent(seg[(*event).n],(*event).n, yevent);
// we set the sweep line here for the generic dictionary
yevent.setX((*event).x);
switch((*event).endpoint)
{
// segment (*event).n is combinatorial event
case LEFT: // Left endpoint
cout<<"Insert segment number "<
<ime<
<endl;
if (Yorder.size()>0)
{
below=Yorder.begin();
while( (below!=Yorder.end()) && ((*below)
<yevent) ) below++;
Yorder.insert(below,yevent);
}
else Yorder.insert(Yorder.begin(),yevent);
SeekBelowAbove();
if (ibelow!=-1)
if (SegmentIntersect(seg[ime],seg[ibelow]))
{cout<<"segments "<
<ime<<" and "<
<ibelow<<" intersect."<
<endl;
inter1=ime; inter2=ibelow;}
if (iabove!=-1)
if (SegmentIntersect(seg[ime],seg[iabove]))
{cout<<"segments "<
<ime<<" and "<
<iabove<<" intersect."<
<endl;
inter1=ime;inter2=iabove;}
break;
case RIGHT: // Right endpoint
cout<<"Remove segment number "<
<ime<
<endl;
below=Yorder.begin();
while((*below)
<yevent) below++;
Yorder.erase(below);
SeekBelowAbove();
if ((ibelow!=-1)&&(iabove!=-1))
if (SegmentIntersect(seg[iabove],seg[ibelow]))
{cout<<"segments "<
<iabove<<" and "<
<ibelow<<" intersect."<
<endl;
inter1=iabove;inter2=ibelow;}
break;
}
}
// Main program interface
void InitializeSegments()
{int i;
double length=0.30;
cout<<"Create randomly "<
<n<<" line segments."<
<endl;
T.clear();
for(i=0;i
<n;i++)
{
redraw:
P1.x=randd();P1.y=randd();P1.n=i;
P2.x=randd();P2.y=randd();P2.n=i;
if ((P1.x-P2.x)*(P1.x-P2.x)+(P1.y-P2.y)*(P1.y-P2.y)>length*length) goto redraw;
if (P1.x<P2.x) {P1.endpoint=LEFT; P2.endpoint=RIGHT; seg[i].A=P1; seg[i].B=P2;}
if (P1.x>P2.x) {P1.endpoint=RIGHT; P2.endpoint=LEFT; seg[i].A=P2; seg[i].B=P1;}
T.push_back(P1);
T.push_back(P2);
}
cout<<"Sort the x-coordinates of segment endpoints."<
<endl;
sort(T.begin(),T.end());
Yorder.clear();
iabove=ibelow=-1;
event=T.begin();
HandleEvent();
}
int _tmain(int argc, _TCHAR* argv[])
{
cout<<"Visual Computing: Geometry, Graphics, and Vision (ISBN:1-58450-427-7)"<
<endl;
cout<<"Demo program\n\n"<
<endl;
cout<<"Press 'r' key to draw another line segment set."<
<endl;
srand(2005);
seg=new segment[n];
InitializeSegments();
glutInit(&argc , argv);
glutInitWindowPosition(50,50);
glutInitWindowSize(W,H);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
glutCreateWindow("Line segment intersection detection algorithm (Sweep line algorithm).");
glutDisplayFunc(disp);
srand(2005);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0,1,0,1,-1,1);
glMatrixMode(GL_MODELVIEW);
glutKeyboardFunc(key);
glutMainLoop();
cout<<"Press Return key"<
<endl;
char line[100];
gets(line);
return 0;
}
(C) Æliens
20/2/2008
You may not copy or print any of this material without explicit permission of the author or the publisher.
In case of other copyright issues, contact the author.
<script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
</script>
<script type="text/javascript">
_uacct = "UA-2780434-1";
urchinTracker();
</script>