1 /***************************************************************************
2                           qgsclipper.h  -  a class that clips line
3                           segments and polygons
4                              -------------------
5     begin                : March 2004
6     copyright            : (C) 2005 by Gavin Macaulay
7     email                :
8  ***************************************************************************/
9 
10 /***************************************************************************
11  *                                                                         *
12  *   This program is free software; you can redistribute it and/or modify  *
13  *   it under the terms of the GNU General Public License as published by  *
14  *   the Free Software Foundation; either version 2 of the License, or     *
15  *   (at your option) any later version.                                   *
16  *                                                                         *
17  ***************************************************************************/
18 
19 #ifndef QGSCLIPPER_H
20 #define QGSCLIPPER_H
21 
22 #include "qgis_core.h"
23 #include "qgis_sip.h"
24 #include "qgspointxy.h"
25 #include "qgsrectangle.h"
26 
27 #include <QVector>
28 #include <QPolygonF>
29 
30 class QgsCurve;
31 
SIP_FEATURE(ARM)32 SIP_FEATURE( ARM ) // Some parts are not available in sip bindings on ARM because of qreal double vs. float issues
33 
34 
35 /**
36  * \ingroup core
37  * \brief A class to trim lines and polygons to within a rectangular region.
38  *
39  * The functions in this class are likely to be called from within a
40  * render loop and hence need to as CPU efficient as possible.
41  * The main purpose of the functions in this class are to trim lines
42  *  and polygons to lie within a rectangular region. This is necessary
43  *  for drawing items to an X11 display which have a limit on the
44  *   magnitude of the screen coordinates (+/- 32768, i.e. 16 bit integer).
45  */
46 class CORE_EXPORT QgsClipper
47 {
48   public:
49 
50     // These are the limits for X11 screen coordinates. The actual
51     // values are +/-32767, but we allow a little bit of space for
52     // rounding errors.
53 
54     // You may wonder why the clipping is done to these coordinates
55     // rather than the boundaries of the qgis canvas. Reasons include:
56     // - making the boundaries static const allows the compiler to
57     //   optimise the code that uses these values more than if they changed
58     //   for every call to the trim code.
59     // - clipping takes quite a bit of CPU effort, and the less that this is
60     //   done the better. More stuff would have to be clipped if the
61     //   boundaries were the qgis canvas (but this may be offset by
62     //   having less to draw).
63     //
64     // The limit is set to 30,000 instead of 32768 because that things
65     // still go wrong.
66 
67     //! Maximum X-coordinate of the rectangular box used for clipping.
68     static const double MAX_X;
69     //! Minimum X-coordinate of the rectangular box used for clipping.
70     static const double MIN_X;
71     //! Maximum Y-coordinate of the rectangular box used for clipping.
72     static const double MAX_Y;
73     //! Minimum Y-coordinate of the rectangular box used for clipping.
74     static const double MIN_Y;
75 
76 
77     //! A handy way to refer to the four boundaries
78     enum Boundary
79     {
80       XMax,
81       XMin,
82       YMax,
83       YMin
84     };
85 
86     SIP_IF_FEATURE( !ARM ) // Not available on ARM sip bindings because of qreal issues
87 
88     /**
89      * Trims the given feature to a rectangular box. Returns the trimmed
90      * feature in x and y. The shapeOpen parameter determines whether
91      * the function treats the points as a closed shape (polygon), or as
92      * an open shape (linestring).
93      *
94      * \note not available in Python bindings on android
95      */
96     static void trimFeature( QVector<double> &x,
97                              QVector<double> &y,
98                              bool shapeOpen );
99 
100     SIP_END
101 
102     static void trimPolygon( QPolygonF &pts, const QgsRectangle &clipRect );
103 
104     /**
105      * Takes a linestring and clips it to clipExtent
106      * \param curve the linestring
107      * \param clipExtent clipping bounds
108      * \returns clipped line coordinates
109      */
110     static QPolygonF clippedLine( const QgsCurve &curve, const QgsRectangle &clipExtent );
111 
112     /**
113      * Takes a \a curve and clips it to clipExtent.
114      *
115      * \since QGIS 3.16
116      */
117     static QPolygonF clippedLine( const QPolygonF &curve, const QgsRectangle &clipExtent );
118 
119   private:
120 
121     // Used when testing for equivalence to 0.0
122     static const double SMALL_NUM;
123 
124     // Trims the given feature to the given boundary. Returns the
125     // trimmed feature in the outX and outY vectors.
126     static inline void trimFeatureToBoundary( const QVector<double> &inX,
127         const QVector<double> &inY,
128         QVector<double> &outX,
129         QVector<double> &outY,
130         Boundary b,
131         bool shapeOpen );
132 
133     static inline void trimPolygonToBoundary( const QPolygonF &inPts, QPolygonF &outPts, const QgsRectangle &rect, Boundary b, double boundaryValue );
134 
135     // Determines if a point is inside or outside the given boundary
136     static inline bool inside( double x, double y, Boundary b );
137 
138     static inline bool inside( QPointF pt, Boundary b, double val );
139 
140     // Calculates the intersection point between a line defined by a
141     // (x1, y1), and (x2, y2) and the given boundary
142     static inline QgsPointXY intersect( double x1, double y1,
143                                         double x2, double y2,
144                                         Boundary b );
145 
146     static inline QPointF intersectRect( QPointF pt1,
147                                          QPointF pt2,
148                                          Boundary b, const QgsRectangle &rect );
149 
150     //Implementation of 'Fast clipping' algorithm (Sobkow et al. 1987, Computers & Graphics Vol.11, 4, p.459-467)
151     static bool clipLineSegment( double xLeft, double xRight, double yBottom, double yTop, double &x0, double &y0, double &x1, double &y1 );
152 
153     /**
154      * Connects two lines split by the clip (by inserting points on the clip border)
155      * \param x0 x-coordinate of the first line end
156      * \param y0 y-coordinate of the first line end
157      * \param x1 x-coordinate of the second line start
158      * \param y1 y-coordinate of the second line start
159      * \param clipRect clip rectangle
160      * \param pts: in/out array of clipped points
161       */
162     static void connectSeparatedLines( double x0, double y0, double x1, double y1,
163                                        const QgsRectangle &clipRect, QPolygonF &pts );
164 
165     //low level clip methods for fast clip algorithm
166     static void clipStartTop( double &x0, double &y0, double x1, double y1, double yMax );
167     static void clipStartBottom( double &x0, double &y0, double x1, double y1, double yMin );
168     static void clipStartRight( double &x0, double &y0, double x1, double y1, double xMax );
169     static void clipStartLeft( double &x0, double &y0, double &x1, double &y1, double xMin );
170     static void clipEndTop( double x0, double y0, double &x1, double &y1, double yMax );
171     static void clipEndBottom( double x0, double y0, double &x1, double &y1, double yMin );
172     static void clipEndRight( double x0, double y0, double &x1, double &y1, double xMax );
173     static void clipEndLeft( double x0, double y0, double &x1, double &y1, double xMin );
174 };
175 
176 #ifndef SIP_RUN
177 // The inline functions
178 
179 // Trim the feature using Sutherland and Hodgman's
180 // polygon-clipping algorithm. See J. D. Foley, A. van Dam,
181 // S. K. Feiner, and J. F. Hughes, Computer Graphics, Principles and
182 // Practice. Addison-Wesley Systems Programming Series,
183 // Addison-Wesley, 2nd ed., 1991.
184 
185 // I understand that this is not the most efficient algorithm, but is
186 // one (the only?) that is guaranteed to always give the correct
187 // result.
188 
trimFeature(QVector<double> & x,QVector<double> & y,bool shapeOpen)189 inline void QgsClipper::trimFeature( QVector<double> &x,
190                                      QVector<double> &y,
191                                      bool shapeOpen )
192 {
193   QVector<double> tmpX;
194   QVector<double> tmpY;
195   trimFeatureToBoundary( x, y, tmpX, tmpY, XMax, shapeOpen );
196 
197   x.clear();
198   y.clear();
199   trimFeatureToBoundary( tmpX, tmpY, x, y, YMax, shapeOpen );
200 
201   tmpX.clear();
202   tmpY.clear();
203   trimFeatureToBoundary( x, y, tmpX, tmpY, XMin, shapeOpen );
204 
205   x.clear();
206   y.clear();
207   trimFeatureToBoundary( tmpX, tmpY, x, y, YMin, shapeOpen );
208 }
209 
trimPolygon(QPolygonF & pts,const QgsRectangle & clipRect)210 inline void QgsClipper::trimPolygon( QPolygonF &pts, const QgsRectangle &clipRect )
211 {
212   QPolygonF tmpPts;
213   tmpPts.reserve( pts.size() );
214 
215   trimPolygonToBoundary( pts, tmpPts, clipRect, XMax, clipRect.xMaximum() );
216   pts.resize( 0 );
217   trimPolygonToBoundary( tmpPts, pts, clipRect, YMax, clipRect.yMaximum() );
218   tmpPts.resize( 0 );
219   trimPolygonToBoundary( pts, tmpPts, clipRect, XMin, clipRect.xMinimum() );
220   pts.resize( 0 );
221   trimPolygonToBoundary( tmpPts, pts, clipRect, YMin, clipRect.yMinimum() );
222 }
223 
224 // An auxiliary function that is part of the polygon trimming
225 // code. Will trim the given polygon to the given boundary and return
226 // the trimmed polygon in the out pointer. Uses Sutherland and
227 // Hodgman's polygon-clipping algorithm.
228 
trimFeatureToBoundary(const QVector<double> & inX,const QVector<double> & inY,QVector<double> & outX,QVector<double> & outY,Boundary b,bool shapeOpen)229 inline void QgsClipper::trimFeatureToBoundary(
230   const QVector<double> &inX,
231   const QVector<double> &inY,
232   QVector<double> &outX,
233   QVector<double> &outY,
234   Boundary b, bool shapeOpen )
235 {
236   // The shapeOpen parameter selects whether this function treats the
237   // shape as open or closed. False is appropriate for polygons and
238   // true for polylines.
239 
240   int i1 = inX.size() - 1; // start with last point
241 
242   // and compare to the first point initially.
243   for ( int i2 = 0; i2 < inX.size() ; ++i2 )
244   {
245     // look at each edge of the polygon in turn
246 
247     //ignore segments with nan or inf coordinates
248     if ( std::isnan( inX[i2] ) || std::isnan( inY[i2] ) || std::isinf( inX[i2] ) || std::isinf( inY[i2] )
249          || std::isnan( inX[i1] ) || std::isnan( inY[i1] ) || std::isinf( inX[i1] ) || std::isinf( inY[i1] ) )
250     {
251       i1 = i2;
252       continue;
253     }
254 
255 
256     if ( inside( inX[i2], inY[i2], b ) ) // end point of edge is inside boundary
257     {
258       if ( inside( inX[i1], inY[i1], b ) )
259       {
260         outX.push_back( inX[i2] );
261         outY.push_back( inY[i2] );
262       }
263       else
264       {
265         // edge crosses into the boundary, so trim back to the boundary, and
266         // store both ends of the new edge
267         if ( !( i2 == 0 && shapeOpen ) )
268         {
269           const QgsPointXY p = intersect( inX[i1], inY[i1], inX[i2], inY[i2], b );
270           outX.push_back( p.x() );
271           outY.push_back( p.y() );
272         }
273 
274         outX.push_back( inX[i2] );
275         outY.push_back( inY[i2] );
276       }
277     }
278     else // end point of edge is outside boundary
279     {
280       // start point is in boundary, so need to trim back
281       if ( inside( inX[i1], inY[i1], b ) )
282       {
283         if ( !( i2 == 0 && shapeOpen ) )
284         {
285           const QgsPointXY p = intersect( inX[i1], inY[i1], inX[i2], inY[i2], b );
286           outX.push_back( p.x() );
287           outY.push_back( p.y() );
288         }
289       }
290     }
291     i1 = i2;
292   }
293 }
294 
trimPolygonToBoundary(const QPolygonF & inPts,QPolygonF & outPts,const QgsRectangle & rect,Boundary b,double boundaryValue)295 inline void QgsClipper::trimPolygonToBoundary( const QPolygonF &inPts, QPolygonF &outPts, const QgsRectangle &rect, Boundary b, double boundaryValue )
296 {
297   int i1 = inPts.size() - 1; // start with last point
298 
299   // and compare to the first point initially.
300   for ( int i2 = 0; i2 < inPts.size() ; ++i2 )
301   {
302     // look at each edge of the polygon in turn
303     if ( inside( inPts[i2], b, boundaryValue ) ) // end point of edge is inside boundary
304     {
305       if ( inside( inPts[i1], b, boundaryValue ) )
306       {
307         outPts.append( inPts[i2] );
308       }
309       else
310       {
311         // edge crosses into the boundary, so trim back to the boundary, and
312         // store both ends of the new edge
313         outPts.append( intersectRect( inPts[i1], inPts[i2], b, rect ) );
314         outPts.append( inPts[i2] );
315       }
316     }
317     else // end point of edge is outside boundary
318     {
319       // start point is in boundary, so need to trim back
320       if ( inside( inPts[i1], b, boundaryValue ) )
321       {
322         outPts.append( intersectRect( inPts[i1], inPts[i2], b, rect ) );
323       }
324     }
325     i1 = i2;
326   }
327 }
328 
329 // An auxiliary function to trimPolygonToBoundarY() that returns
330 // whether a point is inside or outside the given boundary.
331 
inside(const double x,const double y,Boundary b)332 inline bool QgsClipper::inside( const double x, const double y, Boundary b )
333 {
334   switch ( b )
335   {
336     case XMax: // x < MAX_X is inside
337       if ( x < MAX_X )
338         return true;
339       break;
340     case XMin: // x > MIN_X is inside
341       if ( x > MIN_X )
342         return true;
343       break;
344     case YMax: // y < MAX_Y is inside
345       if ( y < MAX_Y )
346         return true;
347       break;
348     case YMin: // y > MIN_Y is inside
349       if ( y > MIN_Y )
350         return true;
351       break;
352   }
353   return false;
354 }
355 
inside(QPointF pt,Boundary b,double val)356 inline bool QgsClipper::inside( QPointF pt, Boundary b, double val )
357 {
358   switch ( b )
359   {
360     case XMax: // x < MAX_X is inside
361       return ( pt.x() < val );
362     case XMin: // x > MIN_X is inside
363       return ( pt.x() > val );
364     case YMax: // y < MAX_Y is inside
365       return ( pt.y() < val );
366     case YMin: // y > MIN_Y is inside
367       return ( pt.y() > val );
368   }
369   return false;
370 }
371 
372 
373 // An auxiliary function to trimPolygonToBoundarY() that calculates and
374 // returns the intersection of the line defined by the given points
375 // and the given boundary.
376 
intersect(const double x1,const double y1,const double x2,const double y2,Boundary b)377 inline QgsPointXY QgsClipper::intersect( const double x1, const double y1,
378     const double x2, const double y2,
379     Boundary b )
380 {
381   // This function assumes that the two given points (x1, y1), and
382   // (x2, y2) cross the given boundary. Making this assumption allows
383   // some optimisations.
384 
385   double r_n = SMALL_NUM, r_d = SMALL_NUM;
386 
387   switch ( b )
388   {
389     case XMax: // x = MAX_X boundary
390       r_n = -( x1 - MAX_X ) * ( MAX_Y - MIN_Y );
391       r_d = ( x2 - x1 )   * ( MAX_Y - MIN_Y );
392       break;
393     case XMin: // x = MIN_X boundary
394       r_n = -( x1 - MIN_X ) * ( MAX_Y - MIN_Y );
395       r_d = ( x2 - x1 )   * ( MAX_Y - MIN_Y );
396       break;
397     case YMax: // y = MAX_Y boundary
398       r_n = ( y1 - MAX_Y ) * ( MAX_X - MIN_X );
399       r_d = -( y2 - y1 )   * ( MAX_X - MIN_X );
400       break;
401     case YMin: // y = MIN_Y boundary
402       r_n = ( y1 - MIN_Y ) * ( MAX_X - MIN_X );
403       r_d = -( y2 - y1 )   * ( MAX_X - MIN_X );
404       break;
405   }
406 
407   QgsPointXY p;
408 
409   if ( std::fabs( r_d ) > SMALL_NUM && std::fabs( r_n ) > SMALL_NUM )
410   {
411     // they cross
412     const double r = r_n / r_d;
413     p.set( x1 + r * ( x2 - x1 ), y1 + r * ( y2 - y1 ) );
414   }
415   else
416   {
417     // Should never get here, but if we do for some reason, cause a
418     // clunk because something else is wrong if we do.
419     Q_ASSERT( std::fabs( r_d ) > SMALL_NUM && std::fabs( r_n ) > SMALL_NUM );
420   }
421 
422   return p;
423 }
424 
intersectRect(QPointF pt1,QPointF pt2,Boundary b,const QgsRectangle & rect)425 inline QPointF QgsClipper::intersectRect( QPointF pt1,
426     QPointF pt2,
427     Boundary b, const QgsRectangle &rect )
428 {
429   // This function assumes that the two given points (x1, y1), and
430   // (x2, y2) cross the given boundary. Making this assumption allows
431   // some optimisations.
432 
433   double r_n = SMALL_NUM, r_d = SMALL_NUM;
434   const double x1 = pt1.x(), x2 = pt2.x();
435   const double y1 = pt1.y(), y2 = pt2.y();
436 
437   switch ( b )
438   {
439     case XMax: // x = MAX_X boundary
440       r_n = -( x1 - rect.xMaximum() ) * ( rect.yMaximum() - rect.yMinimum() );
441       r_d = ( x2 - x1 )   * ( rect.yMaximum() - rect.yMinimum() );
442       break;
443     case XMin: // x = MIN_X boundary
444       r_n = -( x1 - rect.xMinimum() ) * ( rect.yMaximum() - rect.yMinimum() );
445       r_d = ( x2 - x1 )   * ( rect.yMaximum() - rect.yMinimum() );
446       break;
447     case YMax: // y = MAX_Y boundary
448       r_n = ( y1 - rect.yMaximum() ) * ( rect.xMaximum() - rect.xMinimum() );
449       r_d = -( y2 - y1 )   * ( rect.xMaximum() - rect.xMinimum() );
450       break;
451     case YMin: // y = MIN_Y boundary
452       r_n = ( y1 - rect.yMinimum() ) * ( rect.xMaximum() - rect.xMinimum() );
453       r_d = -( y2 - y1 )   * ( rect.xMaximum() - rect.xMinimum() );
454       break;
455   }
456 
457   double r = 0;
458   if ( !qgsDoubleNear( r_d, 0.0 ) )
459   {
460     r = r_n / r_d;
461   }
462   return QPointF( x1 + r * ( x2 - x1 ), y1 + r * ( y2 - y1 ) );
463 }
464 
clipStartTop(double & x0,double & y0,double x1,double y1,double yMax)465 inline void QgsClipper::clipStartTop( double &x0, double &y0, double x1, double y1, double yMax )
466 {
467   x0 += ( x1 - x0 )  * ( yMax - y0 ) / ( y1 - y0 );
468   y0 = yMax;
469 }
470 
clipStartBottom(double & x0,double & y0,double x1,double y1,double yMin)471 inline void QgsClipper::clipStartBottom( double &x0, double &y0, double x1, double y1, double yMin )
472 {
473   x0 += ( x1 - x0 ) * ( yMin - y0 ) / ( y1 - y0 );
474   y0 = yMin;
475 }
476 
clipStartRight(double & x0,double & y0,double x1,double y1,double xMax)477 inline void QgsClipper::clipStartRight( double &x0, double &y0, double x1, double y1, double xMax )
478 {
479   y0 += ( y1 - y0 ) * ( xMax - x0 ) / ( x1 - x0 );
480   x0 = xMax;
481 }
482 
clipStartLeft(double & x0,double & y0,double & x1,double & y1,double xMin)483 inline void QgsClipper::clipStartLeft( double &x0, double &y0, double &x1, double &y1, double xMin )
484 {
485   y0 += ( y1 - y0 ) * ( xMin - x0 ) / ( x1 - x0 );
486   x0 = xMin;
487 }
488 
clipEndTop(double x0,double y0,double & x1,double & y1,double yMax)489 inline void QgsClipper::clipEndTop( double x0, double y0, double &x1, double &y1, double yMax )
490 {
491   x1 += ( x1 - x0 ) * ( yMax - y1 ) / ( y1 - y0 );
492   y1 = yMax;
493 }
494 
clipEndBottom(double x0,double y0,double & x1,double & y1,double yMin)495 inline void QgsClipper::clipEndBottom( double x0, double y0, double &x1, double &y1, double yMin )
496 {
497   x1 += ( x1 - x0 ) * ( yMin - y1 ) / ( y1 - y0 );
498   y1 = yMin;
499 }
500 
clipEndRight(double x0,double y0,double & x1,double & y1,double xMax)501 inline void QgsClipper::clipEndRight( double x0, double y0, double &x1, double &y1, double xMax )
502 {
503   y1 += ( y1 - y0 ) * ( xMax - x1 ) / ( x1 - x0 );
504   x1 = xMax;
505 }
506 
clipEndLeft(double x0,double y0,double & x1,double & y1,double xMin)507 inline void QgsClipper::clipEndLeft( double x0, double y0, double &x1, double &y1, double xMin )
508 {
509   y1 += ( y1 - y0 ) * ( xMin - x1 ) / ( x1 - x0 );
510   x1 = xMin;
511 }
512 
513 //'Fast clipping' algorithm (Sobkow et al. 1987, Computers & Graphics Vol.11, 4, p.459-467)
clipLineSegment(double xLeft,double xRight,double yBottom,double yTop,double & x0,double & y0,double & x1,double & y1)514 inline bool QgsClipper::clipLineSegment( double xLeft, double xRight, double yBottom, double yTop, double &x0, double &y0, double &x1, double &y1 )
515 {
516   int lineCode = 0;
517 
518   if ( y1 < yBottom )
519     lineCode |= 4;
520   else if ( y1 > yTop )
521     lineCode |= 8;
522 
523   if ( x1 > xRight )
524     lineCode |= 2;
525   else if ( x1 < xLeft )
526     lineCode |= 1;
527 
528   if ( y0 < yBottom )
529     lineCode |= 64;
530   else if ( y0 > yTop )
531     lineCode |= 128;
532 
533   if ( x0 > xRight )
534     lineCode |= 32;
535   else if ( x0 < xLeft )
536     lineCode |= 16;
537 
538   switch ( lineCode )
539   {
540     case 0: //completely inside
541       return true;
542 
543     case 1:
544       clipEndLeft( x0, y0, x1, y1, xLeft );
545       return true;
546 
547     case 2:
548       clipEndRight( x0, y0, x1, y1, xRight );
549       return true;
550 
551     case 4:
552       clipEndBottom( x0, y0, x1, y1, yBottom );
553       return true;
554 
555     case 5:
556       clipEndLeft( x0, y0, x1, y1, xLeft );
557       if ( y1 < yBottom )
558         clipEndBottom( x0, y0, x1, y1, yBottom );
559       return true;
560 
561     case 6:
562       clipEndRight( x0, y0, x1, y1, xRight );
563       if ( y1 < yBottom )
564         clipEndBottom( x0, y0, x1, y1, yBottom );
565       return true;
566 
567     case 8:
568       clipEndTop( x0, y0, x1, y1, yTop );
569       return true;
570 
571     case 9:
572       clipEndLeft( x0, y0, x1, y1, xLeft );
573       if ( y1 > yTop )
574         clipEndTop( x0, y0, x1, y1, yTop );
575       return true;
576 
577     case 10:
578       clipEndRight( x0, y0, x1, y1, xRight );
579       if ( y1 > yTop )
580         clipEndTop( x0, y0, x1, y1, yTop );
581       return true;
582 
583     case 16:
584       clipStartLeft( x0, y0, x1, y1, xLeft );
585       return true;
586 
587     case 18:
588       clipStartLeft( x0, y0, x1, y1, xLeft );
589       clipEndRight( x0, y0, x1, y1, xRight );
590       return true;
591 
592     case 20:
593       clipStartLeft( x0, y0, x1, y1, xLeft );
594       if ( y0 < yBottom )
595         return false;
596       clipEndBottom( x0, y0, x1, y1, yBottom );
597       return true;
598 
599     case 22:
600       clipStartLeft( x0, y0, x1, y1, xLeft );
601       if ( y0 < yBottom )
602         return false;
603       clipEndBottom( x0, y0, x1, y1, yBottom );
604       if ( x1 > xRight )
605         clipEndRight( x0, y0, x1, y1, xRight );
606       return true;
607 
608     case 24:
609       clipStartLeft( x0, y0, x1, y1, xLeft );
610       if ( y0 > yTop )
611         return false;
612       clipEndTop( x0, y0, x1, y1, yTop );
613       return true;
614 
615     case 26:
616       clipStartLeft( x0, y0, x1, y1, xLeft );
617       if ( y0 > yTop )
618         return false;
619       clipEndTop( x0, y0, x1, y1, yTop );
620       if ( x1 > xRight )
621         clipEndRight( x0, y0, x1, y1, xRight );
622       return true;
623 
624     case 32:
625       clipStartRight( x0, y0, x1, y1, xRight );
626       return true;
627 
628     case 33:
629       clipStartRight( x0, y0, x1, y1, xRight );
630       clipEndLeft( x0, y0, x1, y1, xLeft );
631       return true;
632 
633     case 36:
634       clipStartRight( x0, y0, x1, y1, xRight );
635       if ( y0 < yBottom )
636         return false;
637       clipEndBottom( x0, y0, x1, y1, yBottom );
638       return true;
639 
640     case 37:
641       clipStartRight( x0, y0, x1, y1, xRight );
642       if ( y0 < yBottom )
643         return false;
644       clipEndBottom( x0, y0, x1, y1, yBottom );
645       if ( x1 < xLeft )
646         clipEndLeft( x0, y0, x1, y1, xLeft );
647       return true;
648 
649     case 40:
650       clipStartRight( x0, y0, x1, y1, xRight );
651       if ( y0 > yTop )
652         return false;
653       clipEndTop( x0, y0, x1, y1, yTop );
654       return true;
655 
656     case 41:
657       clipStartRight( x0, y0, x1, y1, xRight );
658       if ( y0 > yTop )
659         return false;
660       clipEndTop( x0, y0, x1, y1, yTop );
661       if ( x1 < xLeft )
662         clipEndLeft( x0, y0, x1, y1, xLeft );
663       return true;
664 
665     case 64:
666       clipStartBottom( x0, y0, x1, y1, yBottom );
667       return true;
668 
669     case 65:
670       clipStartBottom( x0, y0, x1, y1, yBottom );
671       if ( x0 < xLeft )
672         return false;
673       clipEndLeft( x0, y0, x1, y1, xLeft );
674       if ( y1 < yBottom )
675         clipEndBottom( x0, y0, x1, y1, yBottom );
676       return true;
677 
678     case 66:
679       clipStartBottom( x0, y0, x1, y1, yBottom );
680       if ( x0 > xRight )
681         return false;
682       clipEndRight( x0, y0, x1, y1, xRight );
683       return true;
684 
685     case 72:
686       clipStartBottom( x0, y0, x1, y1, yBottom );
687       clipEndTop( x0, y0, x1, y1, yTop );
688       return true;
689 
690     case 73:
691       clipStartBottom( x0, y0, x1, y1, yBottom );
692       if ( x0 < xLeft )
693         return false;
694       clipEndLeft( x0, y0, x1, y1, xLeft );
695       if ( y1 > yTop )
696         clipEndTop( x0, y0, x1, y1, yTop );
697       return true;
698 
699     case 74:
700       clipStartBottom( x0, y0, x1, y1, yBottom );
701       if ( x0 > xRight )
702         return false;
703       clipEndRight( x0, y0, x1, y1, xRight );
704       if ( y1 > yTop )
705         clipEndTop( x0, y0, x1, y1, yTop );
706       return true;
707 
708     case 80:
709       clipStartLeft( x0, y0, x1, y1, xLeft );
710       if ( y0 < yBottom )
711         clipStartBottom( x0, y0, x1, y1, yBottom );
712       return true;
713 
714     case 82:
715       clipEndRight( x0, y0, x1, y1, xRight );
716       if ( y1 < yBottom )
717         return false;
718       clipStartBottom( x0, y0, x1, y1, yBottom );
719       if ( x0 < xLeft )
720         clipStartLeft( x0, y0, x1, y1, xLeft );
721       return true;
722 
723     case 88:
724       clipEndTop( x0, y0, x1, y1, yTop );
725       if ( x1 < xLeft )
726         return false;
727       clipStartBottom( x0, y0, x1, y1, yBottom );
728       if ( x0 < xLeft )
729         clipStartLeft( x0, y0, x1, y1, xLeft );
730       return true;
731 
732     case 90:
733       clipStartLeft( x0, y0, x1, y1, xLeft );
734       if ( y0 > yTop )
735         return false;
736       clipEndRight( x0, y0, x1, y1, xRight );
737       if ( y1 < yBottom )
738         return false;
739       if ( y0 < yBottom )
740         clipStartBottom( x0, y0, x1, y1, yBottom );
741       if ( y1 > yTop )
742         clipEndTop( x0, y0, x1, y1, yTop );
743       return true;
744 
745     case 96:
746       clipStartRight( x0, y0, x1, y1, xRight );
747       if ( y0 < yBottom )
748         clipStartBottom( x0, y0, x1, y1, yBottom );
749       return true;
750 
751     case 97:
752       clipEndLeft( x0, y0, x1, y1, xLeft );
753       if ( y1 < yBottom )
754         return false;
755       clipStartBottom( x0, y0, x1, y1, yBottom );
756       if ( x0 > xRight )
757         clipStartRight( x0, y0, x1, y1, xRight );
758       return true;
759 
760     case 104:
761       clipEndTop( x0, y0, x1, y1, yTop );
762       if ( x1 > xRight )
763         return false;
764       clipStartRight( x0, y0, x1, y1, xRight );
765       if ( y0 < yBottom )
766         clipStartBottom( x0, y0, x1, y1, yBottom );
767       return true;
768 
769     case 105:
770       clipEndLeft( x0, y0, x1, y1, xLeft );
771       if ( y1 < yBottom )
772         return false;
773       clipStartRight( x0, y0, x1, y1, xRight );
774       if ( y0 > yTop )
775         return false;
776       if ( y1 > yTop )
777         clipEndTop( x0, y0, x1, y1, yTop );
778       if ( y0 < yBottom )
779         clipStartBottom( x0, y0, x1, y1, yBottom );
780       return true;
781 
782     case 128:
783       clipStartTop( x0, y0, x1, y1, yTop );
784       return true;
785 
786     case 129:
787       clipStartTop( x0, y0, x1, y1, yTop );
788       if ( x0 < xLeft )
789         return false;
790       clipEndLeft( x0, y0, x1, y1, xLeft );
791       return true;
792 
793     case 130:
794       clipStartTop( x0, y0, x1, y1, yTop );
795       if ( x0 > xRight )
796         return false;
797       clipEndRight( x0, y0, x1, y1, xRight );
798       return true;
799 
800     case 132:
801       clipStartTop( x0, y0, x1, y1, yTop );
802       clipEndBottom( x0, y0, x1, y1, yBottom );
803       return true;
804 
805     case 133:
806       clipStartTop( x0, y0, x1, y1, yTop );
807       if ( x0 < xLeft )
808         return false;
809       clipEndLeft( x0, y0, x1, y1, xLeft );
810       if ( y1 < yBottom )
811         clipEndBottom( x0, y0, x1, y1, yBottom );
812       return true;
813 
814     case 134:
815       clipStartTop( x0, y0, x1, y1, yTop );
816       if ( x0 > xRight )
817         return false;
818       clipEndRight( x0, y0, x1, y1, xRight );
819       if ( y1 < yBottom )
820         clipEndBottom( x0, y0, x1, y1, yBottom );
821       return true;
822 
823     case 144:
824       clipStartLeft( x0, y0, x1, y1, xLeft );
825       if ( y0 > yTop )
826         clipStartTop( x0, y0, x1, y1, yTop );
827       return true;
828 
829     case 146:
830       clipEndRight( x0, y0, x1, y1, xRight );
831       if ( y1 > yTop )
832         return false;
833       clipStartTop( x0, y0, x1, y1, yTop );
834       if ( x0 < xLeft )
835         clipStartLeft( x0, y0, x1, y1, xLeft );
836       return true;
837 
838     case 148:
839       clipEndBottom( x0, y0, x1, y1, yBottom );
840       if ( x1 < xLeft )
841         return false;
842       clipStartLeft( x0, y0, x1, y1, xLeft );
843       if ( y0 > yTop )
844         clipStartTop( x0, y0, x1, y1, yTop );
845       return true;
846 
847     case 150:
848       clipStartLeft( x0, y0, x1, y1, xLeft );
849       if ( y0 < yBottom )
850         return false;
851       clipEndRight( x0, y0, x1, y1, xRight );
852       if ( y1 > yTop )
853         return false;
854       if ( y0 > yTop )
855         clipStartTop( x0, y0, x1, y1, yTop );
856       if ( y1 < yBottom )
857         clipEndBottom( x0, y0, x1, y1, yBottom );
858       return true;
859 
860     case 160:
861       clipStartRight( x0, y0, x1, y1, xRight );
862       if ( y0 > yTop )
863         clipStartTop( x0, y0, x1, y1, yTop );
864       return true;
865 
866     case 161:
867       clipEndLeft( x0, y0, x1, y1, xLeft );
868       if ( y1 > yTop )
869         return false;
870       clipStartTop( x0, y0, x1, y1, yTop );
871       if ( x0 > xRight )
872         clipStartRight( x0, y0, x1, y1, xRight );
873       return true;
874 
875     case 164:
876       clipEndBottom( x0, y0, x1, y1, yBottom );
877       if ( x1 > xRight )
878         return false;
879       clipStartRight( x0, y0, x1, y1, xRight );
880       if ( y0 > yTop )
881         clipStartTop( x0, y0, x1, y1, yTop );
882       return true;
883 
884     case 165:
885       clipEndLeft( x0, y0, x1, y1, xLeft );
886       if ( y1 > yTop )
887         return false;
888       clipStartRight( x0, y0, x1, y1, xRight );
889       if ( y0 < yBottom )
890         return false;
891       if ( y1 < yBottom )
892         clipEndBottom( x0, y0, x1, y1, yBottom );
893       if ( y0 > yTop )
894         clipStartTop( x0, y0, x1, y1, yTop );
895       return true;
896   }
897 
898   return false;
899 
900 }
901 #endif // SIP_RUN
902 
903 
904 #endif
905