Implementation of linear minimum area enclosing triangle algorithm
Abstract
An algorithm which computes the minimum area triangle enclosing a convex polygon in linear time already exists in the literature. The paper describing the algorithm also proves that the provided solution is optimal and a lower complexity sequential algorithm cannot exist. However, only a highlevel description of the algorithm was provided, making the implementation difficult to reproduce. The present note aims to contribute to the field by providing a detailed description of the algorithm which is easy to implement and reproduce, and a benchmark comprising 10,000 variable sized, randomly generated convex polygons for illustrating the linearity of the algorithm.
Keywords
Minimum area triangle Benchmark Convex polygon Rotating caliper Computational geometryMathematics Subject Classification
68Q25 68U051 Introduction
The algorithm described in Klee and Laskowski (1985) finds all minimum area triangles enclosing a given convex polygon in \(O(n\ {\log }^{2}(n))\). Inspired by Toussaint’s rotating callipers procedure (Toussaint 1983) O’Rourke improved the algorithm reducing its complexity to \({\Theta }(n)\) (O’Rourke et al. 1986). Similarly, the authors of Chandran and Mount (1992) describe a method of parallelising Klee and Laskowski’s algorithm such that the minimum area enclosing triangle can be found in \(O({\log }\ {\log } (n))\) using \((n\ /\ {\log }\ {\log }(n))\) processors.

A detailed and reproducible algorithm for computing the minimal area enclosing triangles (Sects. 2 and 4);

A step by step description of an execution of the algorithm implementation, including a screencast (Sect. 3);

A benchmark of 10,000 randomly generated convex polygons for assessing the efficiency of the algorithm (Sect. 5);

Publicly available C++ implementation of the algorithm released both as a standalone software project and as a module of the Computer Vision library OpenCV (Bradski and Kaehler 2008) (Sects. 6 and 7).
2 Main algorithm
Theorems and lemmas underlying the main algorithm can be found in Klee and Laskowski (1985), O’Rourke et al. (1986) and will not be repeated here.

\(a\), \(b\), \(c\)—indices pointing to the polygon vertices in a clockwise order;

\(A\), \(B\), \(C\)—the sides of the current enclosing triangle;

\(vertexA\), \(vertexB\), \(vertexC\)—the vertices of the current enclosing triangle;

\(p + 1\)—\(polygon\) vertex succeeding \(p\) considering a clockwise order;

\(p  1\)—\(polygon\) vertex preceding \(p\) considering a clockwise order;

\(h\)(\(p\))—distance of \(p\) from line determined by side C;

\(validationFlag\)—used to record what validation conditions should be used.

The midpoints of the enclosing triangle’s sides must touch the polygon.

There exists a local minimum enclosing triangle with at least two sides flush with edges of the polygon. The third side of the triangle can be either flush with an edge or tangent to the polygon.
The main algorithm is described in O’Rourke et al. (1986) using abstract, highlevel descriptions. In contrast, we will describe both the main algorithm and all required subalgorithms in great depth and in an intuitive manner.
First of all, the Main Algorithm 1 contains a loop which iterates over each edge of the convex polygon and sets the side \(C\) of the triangle flush with the selected edge. A necessary condition for finding a minimum enclosing triangle is that \(b\) is on the right chain and \(a\) on the left. The first step inside the loop is therefore to move the index \(b\) on the right chain using the AdvanceBToRightChain() subalgorithm. The initialisation of \(a\) was made in such a manner that it is on the left chain already.
The next condition which must be fulfilled is that \(a\) and \(b\) must be critical or high. The MoveAIfLowAndBIfHigh() subalgorithm advances \(a\) and \(b\) until this condition is fulfilled.
Next \(b\) will be advanced until [\(gamma(a)\) \(b\)] is tangent to the convex polygon via the SearchForBTangency() subalgorithm.
Afterwards the subalgorithm UpdateSidesCA() computes the vertices defining sides \(A\) and \(C\) of the enclosing triangle.
If the tangency was not reached in the previous step (see IsNotBTangency()) then sides \(A\) and \(B\) are updated (see UpdateSidesBA()). Otherwise only side \(B\) needs to be updated (see UpdateSideB()).
The main algorithm and subalgorithms/functions AdvanceBToRightChain(), MoveAIfLowAndBIfHigh(), SearchForBTangency(), IsNotBTangency(), UpdateSidesBA(), Update SideB() and UpdateMinimumAreaEnclosingTriangle() are partially described in O’Rourke et al. (1986) as well. However, they are merged into a single algorithm, some steps are not explicitly described and all remaining subalgorithms/functions are not given. This note complements O’Rourke et al. (1986) by explicitly describing all subalgorithms required to implement the main algorithm.
3 Simple usage example

\(P(300, 700)\);

\(Q(400, 480)\);

\(R(643, 200)\);

\(S(800, 1100)\);

