ADN Open CIS
Сообщество программистов Autodesk в СНГ

09/12/2013

Как определить направление вращения замкнутой полилинии?

Чтобы определить направление вращения полилинии по часовой или против часовой стрелки можно пройтись по всем сегментам полилинии и проверить лежит ли он справа или слева. Следующий код делает именно это. Он подсчитывает сумму числа поворотов для всех сегментов. Повороты возможны внутри сегментов (если они дуговые) или между последовательными сегментами. Результат работы функции – число поворотов. Если поворот налево – число положительное, если поворот направо – отрицательное. Заметим, что алгоритм не определяет, является ли полилиния самопересекающейся и может даже давать ошибочные результаты если полилиния действительно самопересекающаяся.

Код - C++: [Выделить]
  1. Adesk::Boolean polyWindingNumber( AcDbPolyline*pPoly, int&winding )
  2. { 
  3.   if( !pPoly->isClosed() )
  4.   {   
  5.     return Adesk::kFalse;  
  6.   }  
  7.   double turn = 0; 
  8.   int nSegs = pPoly->numVerts(); 
  9.   AcGeVector3dArray startTans(nSegs);  
  10.   AcGeVector3dArray endTans(nSegs);  
  11.   double TWOPI = 6.28318530718; 
  12.   double PIBYTWO = 1.570796326795;  
  13.   for( int i=0;i<nSegs; i++ )
  14.   {
  15.     if( pPoly->segType(i) == AcDbPolyline::kArc )
  16.     {    
  17.       AcGeCircArc2d arc;   
  18.       pPoly->getArcSegAt(i, arc );  
  19.       AcGeVector2d startTan;  
  20.       AcGeVector2d endTan;    
  21.       AcGeVector2dArray startDerivs;  
  22.       arc.evalPoint( arc.startAng(), 1, startDerivs );
  23.       startTan = startDerivs[0];     
  24.       AcGeVector2dArray endDerivs;     
  25.       arc.evalPoint( arc.endAng(), 1, endDerivs);   
  26.       endTan = endDerivs[0];   
  27.       startTans.append(AcGeVector3d(startTan.x, startTan.y,0.0));
  28.       endTans.append(AcGeVector3d(endTan.x, endTan.y, 0.0));        
  29.       double ang = arc.endAng() - arc.startAng();     
  30.       turn += arc.isClockWise() ? -ang : ang;     
  31.     }    
  32.     else if(pPoly->segType(i) == AcDbPolyline::kLine )
  33.     {
  34.       AcGeLineSeg2d line;
  35.       pPoly->getLineSegAt(i, line );
  36.       AcGeVector2d tan2d = line.endPoint() -line.startPoint();  
  37.       AcGeVector3d tan = AcGeVector3d( tan2d.x, tan2d.y, 0.0);  
  38.       startTans.append(tan);  
  39.       endTans.append(tan);
  40.     }
  41.   }
  42.   nSegs = startTans.length();  
  43.  
  44.   for(int i=0;i<nSegs; i++ )
  45.   {
  46.     double angle = endTans[i].angleTo( startTans[(i+1)%nSegs] );   
  47.     AcGeVector3d norm =  endTans[i].crossProduct(
  48.       startTans[(i+1)%nSegs] ); 
  49.     angle = norm.z > 0 ? angle: -angle;
  50.     turn += angle; 
  51.   }
  52.   turn = turn / TWOPI;
  53.   double lower = floor( turn ); 
  54.   double tol = 1e-6; 
  55.   if( (turn - lower ) < tol)     
  56.     winding = (int)lower;  
  57.   else if( (( lower + 1 ) - turn ) <  tol )
  58.     winding = (int)(lower + 1); 
  59.   else
  60.     return Adesk::kFalse;
  61.  
  62.   return Adesk::kTrue;
  63. }
  64.  
  65. static void AsdkUtilsPolyWind(void)
  66. {
  67.   ads_name eName;
  68.   ads_point pt;
  69.   if( RTNORM !=
  70.     acedEntSel(L"\nВыберите полилинию для проверки: ", eName, pt) )
  71.     return;
  72.   AcDbObjectId id; 
  73.   acdbGetObjectId( id, eName ); 
  74.   AcDbPolyline*pPoly;  
  75.   acdbOpenObject(pPoly, id, AcDb::kForRead );
  76.   if(pPoly == NULL )     
  77.     return;
  78.   int winding =0;
  79.   if( polyWindingNumber(pPoly, winding ) )
  80.   {  
  81.     acutPrintf(L"\nЧисло поворотов полигона %d", winding ); 
  82.   } 
  83.   else
  84.   { 
  85.     acutPrintf(L"\nНевозможно вычислить число поворотов полилинии ");
  86.   }
  87.   pPoly->close();
  88. }

Источник: http://adndevblog.typepad.com/autocad/2013/01/how-to-test-the-winding-turn-of-a-closed-polyline.html

Обсуждение: http://adn-cis.org/forum/index.php?topic=387

Опубликовано 09.12.2013