Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsregularpolygon.cpp
3 : : --------------
4 : : begin : May 2017
5 : : copyright : (C) 2017 by Loîc Bartoletti
6 : : email : lbartoletti at tuxfamily dot org
7 : : ***************************************************************************/
8 : :
9 : : /***************************************************************************
10 : : * *
11 : : * This program is free software; you can redistribute it and/or modify *
12 : : * it under the terms of the GNU General Public License as published by *
13 : : * the Free Software Foundation; either version 2 of the License, or *
14 : : * (at your option) any later version. *
15 : : * *
16 : : ***************************************************************************/
17 : :
18 : : #include "qgsregularpolygon.h"
19 : : #include "qgsgeometryutils.h"
20 : :
21 : : #include <memory>
22 : :
23 : 11 : QgsRegularPolygon::QgsRegularPolygon( const QgsPoint ¢er, const double radius, const double azimuth, const unsigned int numSides, const ConstructionOption circle )
24 : 11 : : mCenter( center )
25 : : {
26 : : // TODO: inclination
27 : :
28 : 11 : if ( numSides >= 3 )
29 : : {
30 : 10 : mNumberSides = numSides;
31 : :
32 : 10 : switch ( circle )
33 : : {
34 : : case InscribedCircle:
35 : : {
36 : 5 : mRadius = std::fabs( radius );
37 : 5 : mFirstVertex = mCenter.project( mRadius, azimuth );
38 : 5 : break;
39 : : }
40 : : case CircumscribedCircle:
41 : : {
42 : 4 : mRadius = apothemToRadius( std::fabs( radius ), numSides );
43 : 4 : mFirstVertex = mCenter.project( mRadius, azimuth - centralAngle( numSides ) / 2 );
44 : 4 : break;
45 : : }
46 : : }
47 : :
48 : 10 : }
49 : :
50 : 11 : }
51 : :
52 : 9 : QgsRegularPolygon::QgsRegularPolygon( const QgsPoint ¢er, const QgsPoint &pt1, const unsigned int numSides, const ConstructionOption circle )
53 : 9 : : mCenter( center )
54 : : {
55 : 9 : if ( numSides >= 3 )
56 : : {
57 : 6 : mNumberSides = numSides;
58 : :
59 : 6 : switch ( circle )
60 : : {
61 : : case InscribedCircle:
62 : : {
63 : 3 : mFirstVertex = pt1;
64 : 3 : mRadius = center.distance( pt1 );
65 : 3 : break;
66 : : }
67 : : case CircumscribedCircle:
68 : : {
69 : 1 : mRadius = apothemToRadius( center.distance( pt1 ), numSides );
70 : 1 : double azimuth = center.azimuth( pt1 );
71 : : // TODO: inclination
72 : 1 : mFirstVertex = mCenter.project( mRadius, azimuth - centralAngle( numSides ) / 2 );
73 : 1 : break;
74 : : }
75 : : }
76 : :
77 : 6 : }
78 : :
79 : 9 : }
80 : :
81 : 3 : QgsRegularPolygon::QgsRegularPolygon( const QgsPoint &pt1, const QgsPoint &pt2, const unsigned int numSides )
82 : : {
83 : 3 : if ( numSides >= 3 )
84 : : {
85 : 3 : mNumberSides = numSides;
86 : :
87 : 3 : double azimuth = pt1.azimuth( pt2 );
88 : 3 : QgsPoint pm = QgsGeometryUtils::midpoint( pt1, pt2 );
89 : 3 : double length = pt1.distance( pm );
90 : :
91 : 3 : double angle = ( 180 - ( 360 / numSides ) ) / 2.0;
92 : 3 : double hypothenuse = length / std::cos( angle * M_PI / 180 );
93 : : // TODO: inclination
94 : :
95 : 3 : mCenter = pt1.project( hypothenuse, azimuth + angle );
96 : 3 : mFirstVertex = pt1;
97 : 3 : mRadius = std::fabs( hypothenuse );
98 : 3 : }
99 : 3 : }
100 : :
101 : 10 : bool QgsRegularPolygon::operator ==( const QgsRegularPolygon &rp ) const
102 : : {
103 : 16 : return ( ( mCenter == rp.mCenter ) &&
104 : 6 : ( mFirstVertex == rp.mFirstVertex ) &&
105 : 6 : ( mNumberSides == rp.mNumberSides )
106 : : );
107 : : }
108 : :
109 : 4 : bool QgsRegularPolygon::operator !=( const QgsRegularPolygon &rp ) const
110 : : {
111 : 4 : return !operator==( rp );
112 : : }
113 : :
114 : 51 : bool QgsRegularPolygon::isEmpty() const
115 : : {
116 : 83 : return ( ( mNumberSides < 3 ) ||
117 : 32 : ( mCenter.isEmpty() ) ||
118 : 31 : ( mFirstVertex.isEmpty() ) ||
119 : 30 : ( mCenter == mFirstVertex )
120 : : );
121 : : }
122 : :
123 : 2 : void QgsRegularPolygon::setCenter( const QgsPoint ¢er )
124 : : {
125 : 2 : double azimuth = mFirstVertex.isEmpty() ? 0 : mCenter.azimuth( mFirstVertex );
126 : : // TODO: double inclination = mCenter.inclination(mFirstVertex);
127 : 2 : mCenter = center;
128 : 2 : mFirstVertex = center.project( mRadius, azimuth );
129 : 2 : }
130 : :
131 : 1 : void QgsRegularPolygon::setRadius( const double radius )
132 : : {
133 : 1 : mRadius = std::fabs( radius );
134 : 1 : double azimuth = mFirstVertex.isEmpty() ? 0 : mCenter.azimuth( mFirstVertex );
135 : : // TODO: double inclination = mCenter.inclination(mFirstVertex);
136 : 1 : mFirstVertex = mCenter.project( mRadius, azimuth );
137 : 1 : }
138 : :
139 : 1 : void QgsRegularPolygon::setFirstVertex( const QgsPoint &firstVertex )
140 : : {
141 : 1 : double azimuth = mCenter.azimuth( mFirstVertex );
142 : : // TODO: double inclination = mCenter.inclination(firstVertex);
143 : 1 : mFirstVertex = firstVertex;
144 : 1 : mCenter = mFirstVertex.project( mRadius, azimuth );
145 : 1 : }
146 : :
147 : 8 : void QgsRegularPolygon::setNumberSides( const unsigned int numSides )
148 : : {
149 : 8 : if ( numSides >= 3 )
150 : : {
151 : 5 : mNumberSides = numSides;
152 : 5 : }
153 : 8 : }
154 : :
155 : 6 : QgsPointSequence QgsRegularPolygon::points() const
156 : : {
157 : 6 : QgsPointSequence pts;
158 : 6 : if ( isEmpty() )
159 : : {
160 : 1 : return pts;
161 : : }
162 : :
163 : 5 : double azimuth = mCenter.azimuth( mFirstVertex );
164 : 5 : double azimuth_add = centralAngle();
165 : : // TODO: inclination
166 : :
167 : 5 : unsigned int n = 1;
168 : 23 : while ( n <= mNumberSides )
169 : : {
170 : 18 : pts.push_back( mCenter.project( mRadius, azimuth ) );
171 : 18 : azimuth += azimuth_add;
172 : 18 : if ( ( azimuth_add > 0 ) && ( azimuth > 180.0 ) )
173 : : {
174 : 5 : azimuth -= 360.0;
175 : 5 : }
176 : :
177 : 18 : n++;
178 : : }
179 : :
180 : 5 : return pts;
181 : 6 : }
182 : :
183 : 2 : QgsPolygon *QgsRegularPolygon::toPolygon() const
184 : : {
185 : 2 : std::unique_ptr<QgsPolygon> polygon( new QgsPolygon() );
186 : 2 : if ( isEmpty() )
187 : : {
188 : 1 : return polygon.release();
189 : : }
190 : :
191 : 1 : polygon->setExteriorRing( toLineString() );
192 : :
193 : 1 : return polygon.release();
194 : 2 : }
195 : :
196 : 3 : QgsLineString *QgsRegularPolygon::toLineString() const
197 : : {
198 : 3 : std::unique_ptr<QgsLineString> ext( new QgsLineString() );
199 : 3 : if ( isEmpty() )
200 : : {
201 : 1 : return ext.release();
202 : : }
203 : :
204 : 2 : QgsPointSequence pts;
205 : 2 : pts = points();
206 : :
207 : 2 : ext->setPoints( pts );
208 : 2 : ext->addVertex( pts.at( 0 ) ); //close regular polygon
209 : :
210 : 2 : return ext.release();
211 : 3 : }
212 : :
213 : 3 : QgsTriangle QgsRegularPolygon::toTriangle() const
214 : : {
215 : 3 : if ( isEmpty() || ( mNumberSides != 3 ) )
216 : : {
217 : 2 : return QgsTriangle();
218 : : }
219 : :
220 : 1 : QgsPointSequence pts;
221 : 1 : pts = points();
222 : :
223 : 1 : return QgsTriangle( pts.at( 0 ), pts.at( 1 ), pts.at( 2 ) );
224 : 3 : }
225 : :
226 : 2 : QVector<QgsTriangle> QgsRegularPolygon::triangulate() const
227 : : {
228 : 25 : QVector<QgsTriangle> l_tri;
229 : 25 : if ( isEmpty() )
230 : : {
231 : 1 : return l_tri;
232 : : }
233 : :
234 : 1 : QgsPointSequence pts;
235 : 1 : pts = points();
236 : :
237 : 1 : unsigned int n = 0;
238 : 4 : while ( n < mNumberSides - 1 )
239 : : {
240 : 3 : l_tri.append( QgsTriangle( pts.at( n ), pts.at( n + 1 ), mCenter ) );
241 : 3 : n++;
242 : : }
243 : 1 : l_tri.append( QgsTriangle( pts.at( n ), pts.at( 0 ), mCenter ) );
244 : :
245 : 1 : return l_tri;
246 : 2 : }
247 : :
248 : 1 : QgsCircle QgsRegularPolygon::inscribedCircle() const
249 : : {
250 : : // TODO: inclined circle
251 : 1 : return QgsCircle( mCenter, apothem() );
252 : : }
253 : :
254 : 2 : QgsCircle QgsRegularPolygon::circumscribedCircle() const
255 : : {
256 : : // TODO: inclined circle
257 : 2 : return QgsCircle( mCenter, mRadius );
258 : : }
259 : :
260 : 2 : QString QgsRegularPolygon::toString( int pointPrecision, int radiusPrecision, int anglePrecision ) const
261 : : {
262 : 2 : QString rep;
263 : 2 : if ( isEmpty() )
264 : 2 : rep = QStringLiteral( "Empty" );
265 : : else
266 : 3 : rep = QStringLiteral( "RegularPolygon (Center: %1, First Vertex: %2, Radius: %3, Azimuth: %4)" )
267 : 1 : .arg( mCenter.asWkt( pointPrecision ), 0, 's' )
268 : 1 : .arg( mFirstVertex.asWkt( pointPrecision ), 0, 's' )
269 : 1 : .arg( qgsDoubleToString( mRadius, radiusPrecision ), 0, 'f' )
270 : 1 : .arg( qgsDoubleToString( mCenter.azimuth( mFirstVertex ), anglePrecision ), 0, 'f' );
271 : : // TODO: inclination
272 : : // .arg( qgsDoubleToString( mCenter.inclination(mFirstVertex), anglePrecision ), 0, 'f' );
273 : :
274 : 2 : return rep;
275 : 2 : }
276 : :
277 : 6 : double QgsRegularPolygon::area() const
278 : : {
279 : 6 : if ( isEmpty() )
280 : : {
281 : 2 : return 0.0;
282 : : }
283 : :
284 : 4 : return ( mRadius * mRadius * mNumberSides * std::sin( centralAngle() * M_PI / 180.0 ) ) / 2;
285 : 6 : }
286 : :
287 : 5 : double QgsRegularPolygon::perimeter() const
288 : : {
289 : 5 : if ( isEmpty() )
290 : : {
291 : 2 : return 0.0;
292 : : }
293 : :
294 : 3 : return length() * mNumberSides;
295 : 5 : }
296 : :
297 : 8 : double QgsRegularPolygon::length() const
298 : : {
299 : 8 : if ( isEmpty() )
300 : : {
301 : 2 : return 0.0;
302 : : }
303 : :
304 : 6 : return mRadius * 2 * std::sin( M_PI / mNumberSides );
305 : 8 : }
306 : :
307 : 5 : double QgsRegularPolygon::apothemToRadius( const double apothem, const unsigned int numSides ) const
308 : : {
309 : 5 : return apothem / std::cos( M_PI / numSides );
310 : : }
311 : :
312 : 4 : double QgsRegularPolygon::interiorAngle( const unsigned int nbSides ) const
313 : : {
314 : 4 : return ( nbSides - 2 ) * 180 / nbSides;
315 : : }
316 : :
317 : 18 : double QgsRegularPolygon::centralAngle( const unsigned int nbSides ) const
318 : : {
319 : 18 : return 360.0 / nbSides;
320 : : }
321 : :
322 : 4 : double QgsRegularPolygon::interiorAngle() const
323 : : {
324 : 4 : return interiorAngle( mNumberSides );
325 : : }
326 : :
327 : 13 : double QgsRegularPolygon::centralAngle() const
328 : : {
329 : 13 : return centralAngle( mNumberSides );
330 : : }
|