首页 > 代码库 > convex hull - Graham's scam Algorithm version-0.1
convex hull - Graham's scam Algorithm version-0.1
input:a finite set of two dimentional points P (the number of points n is more than 2)
output: the convex hull of P
Pesudo:
1, sort P in Lexgrahical order
2, construct the upper hull UHLL:
1‘ add two points on UHLL
2‘ for i=3 to n
add P[i] on UHLL
While( UHLL.Length > 2 && the last 3 points of UHLL do not make a right turn)
delete the middle point of the last 3 points of UHLL
3, construct the lower hull LHLL:
1‘ add two points on LHLL
2‘ for i=3 to n
add P[i] on LHLL
While( LHLL.Length > 2 && the last 3 points of LHLL do not make a right turn)
delete the middle point of the last 3 points of LHLL
4, delete the first and last point of LHULL
5, merge UHLL and LHULL to convex hull of P CHULL
6, output CHULL
------------------------------------------------------------------------------------------------------------
next is objectarx implementation( with visual studio 2010 + AutoCAD 2013 x64 +object 2013):
first, get the point set:
1 /********************************************************************************/ 2 /* get points in selection set */ 3 /* reference: http://www.theswamp.org/index.php?topic=30971.msg365170#msg365170 */ 4 /********************************************************************************/ 5 void CModifyEnt::getPoints(AcGePoint3dArray &vec) 6 { 7 AcDbObjectIdArray ids; 8 CResbufPtr pFilter (acutBuildList(RTDXF0,ACRX_T("POINT,LINE,LWPOLYLINE"),NULL)); 9 CSelectionSet sset;10 sset.userSelect(pFilter);11 sset.asArray(ids);12 if (ids.length() < 3)13 {14 acutPrintf(L"there must be more than 3 entities!");15 return;16 }17 for(int i = 0 ; i < ids.length(); i++)18 {19 AcDbEntityPointer pEnt(ids[i],AcDb::kForRead);20 if(pEnt.openStatus() == eOk )21 {22 if(pEnt->isA() == AcDbPoint::desc())23 {24 AcDbPoint *pPoint = AcDbPoint::cast(pEnt);25 if(pPoint)26 {27 vec.append(pPoint->position());28 }29 }30 else if(pEnt->isA() == AcDbLine::desc())31 {32 AcDbLine *pLine = AcDbLine::cast(pEnt);33 if(pLine)34 {35 vec.append(pLine->startPoint());36 vec.append(pLine->endPoint());37 }38 }39 else if(pEnt->isA() == AcDbPolyline::desc())40 {41 AcDbPolyline *pLine = AcDbPolyline::cast(pEnt);42 if(pLine)43 {44 for(unsigned int i = 0 ; i < pLine->numVerts() ; i++)45 {46 AcGePoint3d pt;47 pLine->getPointAt(i,pt);48 vec.append(pt);49 }50 }51 }52 }53 }54 }
second, sort points:
1 void CCalculation::insertion_sort(AcGePoint3dArray &points) 2 { 3 int p; 4 int k; 5 AcGePoint3d current_element; 6 7 int length = points.length(); 8 for ( p = 1; p < length; p++ ) 9 {10 current_element = points.at(p);11 k = p - 1;12 while( k >= 0 && isLessOrEqual(current_element, points.at(k)))13 {14 points.at(k+1) = points.at(k);15 k--;16 }17 points.at(k+1) = current_element;18 }19 }
in this method, there is a method called "isLessOrEqual" to compare two point in lexicographical order:
1 bool CCalculation::isLessOrEqual(AcGePoint3d &p1, AcGePoint3d &p2) 2 { 3 if (p1.x == p2.x) 4 { 5 if (p1.y == p2.y) 6 { 7 return p1.z <= p2.z; 8 } 9 else10 {11 return p1.y < p2.y;12 }13 }14 else15 {16 return p1.x < p2.x;17 }18 }
then construct the upper hull:
1 void CCalculation::constructUpper() 2 { 3 this->m_upperHull.removeAll(); 4 this->m_upperHull.append(this->m_points.at(0)); 5 this->m_upperHull.append(this->m_points.at(1)); 6 7 int k; 8 AcGeVector3d v1, v2; 9 for (int i=2; i < this->m_points.length(); i++)10 {11 this->m_upperHull.append(this->m_points.at(i));12 13 while(this->m_upperHull.length() > 2)14 {15 k = this->m_upperHull.length() - 1;//the last point of current hull‘s index16 17 v1 = this->m_upperHull.at(k) - this->m_upperHull.at(k-2);18 v2 = this->m_upperHull.at(k-1) - this->m_upperHull.at(k-2);19 20 if(CCalculation::isRightTurn(v1,v2))21 break;22 23 this->m_upperHull.removeAt(k-1);24 }25 }26 }
then construct the lower hull:
1 void CCalculation::constructLower() 2 { 3 this->m_lowerHull.removeAll(); 4 this->m_lowerHull.append(this->m_points.at(0)); 5 this->m_lowerHull.append(this->m_points.at(1)); 6 7 int k; 8 AcGeVector3d v1, v2; 9 for (int i=2; i < this->m_points.length(); i++)10 {11 this->m_lowerHull.append(this->m_points.at(i));12 13 while(this->m_lowerHull.length() > 2)14 {15 k = this->m_lowerHull.length() - 1;//the last point of current hull‘s index16 17 v1 = this->m_lowerHull.at(k) - this->m_lowerHull.at(k-2);18 v2 = this->m_lowerHull.at(k-1) - this->m_lowerHull.at(k-2);19 20 if(CCalculation::isLeftTurn(v1,v2))21 break;22 23 this->m_lowerHull.removeAt(k-1);24 }25 }26 }
in this method, there are three methods:
isRightTurn( if two vectors are in right turn order);
1 bool CCalculation::isRightTurn(AcGeVector3d &p1, AcGeVector3d &p2)2 {3 return crossProduction(p1, p2).z > 0;4 }
isLeftTurn( if two vectors are in left turn order);
1 bool CCalculation::isLeftTurn(AcGeVector3d &p1, AcGeVector3d &p2)2 {3 return crossProduction(p1, p2).z < 0;4 }
crossProduction(calculate two vectors‘ cross production):
then construct the convex hull:
1 AcGePoint3dArray CCalculation::getConvexHull() 2 { 3 this->initPoints(); 4 this->constructUpper(); 5 this->constructLower(); 6 7 this->m_convexHull.removeAll(); 8 for (int i=0; i < this->m_upperHull.length(); i++) 9 {10 this->m_convexHull.append(this->m_upperHull[i]);11 }12 13 for (int i = this->m_lowerHull.length() - 2; i >=0 ; i--)14 {15 this->m_convexHull.append(this->m_lowerHull[i]);16 }17 18 return this->m_convexHull;19 }
the CCalculation.h:
1 class CCalculation 2 { 3 public: 4 CCalculation(void); 5 ~CCalculation(void); 6 7 static bool isLessOrEqual(AcGePoint3d &p1, AcGePoint3d &p2); 8 static void insertion_sort(AcGePoint3dArray &points); 9 static AcGeVector3d crossProduction(AcGeVector3d &p1, AcGeVector3d &p2);10 static bool isRightTurn(AcGeVector3d &, AcGeVector3d &);11 static bool isLeftTurn(AcGeVector3d &, AcGeVector3d &);12 static void getPoints(AcGePoint3dArray &points);13 void initPoints();14 AcGePoint3dArray getUpperHull();15 AcGePoint3dArray getLowerHull();16 AcGePoint3dArray getConvexHull();17 private:18 AcGePoint3dArray m_points;//selected points19 AcGePoint3dArray m_upperHull;//upper hull20 AcGePoint3dArray m_lowerHull;//lower hull21 AcGePoint3dArray m_convexHull;//convex hull22 23 void constructUpper();24 void constructLower();25 void constructConveHull();26 };
the CCalculation.cpp:
1 /********************************************************************************/ 2 /* get points in selection set */ 3 /* reference: http://www.theswamp.org/index.php?topic=30971.msg365170#msg365170 */ 4 /********************************************************************************/ 5 void CCalculation::getPoints(AcGePoint3dArray &points) 6 { 7 AcDbObjectIdArray ids; 8 CResbufPtr pFilter (acutBuildList(RTDXF0,ACRX_T("POINT,LINE,LWPOLYLINE"),NULL)); 9 CSelectionSet sset; 10 sset.userSelect(pFilter); 11 sset.asArray(ids); 12 if (ids.length() < 3) 13 { 14 acutPrintf(L"there must be more than 3 entities!"); 15 return; 16 } 17 for(int i = 0 ; i < ids.length(); i++) 18 { 19 AcDbEntityPointer pEnt(ids[i],AcDb::kForRead); 20 if(pEnt.openStatus() == eOk ) 21 { 22 if(pEnt->isA() == AcDbPoint::desc()) 23 { 24 AcDbPoint *pPoint = AcDbPoint::cast(pEnt); 25 if(pPoint) 26 { 27 points.append(pPoint->position()); 28 } 29 } 30 else if(pEnt->isA() == AcDbLine::desc()) 31 { 32 AcDbLine *pLine = AcDbLine::cast(pEnt); 33 if(pLine) 34 { 35 points.append(pLine->startPoint()); 36 points.append(pLine->endPoint()); 37 } 38 } 39 else if(pEnt->isA() == AcDbPolyline::desc()) 40 { 41 AcDbPolyline *pLine = AcDbPolyline::cast(pEnt); 42 if(pLine) 43 { 44 for(unsigned int i = 0 ; i < pLine->numVerts() ; i++) 45 { 46 AcGePoint3d pt; 47 pLine->getPointAt(i,pt); 48 points.append(pt); 49 } 50 } 51 } 52 } 53 } 54 } 55 56 void CCalculation::initPoints() 57 { 58 CCalculation::getPoints(this->m_points); 59 CCalculation::insertion_sort(this->m_points); 60 } 61 62 bool CCalculation::isLessOrEqual(AcGePoint3d &p1, AcGePoint3d &p2) 63 { 64 if (p1.x == p2.x) 65 { 66 if (p1.y == p2.y) 67 { 68 return p1.z <= p2.z; 69 } 70 else 71 { 72 return p1.y < p2.y; 73 } 74 } 75 else 76 { 77 return p1.x < p2.x; 78 } 79 } 80 81 void CCalculation::insertion_sort(AcGePoint3dArray &points) 82 { 83 int p; 84 int k; 85 AcGePoint3d current_element; 86 87 int length = points.length(); 88 for ( p = 1; p < length; p++ ) 89 { 90 current_element = points.at(p); 91 k = p - 1; 92 while( k >= 0 && isLessOrEqual(current_element, points.at(k))) 93 { 94 points.at(k+1) = points.at(k); 95 k--; 96 } 97 points.at(k+1) = current_element; 98 } 99 }100 101 AcGeVector3d CCalculation::crossProduction(AcGeVector3d &p1, AcGeVector3d &p2)102 {103 return AcGeVector3d((p1.y*p2.z - p1.z*p2.y), (p1.z*p2.x - p1.x*p2.z), (p1.x*p2.y - p1.y*p2.x));104 }105 106 bool CCalculation::isRightTurn(AcGeVector3d &p1, AcGeVector3d &p2)107 {108 return crossProduction(p1, p2).z > 0;109 }110 111 bool CCalculation::isLeftTurn(AcGeVector3d &p1, AcGeVector3d &p2)112 {113 return crossProduction(p1, p2).z < 0;114 }115 116 void CCalculation::constructUpper()117 {118 this->m_upperHull.removeAll();119 this->m_upperHull.append(this->m_points.at(0));120 this->m_upperHull.append(this->m_points.at(1));121 122 int k;123 AcGeVector3d v1, v2;124 for (int i=2; i < this->m_points.length(); i++)125 {126 this->m_upperHull.append(this->m_points.at(i));127 128 while(this->m_upperHull.length() > 2)129 {130 k = this->m_upperHull.length() - 1;//the last point of current hull‘s index131 132 v1 = this->m_upperHull.at(k) - this->m_upperHull.at(k-2);133 v2 = this->m_upperHull.at(k-1) - this->m_upperHull.at(k-2);134 135 if(CCalculation::isRightTurn(v1,v2))136 break;137 138 this->m_upperHull.removeAt(k-1);139 }140 }141 }142 143 void CCalculation::constructLower()144 {145 this->m_lowerHull.removeAll();146 this->m_lowerHull.append(this->m_points.at(0));147 this->m_lowerHull.append(this->m_points.at(1));148 149 int k;150 AcGeVector3d v1, v2;151 for (int i=2; i < this->m_points.length(); i++)152 {153 this->m_lowerHull.append(this->m_points.at(i));154 155 while(this->m_lowerHull.length() > 2)156 {157 k = this->m_lowerHull.length() - 1;//the last point of current hull‘s index158 159 v1 = this->m_lowerHull.at(k) - this->m_lowerHull.at(k-2);160 v2 = this->m_lowerHull.at(k-1) - this->m_lowerHull.at(k-2);161 162 if(CCalculation::isLeftTurn(v1,v2))163 break;164 165 this->m_lowerHull.removeAt(k-1);166 }167 }168 }169 170 171 172 AcGePoint3dArray CCalculation::getUpperHull()173 {174 this->constructUpper();175 return this->m_upperHull;176 }177 178 AcGePoint3dArray CCalculation::getLowerHull()179 {180 this->constructLower();181 return this->m_lowerHull;182 }183 184 AcGePoint3dArray CCalculation::getConvexHull()185 {186 this->initPoints();187 this->constructUpper();188 this->constructLower();189 190 this->m_convexHull.removeAll();191 for (int i=0; i < this->m_upperHull.length(); i++)192 {193 this->m_convexHull.append(this->m_upperHull[i]);194 }195 196 for (int i = this->m_lowerHull.length() - 2; i >=0 ; i--)197 {198 this->m_convexHull.append(this->m_lowerHull[i]);199 }200 201 return this->m_convexHull;202 }
the getPoints method use two class(reference http://www.theswamp.org/index.php?topic=30971.msg365170#msg365170):
ResbufPtr.h:
1 #pragma once 2 #include "StdAfx.h" 3 // Chuck Gabriel @ TheSwamp 4 5 class CResbufPtr { 6 resbuf* pHead; // Maintain a pointer to the head of the resource chain for de-allocation 7 resbuf* pBuffer; 8 public: 9 CResbufPtr(resbuf* ptr) : pHead(ptr), pBuffer(ptr) {}10 ~CResbufPtr() {11 acutRelRb(pHead); // release the buffer12 pHead = pBuffer = 0;13 }14 resbuf* operator->() { return pBuffer; } // so you can do things like buf->resval.rstring15 operator resbuf*() { return pBuffer; } // so you can pass the smart pointer to functions that expect a resbuf*16 bool isNull() { return pBuffer == 0; } // null pointer check17 void start() { pBuffer = pHead; } // in case you need to return to the head of the resource chain18 void next() { pBuffer = pBuffer->rbnext; } // try to move the pointer to the next item in the resource chain19 };
SelectionSet.h
1 #pragma once 2 #include "arxHeaders.h" 3 4 class CSelectionSet 5 { 6 ads_name ssname; 7 public: 8 CSelectionSet(void); 9 ~CSelectionSet(void);10 bool isNull();11 Acad::PromptStatus userSelect(const resbuf *pFilter);12 Acad::ErrorStatus asArray(AcDbObjectIdArray &setIds);13 };
SelectionSet.cpp
1 #include "StdAfx.h" 2 #include "SelectionSet.h" 3 4 5 CSelectionSet::CSelectionSet(void) 6 { 7 ssname[0] = 0L; 8 ssname[1] = 0L; 9 }10 11 CSelectionSet::~CSelectionSet(void)12 {13 acedSSFree(ssname);14 }15 16 17 bool CSelectionSet::isNull()18 {19 return ssname[0] == 0L && ssname[1] == 0L;20 }21 22 Acad::PromptStatus CSelectionSet::userSelect(const resbuf *pFilter)23 {24 acedSSFree(ssname);25 return (Acad::PromptStatus) acedSSGet(NULL,NULL,NULL,pFilter,ssname);26 }27 28 Acad::ErrorStatus CSelectionSet::asArray( AcDbObjectIdArray &setIds )29 {30 if(this->isNull())31 return Acad::eNullPtr;32 long sslength;33 ads_name ent;34 acedSSLength(ssname,&sslength);35 for (long i = 0; i < sslength; i++)36 {37 AcDbObjectId oId;38 acedSSName(ssname, i, ent);39 if(acdbGetObjectId(oId, ent) == eOk)40 setIds.append(oId);41 else42 return Acad::eNullObjectId;43 }44 return eOk;45 }
the demo:
Done!
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
references:
1. http://www.theswamp.org/index.php?topic=30971.msg365170#msg365170
2. Computational Geometry - Algorithms and Applications(Mark de Berg · Otfried Cheong & Marc van Kreveld · Mark Overmars)
3. http://en.wikipedia.org/wiki/Graham_scan
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
convex hull - Graham's scam Algorithm version-0.1