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