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
QgsRegularPolygon(const QgsPoint & center,const double radius,const double azimuth,const unsigned int numSides,const ConstructionOption circle)23 QgsRegularPolygon::QgsRegularPolygon( const QgsPoint ¢er, const double radius, const double azimuth, const unsigned int numSides, const ConstructionOption circle )
24 : mCenter( center )
25 {
26 // TODO: inclination
27
28 if ( numSides >= 3 )
29 {
30 mNumberSides = numSides;
31
32 switch ( circle )
33 {
34 case InscribedCircle:
35 {
36 mRadius = std::fabs( radius );
37 mFirstVertex = mCenter.project( mRadius, azimuth );
38 break;
39 }
40 case CircumscribedCircle:
41 {
42 mRadius = apothemToRadius( std::fabs( radius ), numSides );
43 mFirstVertex = mCenter.project( mRadius, azimuth - centralAngle( numSides ) / 2 );
44 break;
45 }
46 }
47
48 }
49
50 }
51
QgsRegularPolygon(const QgsPoint & center,const QgsPoint & pt1,const unsigned int numSides,const ConstructionOption circle)52 QgsRegularPolygon::QgsRegularPolygon( const QgsPoint ¢er, const QgsPoint &pt1, const unsigned int numSides, const ConstructionOption circle )
53 : mCenter( center )
54 {
55 if ( numSides >= 3 )
56 {
57 mNumberSides = numSides;
58
59 switch ( circle )
60 {
61 case InscribedCircle:
62 {
63 mFirstVertex = pt1;
64 mRadius = center.distance( pt1 );
65 break;
66 }
67 case CircumscribedCircle:
68 {
69 mRadius = apothemToRadius( center.distance( pt1 ), numSides );
70 const double azimuth = center.azimuth( pt1 );
71 // TODO: inclination
72 mFirstVertex = mCenter.project( mRadius, azimuth - centralAngle( numSides ) / 2 );
73 break;
74 }
75 }
76
77 }
78
79 }
80
QgsRegularPolygon(const QgsPoint & pt1,const QgsPoint & pt2,const unsigned int numSides)81 QgsRegularPolygon::QgsRegularPolygon( const QgsPoint &pt1, const QgsPoint &pt2, const unsigned int numSides )
82 {
83 if ( numSides >= 3 )
84 {
85 mNumberSides = numSides;
86
87 const double azimuth = pt1.azimuth( pt2 );
88 const QgsPoint pm = QgsGeometryUtils::midpoint( pt1, pt2 );
89 const double length = pt1.distance( pm );
90
91 const double angle = ( 180 - ( 360 / numSides ) ) / 2.0;
92 const double hypothenuse = length / std::cos( angle * M_PI / 180 );
93 // TODO: inclination
94
95 mCenter = pt1.project( hypothenuse, azimuth + angle );
96 mFirstVertex = pt1;
97 mRadius = std::fabs( hypothenuse );
98 }
99 }
100
operator ==(const QgsRegularPolygon & rp) const101 bool QgsRegularPolygon::operator ==( const QgsRegularPolygon &rp ) const
102 {
103 return ( ( mCenter == rp.mCenter ) &&
104 ( mFirstVertex == rp.mFirstVertex ) &&
105 ( mNumberSides == rp.mNumberSides )
106 );
107 }
108
operator !=(const QgsRegularPolygon & rp) const109 bool QgsRegularPolygon::operator !=( const QgsRegularPolygon &rp ) const
110 {
111 return !operator==( rp );
112 }
113
isEmpty() const114 bool QgsRegularPolygon::isEmpty() const
115 {
116 return ( ( mNumberSides < 3 ) ||
117 ( mCenter.isEmpty() ) ||
118 ( mFirstVertex.isEmpty() ) ||
119 ( mCenter == mFirstVertex )
120 );
121 }
122
setCenter(const QgsPoint & center)123 void QgsRegularPolygon::setCenter( const QgsPoint ¢er )
124 {
125 const double azimuth = mFirstVertex.isEmpty() ? 0 : mCenter.azimuth( mFirstVertex );
126 // TODO: double inclination = mCenter.inclination(mFirstVertex);
127 mCenter = center;
128 mFirstVertex = center.project( mRadius, azimuth );
129 }
130
setRadius(const double radius)131 void QgsRegularPolygon::setRadius( const double radius )
132 {
133 mRadius = std::fabs( radius );
134 const double azimuth = mFirstVertex.isEmpty() ? 0 : mCenter.azimuth( mFirstVertex );
135 // TODO: double inclination = mCenter.inclination(mFirstVertex);
136 mFirstVertex = mCenter.project( mRadius, azimuth );
137 }
138
setFirstVertex(const QgsPoint & firstVertex)139 void QgsRegularPolygon::setFirstVertex( const QgsPoint &firstVertex )
140 {
141 const double azimuth = mCenter.azimuth( mFirstVertex );
142 // TODO: double inclination = mCenter.inclination(firstVertex);
143 mFirstVertex = firstVertex;
144 mCenter = mFirstVertex.project( mRadius, azimuth );
145 }
146
setNumberSides(const unsigned int numSides)147 void QgsRegularPolygon::setNumberSides( const unsigned int numSides )
148 {
149 if ( numSides >= 3 )
150 {
151 mNumberSides = numSides;
152 }
153 }
154
points() const155 QgsPointSequence QgsRegularPolygon::points() const
156 {
157 QgsPointSequence pts;
158 if ( isEmpty() )
159 {
160 return pts;
161 }
162
163 double azimuth = mCenter.azimuth( mFirstVertex );
164 const double azimuth_add = centralAngle();
165 // TODO: inclination
166
167 unsigned int n = 1;
168 while ( n <= mNumberSides )
169 {
170 pts.push_back( mCenter.project( mRadius, azimuth ) );
171 azimuth += azimuth_add;
172 if ( ( azimuth_add > 0 ) && ( azimuth > 180.0 ) )
173 {
174 azimuth -= 360.0;
175 }
176
177 n++;
178 }
179
180 return pts;
181 }
182
toPolygon() const183 QgsPolygon *QgsRegularPolygon::toPolygon() const
184 {
185 std::unique_ptr<QgsPolygon> polygon( new QgsPolygon() );
186 if ( isEmpty() )
187 {
188 return polygon.release();
189 }
190
191 polygon->setExteriorRing( toLineString() );
192
193 return polygon.release();
194 }
195
toLineString() const196 QgsLineString *QgsRegularPolygon::toLineString() const
197 {
198 std::unique_ptr<QgsLineString> ext( new QgsLineString() );
199 if ( isEmpty() )
200 {
201 return ext.release();
202 }
203
204 QgsPointSequence pts;
205 pts = points();
206
207 ext->setPoints( pts );
208 ext->addVertex( pts.at( 0 ) ); //close regular polygon
209
210 return ext.release();
211 }
212
toTriangle() const213 QgsTriangle QgsRegularPolygon::toTriangle() const
214 {
215 if ( isEmpty() || ( mNumberSides != 3 ) )
216 {
217 return QgsTriangle();
218 }
219
220 QgsPointSequence pts;
221 pts = points();
222
223 return QgsTriangle( pts.at( 0 ), pts.at( 1 ), pts.at( 2 ) );
224 }
225
triangulate() const226 QVector<QgsTriangle> QgsRegularPolygon::triangulate() const
227 {
228 QVector<QgsTriangle> l_tri;
229 if ( isEmpty() )
230 {
231 return l_tri;
232 }
233
234 QgsPointSequence pts;
235 pts = points();
236
237 unsigned int n = 0;
238 while ( n < mNumberSides - 1 )
239 {
240 l_tri.append( QgsTriangle( pts.at( n ), pts.at( n + 1 ), mCenter ) );
241 n++;
242 }
243 l_tri.append( QgsTriangle( pts.at( n ), pts.at( 0 ), mCenter ) );
244
245 return l_tri;
246 }
247
inscribedCircle() const248 QgsCircle QgsRegularPolygon::inscribedCircle() const
249 {
250 // TODO: inclined circle
251 return QgsCircle( mCenter, apothem() );
252 }
253
circumscribedCircle() const254 QgsCircle QgsRegularPolygon::circumscribedCircle() const
255 {
256 // TODO: inclined circle
257 return QgsCircle( mCenter, mRadius );
258 }
259
toString(int pointPrecision,int radiusPrecision,int anglePrecision) const260 QString QgsRegularPolygon::toString( int pointPrecision, int radiusPrecision, int anglePrecision ) const
261 {
262 QString rep;
263 if ( isEmpty() )
264 rep = QStringLiteral( "Empty" );
265 else
266 rep = QStringLiteral( "RegularPolygon (Center: %1, First Vertex: %2, Radius: %3, Azimuth: %4)" )
267 .arg( mCenter.asWkt( pointPrecision ), 0, 's' )
268 .arg( mFirstVertex.asWkt( pointPrecision ), 0, 's' )
269 .arg( qgsDoubleToString( mRadius, radiusPrecision ), 0, 'f' )
270 .arg( qgsDoubleToString( mCenter.azimuth( mFirstVertex ), anglePrecision ), 0, 'f' );
271 // TODO: inclination
272 // .arg( qgsDoubleToString( mCenter.inclination(mFirstVertex), anglePrecision ), 0, 'f' );
273
274 return rep;
275 }
276
area() const277 double QgsRegularPolygon::area() const
278 {
279 if ( isEmpty() )
280 {
281 return 0.0;
282 }
283
284 return ( mRadius * mRadius * mNumberSides * std::sin( centralAngle() * M_PI / 180.0 ) ) / 2;
285 }
286
perimeter() const287 double QgsRegularPolygon::perimeter() const
288 {
289 if ( isEmpty() )
290 {
291 return 0.0;
292 }
293
294 return length() * mNumberSides;
295 }
296
length() const297 double QgsRegularPolygon::length() const
298 {
299 if ( isEmpty() )
300 {
301 return 0.0;
302 }
303
304 return mRadius * 2 * std::sin( M_PI / mNumberSides );
305 }
306
apothemToRadius(const double apothem,const unsigned int numSides) const307 double QgsRegularPolygon::apothemToRadius( const double apothem, const unsigned int numSides ) const
308 {
309 return apothem / std::cos( M_PI / numSides );
310 }
311
interiorAngle(const unsigned int nbSides) const312 double QgsRegularPolygon::interiorAngle( const unsigned int nbSides ) const
313 {
314 return ( nbSides - 2 ) * 180 / nbSides;
315 }
316
centralAngle(const unsigned int nbSides) const317 double QgsRegularPolygon::centralAngle( const unsigned int nbSides ) const
318 {
319 return 360.0 / nbSides;
320 }
321
interiorAngle() const322 double QgsRegularPolygon::interiorAngle() const
323 {
324 return interiorAngle( mNumberSides );
325 }
326
centralAngle() const327 double QgsRegularPolygon::centralAngle() const
328 {
329 return centralAngle( mNumberSides );
330 }
331