\(T(1202, 1005)\).
The screencast embedded with detailed audio explanations is available as a video at http://people.brunel.ac.uk/~cspgoop/data/notes/2014/min_enclosing_triangle.
4 Subalgorithms
Definition 1
 1.
The ray [\(polygon\)[\(index\)] \(gammaPoint'\)) intersects the polygon, where \(gammaPoint'\) is a point on \(L\) such that \(polygon\)[\(index\)] is the middle point of the line segment [\(gammaPoint\) \(gammaPoint'\)]. Let us denote the intersection point of the ray with the polygon, if it exists, as \(intersectionPoint\);
 2.
\(h\)(\(intersectionPoint) <\ h\)(\(polygon\)[\(index\)]).
Erroneous triangles will be obtained if the line (\(polygon\)[\(index\)] \(gammaPoint'\)) instead of the ray [\(polygon\)[\(index\)] \(gammaPoint'\)) is considered when computing the intersection with the polygon; see Klee and Laskowski (1985) for more details why the ray and not the line is considered.
Definition 2
 1.
The ray [\(polygon\)[\(index\)] \(gammaPoint\)) intersects the polygon. Let us denote this point, if it exists, as \(intersectionPoint\);
 2.
\(h\)(\(intersectionPoint\)) > \(h\)(\(polygon\)[\(index\)]).
According to condition 1 in Definitions 1 and 2, the intersection of the ray with the polygon has to be computed. However, this would lead to an increase in the overall complexity of the main algorithm. Thus an alternative constant complexity solution which considers only the angle (the slope could be considered as well) of the ray determined by \(gammaPoint\) and \(polygon\)[\(index\)] is employed. In this case the point \(gammaPoint'\) is no longer required because the angle of the ray [\(polygon\)[\(index\)] \(gammaPoint'\)) is equal to the angle of the ray [\(gammaPoint\) \(polygon\)[\(index\)]).
Both IntersectsAbove and IntersectsBelow functions call the function Intersects which checks if the line intersects the polygon ABOVE/BELOW the point \(polygon\)[\(index\)], or is CRITICAL. The only difference between IntersectsAbove and IntersectsBelow is the way in which they compute the angle of the line determined by \(gammaPoint\) and \(polygon\)[\(index\)] as can be seen in Algorithms 12 and 13. The angle is computed differently because the rays considered in condition 1 (Definitions 1 and 2) differ.

L1: The line [\(a\), \(a  1\)];

L2, L3: The lines parallel to and at a distance of (2 \(\times \) \(h\)(\(p\))) from [\(c\), \(c  1\)];
5 Results
The overall complexity of the algorithm is \(\Theta (n)\) where n represents the number of vertices defining the convex polygon.
A benchmark was set up to check the linearity of a C++ implementation of the algorithm. The variable of interest n represents the number of points defining the convex polygon. The considered values for n were chosen from the range 100 to 10,000 with a step size of 100.
The advantage of creating such a data set is that it could potentially be employed for testing the efficiency of other similar applications; see http://people.brunel.ac.uk/~cspgoop/data/notes/2014/min_enclosing_triangle for more details.
The execution time was measured in microseconds (\(\mu {s}\)). Since the algorithm is linear the execution times were relatively short. Execution times differences at small scales (e.g. microseconds) can be influenced among others by processes running in the background and the operating system. In order to overcome this issue the benchmark was run 100 times and only the mean of the execution times for each distinct value of n was considered.
The black points represent the mean of the execution times for a fixed value of n; the red and blue lines are the lines fitted to the obtained set of execution times using the formulae \(y \sim {\log }(x)\), respectively \(y \sim x\). According to the obtained results the execution time increases linearly for polygons defined by approximately 0–2,500 points, respectively logarithmically for polygons defined by approximately 2,500–10,000 points, with respect to n. In brief the implementation of the algorithm scales well with respect to the value of n. However, considering that the complexity of the algorithm is \(\Theta (n)\) our expectation was that the algorithm implementation would scale linearly, and not logarithmically, with respect to \(n\). Explaining the cause of this behaviour could be a potentially interesting research question, which is however not pursued here as it goes beyond the scope of this note.
6 Correctness
The executions described in Sect. 5 verified empirically that the algorithm implementation scales (sub)linearly with respect to \(n\). However, they did not assess the correctness of the computed minimal area enclosing triangle. In order to address this challenge three verification approaches were employed. Let us denote the expected optimal minimal area enclosing triangle by \(OT\), and the minimal enclosing triangle computed by the linear algorithm by \(CT\).
The first verification approach relies on generating regular convex ngons, \(n = 3k\) (in our case \(k = \overline{1, 3334}\)), for which the minimal enclosing triangle is known to be equilateral. Given the coordinates of the 2D points defining the ngon, \(OT\) can be automatically computed in \(O(1)\). If the minimal enclosing triangle \(CT\) computed by the algorithm implementation matches \(OT\) the algorithm implementation is considered valid. Otherwise it is invalid.
The second verification approach builds on the first one by applying affine transformations \(AT\) to each generated regular convex ngon, and thus obtaining a new convex polygon; in our case \(AT =\) {scaling by a factor of 1.5 with respect to both Ox and Oy, counterclockwise rotation by \(\pi / 4\)}. According to Klee and Laskowski (1985) the optimal enclosing triangle \(OT\) of a transformed polygon can be determined by applying the same affine transformations \(AT\) to the optimal enclosing triangle computed for the initial nontransformed regular polygon. Similarly, to the first verification approach \(OT\) can be determined in \(O(1)\). Moreover, the algorithm implementation is validated by checking if \(OT\) matches \(CT\).
The main advantage of the first two verification approaches is that the optimal minimal enclosing triangle \(OT\) can be determined in \(O(1)\). Conversely their main disadvantage is that only polygons with specific properties are considered, while the linear algorithm is general purpose and should work for any convex polygon.
In order to address this limitation the third verification approach checks if the results of a bruteforce algorithm (computing minimal enclosing triangles in \(O(n^{3})\), checking their validity in \(O(n)\)), which is guaranteed to check all possible minimal enclosing triangles, matches the results of the linear algorithm described in this note. The main advantage of this approach is that any convex polygon can be considered. Conversely its main disadvantage is that the bruteforce algorithm implementation does not scale well with \(n\). Therefore the value of \(n\) was limited to the range [3, 200] during our tests. Moreover, only ten polygons were randomly generated for each value of \(n\).
All verification approaches described above have been implemented in C++ and were employed to validate the linear minimal area enclosing algorithm implementation; the execution of all tests ended successfully empirically confirming that the algorithm implementation is valid. For reproducibility purposes the implementation of all algorithms and verification approaches (including generated datasets) are made freely available at https://github.com/IceRage/minimalareatriangle.
7 Conclusions
The steps which were not described in the original paper (O’Rourke et al. 1986) have been presented in detail here such that the implementation is easy to reproduce. A step by step execution of a simple example was described in Sect. 3 in order to illustrate how the minimum enclosing triangles are found. The results of the benchmark execution indicate that the algorithm is linear and scales well with respect to the number of points defining the convex polygon. Moreover, three different verification approaches were used to assess the correctness of the algorithm implementation.
The algorithm was implemented and tested in C++. A version of the algorithm was implemented and added to the imgproc module of the OpenCV library. It takes a 2D point set as input and computes its convex hull before finding the minimum enclosing triangle; see https://github.com/Itseez/opencv/blob/master/modules/imgproc/src/min_enclosing_triangle.cpp for more details. A usage example, documentation of the new functionality and unit tests have been added to the OpenCV library as well.
Finally, errors might occur when comparing real numbers due to the finite floating point precision of computers. Depending on the configuration of the system, the compiler and the representation of 2D points a different tolerance value for comparing real numbers should be used; see Dawson (2012) and Goldberg (1991) for more details.
Notes
Acknowledgments
The authors gratefully acknowledge the insightful comments provided by Joseph O’Rourke which helped improve the quality of the manuscript. Ovidiu Pârvu is supported by a scholarship from Brunel University.
Supplementary material
References
 Bradski G, Kaehler A (2008) Learning OpenCV. In: Computer vision with the OpenCV library. O’Reilly, New YorkGoogle Scholar
 CGAL (2013) Computational geometry algorithms library. http://www.cgal.org
 Chandran Mount DM (1992) A parallel algorithm for enclosed and enclosing triangles. Int J Comput Geom Appl 2(2):191–214. doi: 10.1142/S0218195992000123 MathSciNetCrossRefzbMATHGoogle Scholar
 Dawson B (2012) Comparing floating point numbers, 2012 edn. http://randomascii.wordpress.com/2012/02/25/comparingfloatingpointnumbers2012edition/
 Goldberg D (1991) What every computer scientist should know about floating point arithmetic. ACM Comput Surv 23(1):5–48CrossRefGoogle Scholar
 Klee V, Laskowski MC (1985) Finding the smallest triangles containing a given convex polygon. J Algorithms 6(3):359–375. doi: 10.1016/01966774(85)900057. http://www.sciencedirect.com/science/article/pii/0196677485900057
 O’Rourke J, Aggarwal A, Maddila S, Baldwin M (1986) An optimal algorithm for finding minimal enclosing triangles. J Algorithms 7(2):258–269. doi: 10.1016/01966774(86)900076. http://www.sciencedirect.com/science/article/pii/0196677486900076
 Toussaint GT (1983) Solving geometric problems with the rotating calipers. In: Proceedings of the IEEE Melecon, vol 83, p A10. http://web.cs.swarthmore.edu/adanner/cs97/s08/pdf/calipers.pdf
Copyright information
Open AccessThis article is distributed under the terms of the Creative Commons Attribution License which permits any use, distribution, and reproduction in any medium, provided the original author(s) and the source are credited.