15
15
* (at your option) any later version. *
17
17
***************************************************************************/
18
/* $Id: qgsclipper.h 4502 2006-01-08 01:18:20Z timlinux $ */
20
20
#ifndef QGSCLIPPER_H
21
21
#define QGSCLIPPER_H
23
23
#include "qgspoint.h"
24
#include "q3pointarray.h"
29
28
#include <iostream>
31
// The functions in this class are likely to be called from within a
32
// render loop and hence need to as CPU efficient as possible.
34
// The main purpose of the functions in this class are to trim lines
35
// and polygons to lie within a rectangular region. This is necessary
36
// for drawing items to an X11 display which have a limit on the
37
// magnitude of the screen coordinates (+/- 32768, i.e. 16 bit integer).
31
* A class to trim lines and polygons to within a rectangular region.
32
* The functions in this class are likely to be called from within a
33
* render loop and hence need to as CPU efficient as possible.
34
* The main purpose of the functions in this class are to trim lines
35
* and polygons to lie within a rectangular region. This is necessary
36
* for drawing items to an X11 display which have a limit on the
37
* magnitude of the screen coordinates (+/- 32768, i.e. 16 bit integer).
40
class CORE_EXPORT QgsClipper
43
// Coordinates of the rectangular box that we trim to.
45
// These are the limits for X11 screen coordinates. The actual
46
// values are +/-32767, but we allow a little bit of space for
49
// You may wonder why the clipping is done to these coordindates
50
// rather than the boundaries of the qgis canvas. Reasons include:
51
// - making the boundaries static const allows the compiler to
52
// optimise the code that uses these values more than if they changed
53
// for every call to the trim code.
54
// - clipping takes quite a bit of CPU effort, and the less that this is
55
// done the better. More stuff would have to be clipped if the
56
// boundaries were the qgis canvas (but this may be offset by
57
// having less to draw).
59
// The limit is set to 30,000 instead of 32768 because that things
62
static const double maxX;
63
static const double minX;
64
static const double maxY;
65
static const double minY;
67
// Used when testing for equivalance to 0.0
68
static const double SMALL_NUM;
70
// A handy way to refer to the four boundaries
71
enum boundary {Xmax, Xmin, Ymax, Ymin};
73
// Trims the given feature to a rectangular box. Returns the trimmed
74
// feature in x and y. The shapeOpen parameter determines whether
75
// the function treats the points as a closed shape (polygon), or as
76
// an open shape (linestring).
77
static void trimFeature(std::vector<double>& x,
78
std::vector<double>& y,
83
// Trims the given feature to the given boundary. Returns the
84
// trimmed feature in the outX and outY vectors.
85
static void trimFeatureToBoundary(const std::vector<double>& inX,
86
const std::vector<double>& inY,
87
std::vector<double>& outX,
88
std::vector<double>& outY,
92
// Determines if a point is inside or outside the given boundary
93
static bool inside(const double x, const double y, boundary b);
95
// Calculates the intersection point between a line defined by a
96
// (x1, y1), and (x2, y2) and the given boundary
97
static QgsPoint intersect(const double x1, const double y1,
98
const double x2, const double y2,
44
// Coordinates of the rectangular box that we trim to.
46
// These are the limits for X11 screen coordinates. The actual
47
// values are +/-32767, but we allow a little bit of space for
50
// You may wonder why the clipping is done to these coordindates
51
// rather than the boundaries of the qgis canvas. Reasons include:
52
// - making the boundaries static const allows the compiler to
53
// optimise the code that uses these values more than if they changed
54
// for every call to the trim code.
55
// - clipping takes quite a bit of CPU effort, and the less that this is
56
// done the better. More stuff would have to be clipped if the
57
// boundaries were the qgis canvas (but this may be offset by
58
// having less to draw).
60
// The limit is set to 30,000 instead of 32768 because that things
63
static const double MAX_X;
64
static const double MIN_X;
65
static const double MAX_Y;
66
static const double MIN_Y;
69
// A handy way to refer to the four boundaries
70
enum Boundary {XMax, XMin, YMax, YMin};
72
// Trims the given feature to a rectangular box. Returns the trimmed
73
// feature in x and y. The shapeOpen parameter determines whether
74
// the function treats the points as a closed shape (polygon), or as
75
// an open shape (linestring).
76
static void trimFeature( std::vector<double>& x,
77
std::vector<double>& y,
82
// Used when testing for equivalance to 0.0
83
static const double SMALL_NUM;
85
// Trims the given feature to the given boundary. Returns the
86
// trimmed feature in the outX and outY vectors.
87
static void trimFeatureToBoundary( const std::vector<double>& inX,
88
const std::vector<double>& inY,
89
std::vector<double>& outX,
90
std::vector<double>& outY,
94
// Determines if a point is inside or outside the given boundary
95
static bool inside( const double x, const double y, Boundary b );
97
// Calculates the intersection point between a line defined by a
98
// (x1, y1), and (x2, y2) and the given boundary
99
static QgsPoint intersect( const double x1, const double y1,
100
const double x2, const double y2,
102
104
// The inline functions
105
107
// polygon-clipping algorithm. See J. D. Foley, A. van Dam,
106
108
// S. K. Feiner, and J. F. Hughes, Computer Graphics, Principles and
107
109
// Practice. Addison-Wesley Systems Programming Series,
108
// Addison-Wesley, 2nd ed., 1991.
110
// Addison-Wesley, 2nd ed., 1991.
110
112
// I understand that this is not the most efficient algorithm, but is
111
113
// one (the only?) that is guaranteed to always give the correct
114
inline void QgsClipper::trimFeature(std::vector<double>& x,
115
std::vector<double>& y,
116
inline void QgsClipper::trimFeature( std::vector<double>& x,
117
std::vector<double>& y,
118
120
std::vector<double> tmpX;
119
121
std::vector<double> tmpY;
120
trimFeatureToBoundary(x, y, tmpX, tmpY, Xmax, shapeOpen);
122
trimFeatureToBoundary( x, y, tmpX, tmpY, XMax, shapeOpen );
124
trimFeatureToBoundary(tmpX, tmpY, x, y, Ymax, shapeOpen);
126
trimFeatureToBoundary( tmpX, tmpY, x, y, YMax, shapeOpen );
128
trimFeatureToBoundary(x, y, tmpX, tmpY, Xmin, shapeOpen);
130
trimFeatureToBoundary( x, y, tmpX, tmpY, XMin, shapeOpen );
132
trimFeatureToBoundary(tmpX, tmpY, x, y, Ymin, shapeOpen);
134
trimFeatureToBoundary( tmpX, tmpY, x, y, YMin, shapeOpen );
135
137
// An auxilary function that is part of the polygon trimming
138
140
// Hodgman's polygon-clipping algorithm.
140
142
inline void QgsClipper::trimFeatureToBoundary(
141
const std::vector<double>& inX,
142
const std::vector<double>& inY,
143
std::vector<double>& outX,
144
std::vector<double>& outY,
145
boundary b, bool shapeOpen)
143
const std::vector<double>& inX,
144
const std::vector<double>& inY,
145
std::vector<double>& outX,
146
std::vector<double>& outY,
147
Boundary b, bool shapeOpen )
147
149
// The shapeOpen parameter selects whether this function treats the
148
150
// shape as open or closed. False is appropriate for polygons and
149
151
// true for polylines.
151
int i1 = inX.size() - 1; // start with last point
153
unsigned int i1 = inX.size() - 1; // start with last point
153
155
// and compare to the first point initially.
154
for (int i2 = 0; i2 < inX.size() ; ++i2)
156
for ( unsigned int i2 = 0; i2 < inX.size() ; ++i2 )
155
157
{ // look at each edge of the polygon in turn
156
if (inside(inX[i2], inY[i2], b)) // end point of edge is inside boundary
158
if ( inside( inX[i2], inY[i2], b ) ) // end point of edge is inside boundary
158
if (inside(inX[i1], inY[i1], b))
160
if ( inside( inX[i1], inY[i1], b ) )
160
outX.push_back( inX[i2] );
161
outY.push_back( inY[i2] );
162
outX.push_back( inX[i2] );
163
outY.push_back( inY[i2] );
165
// edge crosses into the boundary, so trim back to the boundary, and
166
// store both ends of the new edge
167
if (!(i2 == 0 && shapeOpen))
169
QgsPoint p = intersect(inX[i1], inY[i1], inX[i2], inY[i2], b);
170
outX.push_back( p.x() );
171
outY.push_back( p.y() );
167
// edge crosses into the boundary, so trim back to the boundary, and
168
// store both ends of the new edge
169
if ( !( i2 == 0 && shapeOpen ) )
171
QgsPoint p = intersect( inX[i1], inY[i1], inX[i2], inY[i2], b );
172
outX.push_back( p.x() );
173
outY.push_back( p.y() );
174
outX.push_back( inX[i2] );
175
outY.push_back( inY[i2] );
176
outX.push_back( inX[i2] );
177
outY.push_back( inY[i2] );
178
180
else // end point of edge is outside boundary
180
182
// start point is in boundary, so need to trim back
181
if (inside(inX[i1], inY[i1], b))
183
if ( inside( inX[i1], inY[i1], b ) )
183
if (!(i2 == 0 && shapeOpen))
185
QgsPoint p = intersect(inX[i1], inY[i1], inX[i2], inY[i2], b);
186
outX.push_back( p.x() );
187
outY.push_back( p.y() );
185
if ( !( i2 == 0 && shapeOpen ) )
187
QgsPoint p = intersect( inX[i1], inY[i1], inX[i2], inY[i2], b );
188
outX.push_back( p.x() );
189
outY.push_back( p.y() );
224
226
// returns the intersection of the line defined by the given points
225
227
// and the given boundary.
227
inline QgsPoint QgsClipper::intersect(const double x1, const double y1,
228
const double x2, const double y2,
229
inline QgsPoint QgsClipper::intersect( const double x1, const double y1,
230
const double x2, const double y2,
231
233
// This function assumes that the two given points (x1, y1), and
232
234
// (x2, y2) cross the given boundary. Making this assumption allows
233
// some optimisations.
235
// some optimisations.
237
double r_n = SMALL_NUM, r_d = SMALL_NUM;
239
case Xmax: // x = maxX boundary
240
r_n = -(x1 - maxX) * (maxY - minY);
241
r_d = (x2 - x1) * (maxY - minY);
243
case Xmin: // x = minX boundary
244
r_n = -(x1 - minX) * (maxY - minY);
245
r_d = (x2 - x1) * (maxY - minY);
247
case Ymax: // y = maxY boundary
248
r_n = (y1 - maxY) * (maxX - minX);
249
r_d = -(y2 - y1) * (maxX - minX);
251
case Ymin: // y = minY boundary
252
r_n = (y1 - minY) * (maxX - minX);
253
r_d = -(y2 - y1) * (maxX - minX);
241
case XMax: // x = MAX_X boundary
242
r_n = -( x1 - MAX_X ) * ( MAX_Y - MIN_Y );
243
r_d = ( x2 - x1 ) * ( MAX_Y - MIN_Y );
245
case XMin: // x = MIN_X boundary
246
r_n = -( x1 - MIN_X ) * ( MAX_Y - MIN_Y );
247
r_d = ( x2 - x1 ) * ( MAX_Y - MIN_Y );
249
case YMax: // y = MAX_Y boundary
250
r_n = ( y1 - MAX_Y ) * ( MAX_X - MIN_X );
251
r_d = -( y2 - y1 ) * ( MAX_X - MIN_X );
253
case YMin: // y = MIN_Y boundary
254
r_n = ( y1 - MIN_Y ) * ( MAX_X - MIN_X );
255
r_d = -( y2 - y1 ) * ( MAX_X - MIN_X );
259
if (std::abs(r_d) > SMALL_NUM && std::abs(r_n) > SMALL_NUM)
261
if ( std::abs( r_d ) > SMALL_NUM && std::abs( r_n ) > SMALL_NUM )
261
263
double r = r_n / r_d;
262
p.set(x1 + r*(x2 - x1), y1 + r*(y2 - y1));
264
p.set( x1 + r*( x2 - x1 ), y1 + r*( y2 - y1 ) );
266
268
// Should never get here, but if we do for some reason, cause a
267
269
// clunk because something else is wrong if we do.
268
Q_ASSERT(std::abs(r_d) > SMALL_NUM && std::abs(r_n) > SMALL_NUM);
270
Q_ASSERT( std::abs( r_d ) > SMALL_NUM && std::abs( r_n ) > SMALL_NUM );