Library of Assembled Shared Sources
triangle_2d.inl
Go to the documentation of this file.
1/** @file
2 * @author Bram de Greve (bram@cocamware.com)
3 * @author Tom De Muer (tom@cocamware.com)
4 *
5 * *** BEGIN LICENSE INFORMATION ***
6 *
7 * The contents of this file are subject to the Common Public Attribution License
8 * Version 1.0 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://lass.sourceforge.net/cpal-license. The License is based on the
11 * Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover
12 * use of software over a computer network and provide for limited attribution for
13 * the Original Developer. In addition, Exhibit A has been modified to be consistent
14 * with Exhibit B.
15 *
16 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
17 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
18 * language governing rights and limitations under the License.
19 *
20 * The Original Code is LASS - Library of Assembled Shared Sources.
21 *
22 * The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
23 * The Original Developer is the Initial Developer.
24 *
25 * All portions of the code written by the Initial Developer are:
26 * Copyright (C) 2004-2024 the Initial Developer.
27 * All Rights Reserved.
28 *
29 * Contributor(s):
30 *
31 * Alternatively, the contents of this file may be used under the terms of the
32 * GNU General Public License Version 2 or later (the GPL), in which case the
33 * provisions of GPL are applicable instead of those above. If you wish to allow use
34 * of your version of this file only under the terms of the GPL and not to allow
35 * others to use your version of this file under the CPAL, indicate your decision by
36 * deleting the provisions above and replace them with the notice and other
37 * provisions required by the GPL License. If you do not delete the provisions above,
38 * a recipient may use your version of this file under either the CPAL or the GPL.
39 *
40 * *** END LICENSE INFORMATION ***
41 */
42
43
44
45#ifndef LASS_GUARDIAN_OF_INCLUSION_PRIM_TRIANGLE_2D_INL
46#define LASS_GUARDIAN_OF_INCLUSION_PRIM_TRIANGLE_2D_INL
47
48#include "prim_common.h"
49#include "triangle_2d.h"
50#include "line_2d.h"
51
52namespace lass
53{
54namespace prim
55{
56
57/** constructs an empty triangle.
58 * all vertices are (0, 0) and thus equal.
59 */
60template <typename T>
64
65
66
67/** Constructs a triangle through three points in positive sequence.
68 */
69template <typename T>
70Triangle2D<T>::Triangle2D(const TPoint& a, const TPoint& b, const TPoint& c)
71{
72 vertices_[0] = a;
73 vertices_[1] = b;
74 vertices_[2] = c;
75}
76
77
78
79/** @copydoc SimplePolygon2D::operator[]
80 */
81template <typename T>
82const typename Triangle2D<T>::TPoint& Triangle2D<T>::operator[](size_t vertexIndex) const
83{
84 LASS_ASSERT(isInRange(vertexIndex));
85 return vertices_[vertexIndex];
86}
87
90/** @copydoc SimplePolygon2D::operator[]
91 */
92template <typename T>
93typename Triangle2D<T>::TPoint& Triangle2D<T>::operator[](size_t vertexIndex)
95 LASS_ASSERT(isInRange(vertexIndex));
96 return vertices_[vertexIndex];
97}
100
101/** @copydoc SimplePolygon2D::at
102 */
103template <typename T>
104const typename Triangle2D<T>::TPoint& Triangle2D<T>::at(int vertexIndex) const
106 const size_t i = num::mod(vertexIndex, static_cast<unsigned>(size_));
107 LASS_ASSERT(isInRange(i));
108 return vertices_[i];
109}
111
113/** @copydoc SimplePolygon2D::at
114 */
115template <typename T>
116typename Triangle2D<T>::TPoint& Triangle2D<T>::at(int vertexIndex)
118 const size_t i = num::mod(vertexIndex, static_cast<unsigned>(size_));
119 LASS_ASSERT(isInRange(i));
120 return vertices_[i];
121}
122
123
124
125/** @copydoc SimplePolygon2D::edge
126 */
127template <typename T>
128const typename Triangle2D<T>::TLineSegment Triangle2D<T>::edge(int tailVertexIndex) const
129{
130 return TLineSegment(at(tailVertexIndex), at(tailVertexIndex + 1));
131}
132
133
134
135/** @copydoc SimplePolygon2D::vector
136 */
137template <typename T>
138const typename Triangle2D<T>::TVector Triangle2D<T>::vector(int tailVertexIndex) const
139{
140 return at(tailVertexIndex + 1) - at(tailVertexIndex);
141}
142
143
144
145/** @copydoc SimplePolygon2D::isEmpty
146 * @par Triangle specific:
147 * if all vertices are equal, we assume the triangle is empty
148 */
149template <typename T>
151{
152 return vertices_[0] == vertices_[1] && vertices_[0] == vertices_[2];
153}
154
155
156
157/** @copydoc SimplePolygon2D::size
158 */
159template <typename T>
161{
162 return size_;
163}
164
165
166
167/** @copydoc SimplePolygon2D::signedArea
168 */
169template <typename T>
170const typename Triangle2D<T>::TValue Triangle2D<T>::signedArea() const
171{
172 static_assert(size_ == 3);
173 return perpDot(vertices_[1] - vertices_[0], vertices_[2] - vertices_[0]) / T(2);
174}
175
176
177
178/** @copydoc SimplePolygon2D::area
179 */
180template <typename T>
181const typename Triangle2D<T>::TValue Triangle2D<T>::area() const
182{
183 return num::abs(signedArea());
184}
185
186
187
188/** @copydoc SimplePolygon2D::perimeter
189 */
190template <typename T>
191const typename Triangle2D<T>::TValue Triangle2D<T>::perimeter() const
192{
193 return distance(vertices_[0], vertices_[1]) +
194 distance(vertices_[1], vertices_[2]) +
195 distance(vertices_[2], vertices_[0]);
196}
197
198
199
200/** @copydoc SimplePolygon2D::vertexCentroid
201 */
202template <typename T>
203const typename Triangle2D<T>::TPointH
205{
206 TPointH result = vertices_[0] + vertices_[1] + vertices_[2];
207 return result;
208}
209
210
211
212/** @copydoc SimplePolygon2D::vertexCentroid
213 */
214template <typename T> inline
215const typename Triangle2D<T>::TPointH
217{
218 return vertexCentroid();
219}
220
221
222
223/** @copydoc SimplePolygon2D::isSimple
224 * @par Triangle specific:
225 * A triangle always is simple
226 */
227template <typename T>
229{
230 return true;
231}
232
233
234
235/** @copydoc SimplePolygon2D::isConvex
236 * @par Triangle specific:
237 * A triangle always is convex
238 */
239template <typename T>
241{
242 return true;
243}
244
245
246
247/** @copydoc SimplePolygon2D::orientation
248 */
249template <typename T>
251{
252 const TValue area = signedArea();
253 if (area > TNumTraits::zero)
254 {
255 return oCounterClockWise;
256 }
257 else if (area < TNumTraits::zero)
258 {
259 return oClockWise;
260 }
261 else
262 {
263 return oInvalid;
264 }
265}
266
267
268
269/** @copydoc SimplePolygon2D::isReflex
270 * @par Triangle specific:
271 * triangles never have reflex vertices, so always returns false.
272 */
273template <typename T>
275{
276 return false;
277}
278
279
280
281/** return true when a point is inside or on the edge of a triangle.
282 */
283template <typename T>
284bool Triangle2D<T>::contains(const TPoint& point) const
285{
286 return
287 perpDot(vertices_[1] - vertices_[0], point - vertices_[0]) >= TNumTraits::zero &&
288 perpDot(vertices_[2] - vertices_[1], point - vertices_[1]) >= TNumTraits::zero &&
289 perpDot(vertices_[0] - vertices_[2], point - vertices_[2]) >= TNumTraits::zero;
290}
291
292
293
294template <typename T>
295Side Triangle2D<T>::classify(const TPoint& point) const
296{
297 return contains(point) ? sInside : sOutside;
298}
299
300
301
302/** flip orientation of polygon.
303 */
304template <typename T>
306{
307 std::swap(vertices_[0], vertices_[2]);
308}
309
310
311
312// --- private -------------------------------------------------------------------------------------
313
314/** return if index of vertex is in range of the std::vector
315 */
316template <typename T>
317bool Triangle2D<T>::isInRange(size_t vertexIndex) const
318{
319 return vertexIndex < size_;
320}
321
322
323
324// --- free ----------------------------------------------------------------------------------------
325
326/** @relates lass::prim::Triangle2D
327 */
328template <typename T>
329const T squaredDistance(const Triangle2D<T>& triangle, const Point2D<T>& point)
330{
331 typedef typename Triangle2D<T>::TPoint TPoint;
332 typedef typename Triangle2D<T>::TVector TVector;
333 typedef typename Triangle2D<T>::TValue TValue;
334 typedef typename Triangle2D<T>::TNumTraits TNumTraits;
335
336 TValue sqrBest = TNumTraits::infinity;
337 for (size_t k1 = 0, k0 = 2; k1 < 3; k0 = k1++)
338 {
339 const TPoint& tail = triangle[k0];
340 const TPoint& head = triangle[k1];
341 const TVector vector = point - tail;
342 const TVector edge = head - tail;
343 const TValue t = dot(vector, edge);
344 const TValue tMax = dot(edge, edge);
345 if (t > 0 && t < tMax)
346 {
347 const TVector rejected = vector - edge * (t / tMax);
348 sqrBest = std::min(sqrBest, rejected.squaredNorm());
349 }
350 sqrBest = std::min(sqrBest, vector.squaredNorm());
351 }
352
353 return sqrBest;
354}
355
356
357
358/** @relates lass::prim::Triangle2D
359 */
360template <typename T>
361const T distance(const Triangle2D<T>& triangle, const Point2D<T>& point)
362{
363 return num::sqrt(squaredDistance(triangle, point));
364}
365
366
367namespace impl
368{
369 template <typename T>
370 bool hasSeperatingAxis(const Triangle2D<T>& a, const Triangle2D<T>& b)
371 {
372 // applying seperating axis theorem, normal to each edge.
373 typedef typename Triangle2D<T>::TVector TVector;
374 typedef typename Triangle2D<T>::TPoint TPoint;
375
376 size_t j = 1, k = 2;
377 for (size_t i = 0; i < 3; ++i)
378 {
379 const TPoint& aTail = a[i];
380 const TPoint& aHead = a[j];
381 const TVector aEdge = aHead - aTail;
382
383 const T b0 = perpDot(aEdge, b[0] - aTail);
384 const T b1 = perpDot(aEdge, b[1] - aTail);
385 const T b2 = perpDot(aEdge, b[2] - aTail);
386 const T aRef = perpDot(aEdge, a[k] - aTail);
387
388 if (aRef > 0)
389 {
390 if ((b0 < 0 && b1 < 0 && b2 < 0) || (b0 > aRef && b1 > aRef && b2 > aRef))
391 {
392 return true;
393 }
394 }
395 else
396 {
397 if ((b0 > 0 && b1 > 0 && b2 > 0) || (b0 < aRef && b1 < aRef && b2 < aRef))
398 {
399 return true;
400 }
401 }
402
403 j = k;
404 k = i;
405 }
406
407 return false;
408 }
409}
410
411/** @relates lass::prim::Triangle2D
412 */
413template <typename T>
414bool intersects(const Triangle2D<T>& a, const Triangle2D<T>& b)
415{
416 return !(impl::hasSeperatingAxis(a, b) || impl::hasSeperatingAxis(b, a));
417}
418
419
420
421/** @relates lass::prim::Triangle2D
422 */
423template <typename T>
424io::XmlOStream& operator<<(io::XmlOStream& ioOStream, const Triangle2D<T>& iTriangle)
425{
426 LASS_ENFORCE_STREAM(ioOStream) << "<Triangle2D>\n";
427 for (unsigned i = 0; i < 3; ++i)
428 {
429 ioOStream << "<vertex id='" << i << "'>" << iTriangle[i] << "</vertex>\n";
430 }
431 ioOStream << "</Triangle2D>\n";
432 return ioOStream;
433}
434
435
436
437/** @relates lass::prim::Triangle2D
438 */
439template <typename T>
440std::ostream& operator<<(std::ostream& ioOStream, const Triangle2D<T>& iTriangle)
441{
442 LASS_ENFORCE_STREAM(ioOStream)
443 << "{" << iTriangle[0] << ", " << iTriangle[1] << ", " << iTriangle[2] << "}";
444 return ioOStream;
445}
446
447/** @relates lass::prim::Triangle2D
448 */
449template<typename T>
450lass::io::MatlabOStream& operator<<(lass::io::MatlabOStream& oOStream,
451 const Triangle2D<T>& iTriangle)
452{
453 LASS_ENFORCE_STREAM(oOStream) << "lasthandle = patch(";
454 oOStream << "[" << iTriangle[0].x << "," << iTriangle[1].x << "," << iTriangle[2].x << "],";
455 oOStream << "[" << iTriangle[0].y << "," << iTriangle[1].y << "," << iTriangle[2].y << "],";
456 oOStream << oOStream.color() << ");\n";
457 return oOStream;
458}
459
460
461/** @relates lass::prim::Triangle2D
462* Returns the surface of the partial Voronoi cell constructed around vertex
463* vertexIndex (say vertex a in triangle abc). Then the surface is determined by
464* the quad built by a, the two midpoints on ab and ac and the intersection of the two
465* perpendicular bisectors.
466*/
467template <typename T>
468T partialVoronoiArea(const Triangle2D<T> iT, int vertexIndex)
469{
470 // compute the two midpoints
471 typedef typename Triangle2D<T>::TPoint TPoint;
472 typedef typename Triangle2D<T>::TPointH TPointH;
473 typedef Line2D<T> TLine;
474 TPoint a = iT.at(vertexIndex);
475 TPoint b = iT.at(vertexIndex+1);
476 TPoint c = iT.at(vertexIndex-1);
477
478 TPointH abh = a+b;
479 TPointH ach = a+c;
480 TPoint ab = abh.affine();
481 TPoint ac = ach.affine();
482 TLine pbisAb(ab, (b-a).perp());
483 TLine pbisAc(ac, (c-a).perp());
484
485 TPoint m;
486 LASS_ENFORCE(rOne == intersect(pbisAb, pbisAc,m));
487
488 Triangle2D<T> aAbm(a,ab,m);
489 Triangle2D<T> amAc(a,m,ac);
490
491 return aAbm.area()+amAc.area();
492}
493
494}
495
496}
497
498#endif
499
500// EOF
A very simple 2D polygon :)
Definition triangle_2d.h:65
bool isSimple() const
return true if polygon is simple, false if not.
int size() const
return number of vertices
const TPoint & operator[](size_t iIndexOfVertex) const
return vertex of polygon by its index, not wrapped, no bounds check.
const TVector vector(int iIndexOfTailVertex) const
return the vector between vertices at(iIndex) and at(iIndex + 1)\
bool contains(const TPoint &iP) const
return true when a point is inside or on the edge of a triangle.
const TPointH vertexCentroid() const
return the barycenter of all vertices.
bool isReflex(int iIndexOfVertex) const
return true if inner angle of vertex is reflex (is > 180 degrees).
Orientation orientation() const
return orientation of polygon.
const TValue perimeter() const
return sum of the lengths of all edges
bool isEmpty() const
return true if polygon has no vertices
const TPoint & at(int iIndexOfVertex) const
return vertex of polygon by its index, but wrap around the bounds.
void flip()
flip orientation of polygon.
bool isConvex() const
return true if polygon is convex, false if not.
Result intersect(const Triangle2D< U > &triangle, const Ray2D< U, NP, PP > &ray, U &t, const U &tMin=U())
T partialVoronoiArea(const Triangle2D< T > iT, int vertexIndex)
Returns the surface of the partial Voronoi cell constructed around vertex vertexIndex (say vertex a i...
const TValue signedArea() const
return signed polygon area.
const TLineSegment edge(int iIndexOfTailVertex) const
return the edge of the polygon between vertices at(iIndex) and at(iIndex + 1).
Triangle2D()
constructs an empty triangle.
const TValue area() const
return area of the polygons surface.
const TPointH surfaceCentroid() const
return the barycenter of all vertices.
T abs(const T &x)
if x < 0 return -x, else return x.
Definition basic_ops.h:145
implementation details of lass::prim
set of geometrical primitives
Definition aabb_2d.h:81
Side
Different sides of a surface.
Definition side.h:79
@ sOutside
outside the surface
Definition side.h:86
@ sInside
inside the surface
Definition side.h:85
Orientation
enumeration of clockwise versus counterclockwise
Definition orientation.h:58
@ oClockWise
clockwise orientation
Definition orientation.h:60
@ oInvalid
invalid state
Definition orientation.h:59
@ oCounterClockWise
counterclockwise orientation
Definition orientation.h:61
@ rOne
there's exactly one answer, 1 output argument contains the answer
Definition result.h:77
Library for Assembled Shared Sources.
Definition config.h:53
const Point2D< T > affine() const
Return rescaled version of point with weight = 1.