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 &center, 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 &center, 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 &center )
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