Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgstriangle.cpp
3 : : -------------------
4 : : begin : January 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 "qgstriangle.h"
19 : : #include "qgsgeometryutils.h"
20 : : #include "qgslinestring.h"
21 : : #include "qgswkbptr.h"
22 : :
23 : : #include <memory>
24 : :
25 : 249 : QgsTriangle::QgsTriangle()
26 : 498 : {
27 : 249 : mWkbType = QgsWkbTypes::Triangle;
28 : 249 : }
29 : :
30 : 38 : QgsTriangle::QgsTriangle( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3 )
31 : 76 : {
32 : 38 : mWkbType = QgsWkbTypes::Triangle;
33 : :
34 : 38 : QVector< double > x { p1.x(), p2.x(), p3.x(), p1.x() };
35 : 38 : QVector< double > y { p1.y(), p2.y(), p3.y(), p1.y() };
36 : 38 : QVector< double > z;
37 : 38 : if ( p1.is3D() )
38 : : {
39 : 3 : z = { p1.z(), p2.z(), p3.z(), p1.z() };
40 : 3 : }
41 : 38 : QVector< double > m;
42 : 38 : if ( p1.isMeasure() )
43 : : {
44 : 2 : m = {p1.m(), p2.m(), p3.m(), p1.m() };
45 : 2 : }
46 : 38 : setExteriorRing( new QgsLineString( x, y, z, m ) );
47 : 38 : }
48 : :
49 : 2 : QgsTriangle::QgsTriangle( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3 )
50 : 4 : {
51 : 2 : mWkbType = QgsWkbTypes::Triangle;
52 : :
53 : 2 : QVector< double > x { p1.x(), p2.x(), p3.x(), p1.x() };
54 : 2 : QVector< double > y {p1.y(), p2.y(), p3.y(), p1.y() };
55 : 2 : QgsLineString *ext = new QgsLineString( x, y );
56 : 2 : setExteriorRing( ext );
57 : 2 : }
58 : :
59 : 2 : QgsTriangle::QgsTriangle( const QPointF p1, const QPointF p2, const QPointF p3 )
60 : 4 : {
61 : 2 : mWkbType = QgsWkbTypes::Triangle;
62 : :
63 : 2 : QVector< double > x{ p1.x(), p2.x(), p3.x(), p1.x() };
64 : 2 : QVector< double > y{ p1.y(), p2.y(), p3.y(), p1.y() };
65 : 2 : QgsLineString *ext = new QgsLineString( x, y );
66 : 2 : setExteriorRing( ext );
67 : 2 : }
68 : :
69 : 20 : bool QgsTriangle::operator==( const QgsTriangle &other ) const
70 : : {
71 : 20 : if ( isEmpty() && other.isEmpty() )
72 : : {
73 : 4 : return true;
74 : : }
75 : 16 : else if ( isEmpty() || other.isEmpty() )
76 : : {
77 : 4 : return false;
78 : : }
79 : :
80 : 23 : return ( ( vertexAt( 0 ) == other.vertexAt( 0 ) ) &&
81 : 11 : ( vertexAt( 1 ) == other.vertexAt( 1 ) ) &&
82 : 11 : ( vertexAt( 2 ) == other.vertexAt( 2 ) )
83 : : );
84 : 20 : }
85 : :
86 : 5 : bool QgsTriangle::operator!=( const QgsTriangle &other ) const
87 : : {
88 : 5 : return !operator==( other );
89 : : }
90 : :
91 : 442 : QString QgsTriangle::geometryType() const
92 : : {
93 : 884 : return QStringLiteral( "Triangle" );
94 : : }
95 : :
96 : 1 : QgsTriangle *QgsTriangle::createEmptyWithSameType() const
97 : : {
98 : 1 : auto result = std::make_unique< QgsTriangle >();
99 : 1 : result->mWkbType = mWkbType;
100 : 1 : return result.release();
101 : 1 : }
102 : :
103 : 443 : void QgsTriangle::clear()
104 : : {
105 : 443 : QgsPolygon::clear();
106 : 443 : mWkbType = QgsWkbTypes::Triangle;
107 : 443 : }
108 : :
109 : 1 : QgsTriangle *QgsTriangle::clone() const
110 : : {
111 : 1 : return new QgsTriangle( *this );
112 : 0 : }
113 : :
114 : 3 : bool QgsTriangle::fromWkb( QgsConstWkbPtr &wkbPtr )
115 : : {
116 : 3 : clear();
117 : 3 : if ( !wkbPtr )
118 : : {
119 : 1 : return false;
120 : : }
121 : :
122 : 2 : QgsWkbTypes::Type type = wkbPtr.readHeader();
123 : 2 : if ( QgsWkbTypes::flatType( type ) != QgsWkbTypes::Triangle )
124 : : {
125 : 1 : return false;
126 : : }
127 : 1 : mWkbType = type;
128 : :
129 : : QgsWkbTypes::Type ringType;
130 : 1 : switch ( mWkbType )
131 : : {
132 : : case QgsWkbTypes::TriangleZ:
133 : 0 : ringType = QgsWkbTypes::LineStringZ;
134 : 0 : break;
135 : : case QgsWkbTypes::TriangleM:
136 : 0 : ringType = QgsWkbTypes::LineStringM;
137 : 0 : break;
138 : : case QgsWkbTypes::TriangleZM:
139 : 1 : ringType = QgsWkbTypes::LineStringZM;
140 : 1 : break;
141 : : default:
142 : 0 : ringType = QgsWkbTypes::LineString;
143 : 0 : break;
144 : : }
145 : :
146 : : int nRings;
147 : 1 : wkbPtr >> nRings;
148 : 1 : if ( nRings > 1 )
149 : : {
150 : 0 : return false;
151 : : }
152 : :
153 : 1 : QgsLineString *line = new QgsLineString();
154 : 1 : line->fromWkbPoints( ringType, wkbPtr );
155 : 1 : mExteriorRing.reset( line );
156 : :
157 : 1 : return true;
158 : 3 : }
159 : :
160 : 418 : bool QgsTriangle::fromWkt( const QString &wkt )
161 : : {
162 : 418 : clear();
163 : :
164 : 418 : QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
165 : :
166 : 418 : if ( QgsWkbTypes::geometryType( parts.first ) != QgsWkbTypes::PolygonGeometry )
167 : 1 : return false;
168 : :
169 : 417 : mWkbType = parts.first;
170 : :
171 : 417 : QString secondWithoutParentheses = parts.second;
172 : 417 : secondWithoutParentheses = secondWithoutParentheses.simplified().remove( ' ' );
173 : 434 : if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
174 : 17 : secondWithoutParentheses.isEmpty() )
175 : 400 : return true;
176 : :
177 : 36 : QString defaultChildWkbType = QStringLiteral( "LineString%1%2" ).arg( is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
178 : :
179 : 17 : const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defaultChildWkbType );
180 : 30 : for ( const QString &childWkt : blocks )
181 : : {
182 : 17 : QPair<QgsWkbTypes::Type, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
183 : :
184 : 17 : QgsWkbTypes::Type flatCurveType = QgsWkbTypes::flatType( childParts.first );
185 : 17 : if ( flatCurveType == QgsWkbTypes::LineString )
186 : 15 : mInteriorRings.append( new QgsLineString() );
187 : : else
188 : : {
189 : 2 : clear();
190 : 2 : return false;
191 : : }
192 : 15 : if ( !mInteriorRings.back()->fromWkt( childWkt ) )
193 : : {
194 : 2 : clear();
195 : 2 : return false;
196 : : }
197 : 17 : }
198 : :
199 : 13 : if ( mInteriorRings.isEmpty() )
200 : : {
201 : 0 : clear();
202 : 0 : return false;
203 : : }
204 : 13 : mExteriorRing.reset( mInteriorRings.takeFirst() );
205 : 19 : if ( ( mExteriorRing->numPoints() < 3 ) || ( mExteriorRing->numPoints() > 4 ) || ( mExteriorRing->numPoints() == 4 && mExteriorRing->startPoint() != mExteriorRing->endPoint() ) )
206 : : {
207 : 6 : clear();
208 : 6 : return false;
209 : : }
210 : :
211 : : //scan through rings and check if dimensionality of rings is different to CurvePolygon.
212 : : //if so, update the type dimensionality of the CurvePolygon to match
213 : 7 : bool hasZ = false;
214 : 7 : bool hasM = false;
215 : 7 : if ( mExteriorRing )
216 : : {
217 : 7 : hasZ = hasZ || mExteriorRing->is3D();
218 : 7 : hasM = hasM || mExteriorRing->isMeasure();
219 : 7 : }
220 : 7 : if ( hasZ )
221 : 1 : addZValue( 0 );
222 : 7 : if ( hasM )
223 : 1 : addMValue( 0 );
224 : :
225 : 7 : return true;
226 : 418 : }
227 : :
228 : 4 : QDomElement QgsTriangle::asGml3( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
229 : : {
230 : :
231 : 8 : QDomElement elemTriangle = doc.createElementNS( ns, QStringLiteral( "Triangle" ) );
232 : :
233 : 4 : if ( isEmpty() )
234 : 1 : return elemTriangle;
235 : :
236 : 6 : QDomElement elemExterior = doc.createElementNS( ns, QStringLiteral( "exterior" ) );
237 : 3 : QDomElement curveElem = exteriorRing()->asGml3( doc, precision, ns, axisOrder );
238 : 3 : if ( curveElem.tagName() == QLatin1String( "LineString" ) )
239 : : {
240 : 6 : curveElem.setTagName( QStringLiteral( "LinearRing" ) );
241 : 3 : }
242 : 3 : elemExterior.appendChild( curveElem );
243 : 3 : elemTriangle.appendChild( elemExterior );
244 : :
245 : 3 : return elemTriangle;
246 : 4 : }
247 : :
248 : 1 : QgsPolygon *QgsTriangle::surfaceToPolygon() const
249 : : {
250 : 1 : return toPolygon();
251 : : }
252 : :
253 : 1 : QgsCurvePolygon *QgsTriangle::toCurveType() const
254 : : {
255 : 1 : std::unique_ptr<QgsCurvePolygon> curvePolygon( new QgsCurvePolygon() );
256 : 1 : curvePolygon->setExteriorRing( mExteriorRing->clone() );
257 : :
258 : 1 : return curvePolygon.release();
259 : 1 : }
260 : :
261 : 1 : void QgsTriangle::addInteriorRing( QgsCurve *ring )
262 : : {
263 : 1 : delete ring;
264 : 1 : }
265 : :
266 : 1 : bool QgsTriangle::deleteVertex( QgsVertexId position )
267 : : {
268 : : Q_UNUSED( position )
269 : 1 : return false;
270 : : }
271 : :
272 : 1 : bool QgsTriangle::insertVertex( QgsVertexId position, const QgsPoint &vertex )
273 : : {
274 : : Q_UNUSED( position )
275 : 1 : Q_UNUSED( vertex )
276 : 1 : return false;
277 : : }
278 : :
279 : 15 : bool QgsTriangle::moveVertex( QgsVertexId vId, const QgsPoint &newPos )
280 : : {
281 : 15 : if ( isEmpty() )
282 : 0 : return false;
283 : :
284 : 15 : if ( !mExteriorRing || vId.part != 0 || vId.ring != 0 || vId.vertex < 0 || vId.vertex > 4 )
285 : : {
286 : 6 : return false;
287 : : }
288 : :
289 : 9 : if ( vId.vertex == 4 )
290 : : {
291 : 2 : vId.vertex = 0;
292 : 2 : }
293 : :
294 : 9 : int n = mExteriorRing->numPoints();
295 : 9 : bool success = mExteriorRing->moveVertex( vId, newPos );
296 : 9 : if ( success )
297 : : {
298 : : // If first or last vertex is moved, also move the last/first vertex
299 : 9 : if ( vId.vertex == 0 )
300 : 4 : mExteriorRing->moveVertex( QgsVertexId( vId.part, vId.ring, n - 1 ), newPos );
301 : 9 : clearCache();
302 : 9 : }
303 : 9 : return success;
304 : 15 : }
305 : :
306 : 55 : void QgsTriangle::setExteriorRing( QgsCurve *ring )
307 : : {
308 : 55 : if ( !ring )
309 : : {
310 : 1 : return;
311 : : }
312 : :
313 : 54 : if ( ring->hasCurvedSegments() )
314 : : {
315 : : //need to segmentize ring as polygon does not support curves
316 : 1 : QgsCurve *line = ring->segmentize();
317 : 1 : delete ring;
318 : 1 : ring = line;
319 : 1 : }
320 : :
321 : 54 : if ( ( ring->numPoints() > 4 ) || ( ring->numPoints() < 3 ) )
322 : : {
323 : 3 : delete ring;
324 : 3 : return;
325 : : }
326 : 51 : else if ( ring->numPoints() == 4 )
327 : : {
328 : 48 : if ( !ring->isClosed() )
329 : : {
330 : 1 : delete ring;
331 : 1 : return;
332 : : }
333 : 47 : }
334 : 3 : else if ( ring->numPoints() == 3 )
335 : : {
336 : 3 : if ( ring->isClosed() )
337 : : {
338 : 1 : delete ring;
339 : 1 : return;
340 : : }
341 : 2 : QgsLineString *lineString = static_cast< QgsLineString *>( ring );
342 : 2 : if ( !lineString->isClosed() )
343 : : {
344 : 2 : lineString->close();
345 : 2 : }
346 : 2 : ring = lineString;
347 : 2 : }
348 : :
349 : 49 : mExteriorRing.reset( ring );
350 : :
351 : : //set proper wkb type
352 : 49 : setZMTypeFromSubGeometry( ring, QgsWkbTypes::Triangle );
353 : :
354 : 49 : clearCache();
355 : 55 : }
356 : :
357 : 2 : QgsCurve *QgsTriangle::boundary() const
358 : : {
359 : 2 : if ( !mExteriorRing )
360 : 1 : return nullptr;
361 : :
362 : 1 : return mExteriorRing->clone();
363 : 2 : }
364 : :
365 : 710 : QgsPoint QgsTriangle::vertexAt( int atVertex ) const
366 : : {
367 : 710 : if ( isEmpty() )
368 : 0 : return QgsPoint();
369 : :
370 : 710 : QgsVertexId id( 0, 0, atVertex );
371 : 710 : return mExteriorRing->vertexAt( id );
372 : 710 : }
373 : :
374 : 32 : QVector<double> QgsTriangle::lengths() const
375 : : {
376 : 32 : QVector<double> lengths;
377 : 32 : if ( isEmpty() )
378 : 1 : return lengths;
379 : :
380 : 31 : lengths.append( vertexAt( 0 ).distance( vertexAt( 1 ) ) );
381 : 31 : lengths.append( vertexAt( 1 ).distance( vertexAt( 2 ) ) );
382 : 31 : lengths.append( vertexAt( 2 ).distance( vertexAt( 0 ) ) );
383 : :
384 : 31 : return lengths;
385 : 32 : }
386 : :
387 : 5 : QVector<double> QgsTriangle::angles() const
388 : : {
389 : 5 : QVector<double> angles;
390 : 5 : if ( isEmpty() )
391 : 1 : return angles;
392 : : double ax, ay, bx, by, cx, cy;
393 : :
394 : 4 : ax = vertexAt( 0 ).x();
395 : 4 : ay = vertexAt( 0 ).y();
396 : 4 : bx = vertexAt( 1 ).x();
397 : 4 : by = vertexAt( 1 ).y();
398 : 4 : cx = vertexAt( 2 ).x();
399 : 4 : cy = vertexAt( 2 ).y();
400 : :
401 : 4 : double a1 = std::fmod( QgsGeometryUtils::angleBetweenThreePoints( cx, cy, ax, ay, bx, by ), M_PI );
402 : 4 : double a2 = std::fmod( QgsGeometryUtils::angleBetweenThreePoints( ax, ay, bx, by, cx, cy ), M_PI );
403 : 4 : double a3 = std::fmod( QgsGeometryUtils::angleBetweenThreePoints( bx, by, cx, cy, ax, ay ), M_PI );
404 : :
405 : 4 : angles.append( ( a1 > M_PI_2 ? a1 - M_PI_2 : a1 ) );
406 : 4 : angles.append( ( a2 > M_PI_2 ? a2 - M_PI_2 : a2 ) );
407 : 4 : angles.append( ( a3 > M_PI_2 ? a3 - M_PI_2 : a3 ) );
408 : :
409 : 4 : return angles;
410 : 5 : }
411 : :
412 : 4 : bool QgsTriangle::isDegenerate()
413 : : {
414 : 4 : if ( isEmpty() )
415 : 1 : return true;
416 : :
417 : 3 : QgsPoint p1( vertexAt( 0 ) );
418 : 3 : QgsPoint p2( vertexAt( 1 ) );
419 : 3 : QgsPoint p3( vertexAt( 2 ) );
420 : 3 : return ( ( ( p1 == p2 ) || ( p1 == p3 ) || ( p2 == p3 ) ) || QgsGeometryUtils::leftOfLine( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() ) == 0 );
421 : 4 : }
422 : :
423 : 7 : bool QgsTriangle::isIsocele( double lengthTolerance ) const
424 : : {
425 : 7 : if ( isEmpty() )
426 : 1 : return false;
427 : 6 : QVector<double> sides = lengths();
428 : 6 : bool ab_bc = qgsDoubleNear( sides.at( 0 ), sides.at( 1 ), lengthTolerance );
429 : 6 : bool bc_ca = qgsDoubleNear( sides.at( 1 ), sides.at( 2 ), lengthTolerance );
430 : 6 : bool ca_ab = qgsDoubleNear( sides.at( 2 ), sides.at( 0 ), lengthTolerance );
431 : :
432 : 6 : return ( ab_bc || bc_ca || ca_ab );
433 : 7 : }
434 : :
435 : 4 : bool QgsTriangle::isEquilateral( double lengthTolerance ) const
436 : : {
437 : 4 : if ( isEmpty() )
438 : 1 : return false;
439 : 3 : QVector<double> sides = lengths();
440 : 3 : bool ab_bc = qgsDoubleNear( sides.at( 0 ), sides.at( 1 ), lengthTolerance );
441 : 3 : bool bc_ca = qgsDoubleNear( sides.at( 1 ), sides.at( 2 ), lengthTolerance );
442 : 3 : bool ca_ab = qgsDoubleNear( sides.at( 2 ), sides.at( 0 ), lengthTolerance );
443 : :
444 : 3 : return ( ab_bc && bc_ca && ca_ab );
445 : 4 : }
446 : :
447 : 4 : bool QgsTriangle::isRight( double angleTolerance ) const
448 : : {
449 : 4 : if ( isEmpty() )
450 : 1 : return false;
451 : 3 : QVector<double> a = angles();
452 : 3 : QVector<double>::iterator ita = a.begin();
453 : 9 : while ( ita != a.end() )
454 : : {
455 : 8 : if ( qgsDoubleNear( *ita, M_PI_2, angleTolerance ) )
456 : 2 : return true;
457 : 6 : ++ita;
458 : : }
459 : 1 : return false;
460 : 4 : }
461 : :
462 : 4 : bool QgsTriangle::isScalene( double lengthTolerance ) const
463 : : {
464 : 4 : if ( isEmpty() )
465 : 1 : return false;
466 : 3 : return !isIsocele( lengthTolerance );
467 : 4 : }
468 : :
469 : 9 : QVector<QgsLineString> QgsTriangle::altitudes() const
470 : : {
471 : 9 : QVector<QgsLineString> alt;
472 : 9 : if ( isEmpty() )
473 : 1 : return alt;
474 : :
475 : 8 : alt.append( QgsGeometryUtils::perpendicularSegment( vertexAt( 0 ), vertexAt( 2 ), vertexAt( 1 ) ) );
476 : 8 : alt.append( QgsGeometryUtils::perpendicularSegment( vertexAt( 1 ), vertexAt( 0 ), vertexAt( 2 ) ) );
477 : 8 : alt.append( QgsGeometryUtils::perpendicularSegment( vertexAt( 2 ), vertexAt( 0 ), vertexAt( 1 ) ) );
478 : :
479 : 8 : return alt;
480 : 9 : }
481 : :
482 : 4 : QVector<QgsLineString> QgsTriangle::medians() const
483 : : {
484 : 4 : QVector<QgsLineString> med;
485 : 4 : if ( isEmpty() )
486 : 1 : return med;
487 : :
488 : 3 : QgsLineString med1;
489 : 3 : QgsLineString med2;
490 : 3 : QgsLineString med3;
491 : 3 : med1.setPoints( QgsPointSequence() << vertexAt( 0 ) << QgsGeometryUtils::midpoint( vertexAt( 1 ), vertexAt( 2 ) ) );
492 : 3 : med2.setPoints( QgsPointSequence() << vertexAt( 1 ) << QgsGeometryUtils::midpoint( vertexAt( 0 ), vertexAt( 2 ) ) );
493 : 3 : med3.setPoints( QgsPointSequence() << vertexAt( 2 ) << QgsGeometryUtils::midpoint( vertexAt( 0 ), vertexAt( 1 ) ) );
494 : 3 : med.append( med1 );
495 : 3 : med.append( med2 );
496 : 3 : med.append( med3 );
497 : :
498 : 3 : return med;
499 : 4 : }
500 : :
501 : 2 : QVector<QgsLineString> QgsTriangle::bisectors( double lengthTolerance ) const
502 : : {
503 : 2 : QVector<QgsLineString> bis;
504 : 2 : if ( isEmpty() )
505 : 1 : return bis;
506 : :
507 : 1 : QgsLineString bis1;
508 : 1 : QgsLineString bis2;
509 : 1 : QgsLineString bis3;
510 : 1 : QgsPoint incenter = inscribedCenter();
511 : 1 : QgsPoint out;
512 : 1 : bool intersection = false;
513 : :
514 : 1 : QgsGeometryUtils::segmentIntersection( vertexAt( 0 ), incenter, vertexAt( 1 ), vertexAt( 2 ), out, intersection, lengthTolerance );
515 : 1 : bis1.setPoints( QgsPointSequence() << vertexAt( 0 ) << out );
516 : :
517 : 1 : QgsGeometryUtils::segmentIntersection( vertexAt( 1 ), incenter, vertexAt( 0 ), vertexAt( 2 ), out, intersection, lengthTolerance );
518 : 1 : bis2.setPoints( QgsPointSequence() << vertexAt( 1 ) << out );
519 : :
520 : 1 : QgsGeometryUtils::segmentIntersection( vertexAt( 2 ), incenter, vertexAt( 0 ), vertexAt( 1 ), out, intersection, lengthTolerance );
521 : 1 : bis3.setPoints( QgsPointSequence() << vertexAt( 2 ) << out );
522 : :
523 : 1 : bis.append( bis1 );
524 : 1 : bis.append( bis2 );
525 : 1 : bis.append( bis3 );
526 : :
527 : 1 : return bis;
528 : 2 : }
529 : :
530 : 3 : QgsTriangle QgsTriangle::medial() const
531 : : {
532 : 3 : if ( isEmpty() )
533 : 1 : return QgsTriangle();
534 : 2 : QgsPoint p1, p2, p3;
535 : 2 : p1 = QgsGeometryUtils::midpoint( vertexAt( 0 ), vertexAt( 1 ) );
536 : 2 : p2 = QgsGeometryUtils::midpoint( vertexAt( 1 ), vertexAt( 2 ) );
537 : 2 : p3 = QgsGeometryUtils::midpoint( vertexAt( 2 ), vertexAt( 0 ) );
538 : 2 : return QgsTriangle( p1, p2, p3 );
539 : 3 : }
540 : :
541 : 7 : QgsPoint QgsTriangle::orthocenter( double lengthTolerance ) const
542 : : {
543 : 7 : if ( isEmpty() )
544 : 1 : return QgsPoint();
545 : 6 : QVector<QgsLineString> alt = altitudes();
546 : 6 : QgsPoint ortho;
547 : : bool intersection;
548 : 6 : QgsGeometryUtils::segmentIntersection( alt.at( 0 ).pointN( 0 ), alt.at( 0 ).pointN( 1 ), alt.at( 1 ).pointN( 0 ), alt.at( 1 ).pointN( 1 ), ortho, intersection, lengthTolerance );
549 : :
550 : 6 : return ortho;
551 : 7 : }
552 : :
553 : 14 : QgsPoint QgsTriangle::circumscribedCenter() const
554 : : {
555 : 14 : if ( isEmpty() )
556 : 1 : return QgsPoint();
557 : : double r, x, y;
558 : 13 : QgsGeometryUtils::circleCenterRadius( vertexAt( 0 ), vertexAt( 1 ), vertexAt( 2 ), r, x, y );
559 : 13 : return QgsPoint( x, y );
560 : 14 : }
561 : :
562 : 14 : double QgsTriangle::circumscribedRadius() const
563 : : {
564 : 14 : if ( isEmpty() )
565 : 1 : return 0.0;
566 : : double r, x, y;
567 : 13 : QgsGeometryUtils::circleCenterRadius( vertexAt( 0 ), vertexAt( 1 ), vertexAt( 2 ), r, x, y );
568 : 13 : return r;
569 : 14 : }
570 : :
571 : 7 : QgsCircle QgsTriangle::circumscribedCircle() const
572 : : {
573 : 7 : if ( isEmpty() )
574 : 0 : return QgsCircle();
575 : 7 : return QgsCircle( circumscribedCenter(), circumscribedRadius() );
576 : 7 : }
577 : :
578 : 22 : QgsPoint QgsTriangle::inscribedCenter() const
579 : : {
580 : 22 : if ( isEmpty() )
581 : 1 : return QgsPoint();
582 : :
583 : 21 : QVector<double> l = lengths();
584 : 63 : double x = ( l.at( 0 ) * vertexAt( 2 ).x() +
585 : 42 : l.at( 1 ) * vertexAt( 0 ).x() +
586 : 21 : l.at( 2 ) * vertexAt( 1 ).x() ) / perimeter();
587 : 63 : double y = ( l.at( 0 ) * vertexAt( 2 ).y() +
588 : 42 : l.at( 1 ) * vertexAt( 0 ).y() +
589 : 21 : l.at( 2 ) * vertexAt( 1 ).y() ) / perimeter();
590 : :
591 : 21 : QgsPoint center( x, y );
592 : :
593 : 21 : QgsPointSequence points;
594 : 21 : points << vertexAt( 0 ) << vertexAt( 1 ) << vertexAt( 2 );
595 : 21 : QgsGeometryUtils::setZValueFromPoints( points, center );
596 : :
597 : 21 : return center;
598 : 22 : }
599 : :
600 : 15 : double QgsTriangle::inscribedRadius() const
601 : : {
602 : 15 : if ( isEmpty() )
603 : 1 : return 0.0;
604 : 14 : return ( 2.0 * area() / perimeter() );
605 : 15 : }
606 : :
607 : 8 : QgsCircle QgsTriangle::inscribedCircle() const
608 : : {
609 : 8 : if ( isEmpty() )
610 : 0 : return QgsCircle();
611 : 8 : return QgsCircle( inscribedCenter(), inscribedRadius() );
612 : 8 : }
|