You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

273 lines
9.5 KiB

  1. /********************************************************************************
  2. * Copyright (C) 2004 Sjaak Priester
  3. *
  4. * This is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This file is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with Tinter; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  17. ********************************************************************************/
  18. // SutherlandHodgman
  19. // Class to perform polygon clipping against an upright rectangular boundary window.
  20. // Implementation of Sutherland-Hodgman algorithm (1974).
  21. //
  22. // Version 1.0 (C) 2004, Sjaak Priester, Amsterdam.
  23. // mailto:sjaak@sjaakpriester.nl
  24. // http://www.sjaakpriester.nl
  25. #ifndef __SUTHERLAND_HODGMAN_H__
  26. #define __SUTHERLAND_HODGMAN_H__
  27. #include <vector>
  28. #include <functional>
  29. #ifndef _GDIPLUS_H
  30. // I designed this with GDI+ in mind. However, this particular code doesn't
  31. // use GDI+ at all, only some of it's variable types.
  32. // These definitions are substitutes for those of GDI+.
  33. typedef double REAL;
  34. class PointF
  35. {
  36. public:
  37. REAL X;
  38. REAL Y;
  39. PointF() : X( 0 )
  40. , Y( 0 ) { }
  41. PointF( const PointF& p ) : X( p.X )
  42. , Y( p.Y ) { }
  43. PointF( REAL x, REAL y ) : X( x )
  44. , Y( y ) { }
  45. PointF operator+( const PointF& p ) const { return PointF( X + p.X, Y + p.Y ); }
  46. PointF operator-( const PointF& p ) const { return PointF( X - p.X, Y - p.Y ); }
  47. bool Equals( const PointF& p ) { return (X == p.X) && (Y == p.Y); }
  48. };
  49. class RectF
  50. {
  51. public:
  52. REAL X;
  53. REAL Y;
  54. REAL Width;
  55. REAL Height;
  56. RectF() { X = 0, Y = 0, Height = 0, Width = 0; }
  57. RectF( const RectF& r )
  58. {
  59. X = r.X; Y = r.Y; Height = r.Height, Width = r.Width;
  60. }
  61. RectF( REAL x, REAL y, REAL w, REAL h ) : X( x ), Y( y ),Width( w ), Height( h )
  62. { }
  63. REAL GetLeft() const { return X; }
  64. REAL GetTop() const { return Y; }
  65. REAL GetRight() const { return X + Width; }
  66. REAL GetBottom() const { return Y + Height; }
  67. };
  68. #endif // _GDIPLUS_H
  69. typedef std::vector<PointF> pointVector;
  70. typedef std::vector<PointF>::iterator pointIterator;
  71. typedef std::vector<PointF>::const_iterator cpointIterator;
  72. class SutherlandHodgman
  73. {
  74. public:
  75. // Constructor. Parameter is the boundary rectangle.
  76. // SutherlandHodgman expects a 'normalized' boundary rectangle, meaning
  77. // that boundaries.GetRight() > boundaries.GetLeft() and
  78. // boundaries.GetBottom() > boundaries.GetTop().
  79. // In other words: boundary.Width > 0 and boundaries.Height > 0.
  80. // If this is violated, nothing will be output.
  81. SutherlandHodgman( RectF& boundaries ) :
  82. m_stageBottom( m_stageOut, boundaries.GetBottom() )
  83. , /* Initialize each stage */ m_stageLeft( m_stageBottom, boundaries.GetLeft() )
  84. , /* with its next stage and */ m_stageTop( m_stageLeft, boundaries.GetTop() )
  85. , /* the boundary position. */ m_stageRight( m_stageTop, boundaries.GetRight() )
  86. {
  87. }
  88. void Clip( pointVector& input, pointVector& clipped )
  89. {
  90. clipped.clear();
  91. m_stageOut.SetDestination( &clipped );
  92. // Clip each input vertex.
  93. for( cpointIterator it = input.begin(); it != input.end(); ++it )
  94. m_stageRight.HandleVertex( *it );
  95. // Do the final step.
  96. m_stageRight.Finalize();
  97. }
  98. private:
  99. // Implementation of a horizontal boundary (top or bottom).
  100. // Comp is a std::binary_function object, comparing its two parameters, f.i. std::less.
  101. template <class Comp>
  102. class BoundaryHor
  103. {
  104. public:
  105. BoundaryHor( REAL y ) : m_Y( y ) { }
  106. bool IsInside( const PointF& pnt ) const
  107. {
  108. return Comp ()( pnt.Y, m_Y );
  109. } // return true if pnt.Y is at the inside of the boundary
  110. PointF Intersect( const PointF& p0, const PointF& p1 ) const // return intersection point of line p0...p1 with boundary
  111. { // assumes p0...p1 is not strictly horizontal
  112. PointF d = p1 - p0;
  113. REAL xslope = d.X / d.Y;
  114. PointF r;
  115. r.Y = m_Y;
  116. r.X = p0.X + xslope * (m_Y - p0.Y);
  117. return r;
  118. }
  119. private:
  120. REAL m_Y;
  121. };
  122. // Implementation of a vertical boundary (left or right).
  123. template <class Comp>
  124. class BoundaryVert
  125. {
  126. public:
  127. BoundaryVert( REAL x ) : m_X( x )
  128. { }
  129. bool IsInside( const PointF& pnt ) const
  130. {
  131. return Comp() ( pnt.X, m_X );
  132. }
  133. PointF Intersect( const PointF& p0, const PointF& p1 ) const // assumes p0...p1 is not strictly vertical
  134. {
  135. PointF d = p1 - p0;
  136. REAL yslope = d.Y / d.X;
  137. PointF r;
  138. r.X = m_X;
  139. r.Y = p0.Y + yslope * (m_X - p0.X);
  140. return r;
  141. }
  142. private:
  143. REAL m_X;
  144. };
  145. // This template class is the workhorse of the algorithm. It handles the clipping against one boundary.
  146. // Boundary is either BoundaryHor or BoundaryVert, Stage is the next ClipStage, or the output stage.
  147. template <class Boundary, class Stage>
  148. class ClipStage : private Boundary
  149. {
  150. public:
  151. ClipStage( Stage& nextStage, REAL position ) :
  152. Boundary( position ) , m_NextStage( nextStage ), m_bFirst( true ), m_bPreviousInside( false )
  153. { }
  154. // Function to handle one vertex
  155. void HandleVertex( const PointF& pntCurrent )
  156. {
  157. bool bCurrentInside = this->IsInside( pntCurrent ); // See if vertex is inside the boundary.
  158. if( m_bFirst ) // If this is the first vertex...
  159. {
  160. m_pntFirst = pntCurrent; // ... just remember it,...
  161. m_bFirst = false;
  162. }
  163. else // Common cases, not the first vertex.
  164. {
  165. if( bCurrentInside ) // If this vertex is inside...
  166. {
  167. if( !m_bPreviousInside ) // ... and the previous one was outside
  168. m_NextStage.HandleVertex( this->Intersect( m_pntPrevious, pntCurrent ) );
  169. // ... first output the intersection point.
  170. m_NextStage.HandleVertex( pntCurrent ); // Output the current vertex.
  171. }
  172. else if( m_bPreviousInside ) // If this vertex is outside, and the previous one was inside...
  173. m_NextStage.HandleVertex( this->Intersect( m_pntPrevious, pntCurrent ) );
  174. // ... output the intersection point.
  175. // If neither current vertex nor the previous one are inside, output nothing.
  176. }
  177. m_pntPrevious = pntCurrent; // Be prepared for next vertex.
  178. m_bPreviousInside = bCurrentInside;
  179. }
  180. void Finalize()
  181. {
  182. HandleVertex( m_pntFirst ); // Close the polygon.
  183. m_NextStage.Finalize(); // Delegate to the next stage.
  184. }
  185. private:
  186. Stage& m_NextStage; // the next stage
  187. bool m_bFirst; // true if no vertices have been handled
  188. PointF m_pntFirst; // the first vertex
  189. PointF m_pntPrevious; // the previous vertex
  190. bool m_bPreviousInside; // true if the previous vertex was inside the Boundary
  191. };
  192. class OutputStage
  193. {
  194. public:
  195. OutputStage() : m_pDest( 0 ) { }
  196. void SetDestination( pointVector* pDest ) { m_pDest = pDest; }
  197. void HandleVertex( const PointF& pnt ) { m_pDest->push_back( pnt ); } // Append the vertex to the output container.
  198. void Finalize() { } // Do nothing.
  199. private:
  200. pointVector* m_pDest;
  201. };
  202. // These typedefs define the four boundaries. In keeping up with the GDI/GDI+ interpretation of
  203. // rectangles, we include the left and top boundaries, but not the right and bottom boundaries.
  204. // In other words: a vertex on the left boundary is considered to be inside, but a vertex
  205. // on the right boundary is considered to be outside.
  206. typedef BoundaryVert<std::less<REAL> > BoundaryRight;
  207. typedef BoundaryHor<std::greater_equal<REAL> > BoundaryTop;
  208. typedef BoundaryVert<std::greater_equal<REAL> > BoundaryLeft;
  209. typedef BoundaryHor<std::less<REAL> > BoundaryBottom;
  210. // Next typedefs define the four stages. First template parameter is the boundary,
  211. // second template parameter is the next stage.
  212. typedef ClipStage<BoundaryBottom, OutputStage> ClipBottom;
  213. typedef ClipStage<BoundaryLeft, ClipBottom> ClipLeft;
  214. typedef ClipStage<BoundaryTop, ClipLeft> ClipTop;
  215. typedef ClipStage<BoundaryRight, ClipTop> ClipRight;
  216. // Our data members.
  217. OutputStage m_stageOut;
  218. ClipBottom m_stageBottom;
  219. ClipLeft m_stageLeft;
  220. ClipTop m_stageTop;
  221. ClipRight m_stageRight;
  222. };
  223. #endif