cgal/Packages/Min_circle_2/web/Min_circle_2.aw

1324 lines
41 KiB
PHP

@! ============================================================================
@! The CGAL Project
@! Implementation: 2D Smallest Enclosing Circle
@! ----------------------------------------------------------------------------
@! file : Library/web/Min_circle_2.aw
@! author: Bernd Gärtner, Sven Schönherr (sven@inf.fu-berlin.de)
@! ----------------------------------------------------------------------------
@! $Revision$
@! $Date$
@! ============================================================================
@p maximum_input_line_length = 180
@p maximum_output_line_length = 180
@documentclass[twoside]{article}
@usepackage[latin1]{inputenc}
@usepackage{a4wide2}
@usepackage{amssymb}
@usepackage{cc_manual}
@article
\input{cprog.sty}
\setlength{\parskip}{0.6ex}
@! LaTeX macros
\newenvironment{pseudocode}[1]%
{\vspace*{-0.5\baselineskip} \upshape \begin{tabbing}
99 \= bla \= bla \= bla \= bla \= bla \= bla \= bla \= bla \kill
#1 \+ \\}%
{\end{tabbing}}
\newcommand{\keyword}[1]{\texttt{#1}}
\newcommand{\IF}{\keyword{IF} }
\newcommand{\THEN}{\keyword{THEN} \+ \\}
\newcommand{\ELSE}{\< \keyword{ELSE} \\}
\newcommand{\END}{\< \keyword{END} \- \\ }
\newcommand{\OR}{\keyword{OR} }
\newcommand{\FOR}{\keyword{FOR} }
\newcommand{\TO}{\keyword{TO} }
\newcommand{\DO}{\keyword{DO} \+ \\}
\newcommand{\RETURN}{\keyword{RETURN} }
\newcommand{\mc}{\texttt{mc}}
@! ============================================================================
@! Title
@! ============================================================================
\RCSdef{\rcsrevision}{$Revision$}
\RCSdefDate{\rcsdate}{$Date$}
@t vskip 5 mm
@t title titlefont centre "CGAL -- 2D Smallest Enclosing Circle*"
@t vskip 1 mm
@t title smalltitlefont centre "Implementation Documentation"
@t vskip 5 mm
@t title smalltitlefont centre "Bernd Gärtner and Sven Schönherr"
\smallskip
\centerline{\rcsrevision\ , \rcsdate}
@t vskip 1 mm
\renewcommand{\thefootnote}{\fnsymbol{footnote}}
\footnotetext[1]{This work was supported by the ESPRIT IV LTR Project
No.~21957 (CGAL).}
@! ============================================================================
@! Introduction and Contents
@! ============================================================================
\section*{Introduction}
We provide an implementation of an optimisation algorithm for computing
the smallest (w.r.t.\ area) enclosing circle of a finite point set $P$ in
the plane. The class template \ccc{CGAL_Min_circle_2<I>} is implemented
as a semi-dynamic data structure, thus allowing to insert points while
maintaining the smallest enclosing circle. It is parameterized with an
interface class, that defines the interface between the optimisation
algorithm and the primitives it uses.
This document is organized as follows. The algorithm is described in
Section~1. Section~2 contains the specification as it appears in the
CGAL Reference Manual. Section~3 gives the implementation. In
Section~4 we provide a test program which performs some correctness
checks. Finally the product files are created in Section~5.
\tableofcontents
@! ============================================================================
@! The Algorithm
@! ============================================================================
\setlength{\parskip}{0.8ex}
\clearpage
\section{The Algorithm} \label{sec:algo}
The implementation is based on an algorithm by Welzl~\cite{w-sedbe-91a},
which we shortly describe now. The smallest (w.r.t.\ area) enclosing
circle of a finite point set $P$ in the plane, denoted by $mc(P)$, is
built up incrementally, adding one point after another. Assume $mc(P)$
has been constructed, and we would like to obtain $mc(P \cup \{p\})$, $p$
some new point. There are two cases: if $p$ already lies inside $mc(P)$,
then $mc(P \cup \{p\}) = mc(P)$. Otherwise $p$ must lie on the boundary
of $mc(P \cup \{p\})$ (this is proved in~\cite{w-sedbe-91a} and not hard
to see), so we need to compute $mc(P,\{p\})$, the smallest circle
enclosing $P$ with $p$ on the boundary. This is recursively done in the
same manner. In general, for point sets $P$,$B$, define $mc(P,B)$ as the
smallest circle enclosing $P$ that has the points of $B$ on the boundary
(if defined). Although the algorithm finally delivers a circle
$mc(P,\emptyset)$, it internally deals with circles that have a possibly
nonempty set $B$. Here is the pseudocode of Welzl's method. To compute
$mc(P)$, it is called with the pair $(P,\emptyset)$, assuming that
$P=\{p_1,\ldots,p_n\}$ is stored in a linked list.
\begin{pseudocode}{$\mc(P,B)$:}
$mc := \mc(\emptyset,B)$ \\
\IF $|B| = 3$ \keyword{THEN} \RETURN $mc$ \\
\FOR $i := 1$ \TO $n$ \DO
\IF $p_i \not\in mc$ \THEN
$mc := \mc(\{p_1,\ldots,p_{i-1}\}, B \cup \{p_i\})$ \\
move $p_i$ to the front of $P$ \\
\END
\END
\RETURN $mc$ \\
\end{pseudocode}
Note the following: (a) $|B|$ is always bounded by 3, thus the
computation of $mc(\emptyset,B)$ is easy. In our implementation, it is
done by the private member function \ccc{compute_circle}. (b) One can
check that the method maintains the invariant `$mc(P,B)$ exists'. This
justifies termination if $|B| = 3$, because then $mc(P,B)$ must be the
unique circle with the points of $B$ on the boundary, and $mc(P,B)$
exists if and only if this circle contains the points of $P$. Thus, no
subsequent in-circle tests are necessary anymore (for details
see~\cite{w-sedbe-91a}). (c) points which are found to lie outside the
current circle $mc$ are considered `important' and are moved to the front
of the linked list that stores $P$. This is crucial for the method's
efficiency.
It can also be advisable to bring $P$ into random order before
computation starts. There are `bad' insertion orders which cause the
method to be very slow -- random shuffling gives these orders a very
small probability.
@! ============================================================================
@! Specification
@! ============================================================================
\clearpage
\section{Specification}
{\em Note:} Below some references are undefined, they refer to sections
in the \cgal\ Reference Manual.
\renewcommand{\ccSection}{\ccSubsection}
\renewcommand{\ccFont}{\tt}
\renewcommand{\ccEndFont}{}
\ccSetThreeColumns{Support_point_iterator}{Circle;}{}
\ccPropagateThreeToTwoColumns
\input{../spec/Min_circle_2.tex}
@! ============================================================================
@! Implementation
@! ============================================================================
\clearpage
\section{Implementation}
First, we declare the class template \ccc{CGAL_Min_circle_2}.
@macro<Min_circle_2 declaration> = @begin
template < class I >
class CGAL_Min_circle_2;
@end
The actual work of the algorithm is done in the private member
functions \ccc{mc} and \ccc{compute_circle}. The former directly
realizes the pseudocode of $\mc(P,B)$, the latter solves the basic
case $\mc(\emptyset,B)$, see Section~\ref{sec:algo}.
{\em Workaround:} The GNU compiler (g++ 2.7.*) does not accept types
with scope operator as argument type or return type in class template
member functions. Therefore, all member functions are implemented in
the class interface.
The class interface looks as follows.
@macro <Min_circle_2 interface> = @begin
template < class _I >
class CGAL_Min_circle_2 {
public:
@<Min_circle_2 public interface>
private:
// private data members
@<Min_circle_2 private data members>
// copying and assignment not allowed!
CGAL_Min_circle_2( CGAL_Min_circle_2<_I> const&);
CGAL_Min_circle_2<_I>& operator = ( CGAL_Min_circle_2<_I> const&);
@<dividing line>
// Class implementation
// ====================
public:
// Access functions and predicates
// -------------------------------
@<Min_circle_2 access functions `number_of_...'>
@<Min_circle_2 predicates `is_...'>
@<Min_circle_2 access functions>
@<Min_circle_2 predicates>
private:
// Privat member functions
// -----------------------
@<Min_circle_2 private member function `compute_circle'>
@<Min_circle_2 private member function `mc'>
public:
// Constructors
// ------------
@<Min_circle_2 constructors>
// Destructor
// ----------
@<Min_circle_2 destructor>
// Modifiers
// ---------
@<Min_circle_2 modifiers>
// Validity check
// --------------
@<Min_circle_2 validity check>
// I/O
// ---
@<Min_circle_2 I/O operators>
};
@end
@! ----------------------------------------------------------------------------
\subsection{Public Interface}
The functionality is described and documented in the specification
section, so we do not comment on it here.
@macro <Min_circle_2 public interface> = @begin
// types
typedef _I I;
typedef typename _I::Point Point;
typedef typename _I::Circle Circle;
typedef typename list<Point>::const_iterator Point_iterator;
typedef const Point * Support_point_iterator;
/**************************************************************************
WORKAROUND: The GNU compiler (g++ 2.7.*) does not accept types with
scope operator as argument type or return type in class template
member functions. Therefore, all member functions are implemented in
the class interface.
// creation
CGAL_Min_circle_2( I const& i = I());
CGAL_Min_circle_2( Point const& p,
I const& i = I());
CGAL_Min_circle_2( Point const& p,
Point const& q,
I const& i = I());
CGAL_Min_circle_2( Point const& p1,
Point const& p2,
Point const& p3,
I const& i = I());
CGAL_Min_circle_2( const Point* first,
const Point* last,
bool randomize = false,
CGAL_Random& random = CGAL_random,
I const& i = I());
CGAL_Min_circle_2( list<Point>::const_iterator first,
list<Point>::const_iterator last,
bool randomize = false,
CGAL_Random& random = CGAL_random,
I const& i = I());
~CGAL_Min_circle_2( );
// access functions
int number_of_points ( ) const;
int number_of_support_points( ) const;
Point_iterator points_begin( ) const;
Point_iterator points_end ( ) const;
Support_point_iterator support_points_begin( ) const;
Support_point_iterator support_points_end ( ) const;
Point const& support_point( int i) const;
Circle const& circle( ) const;
// predicates
CGAL_Bounded_side bounded_side( Point const& p) const;
bool has_on_bounded_side ( Point const& p) const;
bool has_on_boundary ( Point const& p) const;
bool has_on_unbounded_side ( Point const& p) const;
bool is_empty ( ) const;
bool is_degenerate( ) const;
// modifiers
void insert ( Point const& p);
// validity check
bool is_valid( bool verbose = false, int level = 0) const;
// I/O
friend ostream& operator << ( ostream& os, CGAL_Min_circle_2<I> const& mc);
friend istream& operator >> ( istream& is, CGAL_Min_circle_2<I> & mc);
**************************************************************************/
@end
@! ----------------------------------------------------------------------------
\subsection{Private Data Members}
First, the interface class object is stored.
@macro <Min_circle_2 private data members> += @begin
I ico; // interface class object
@end
The points of $P$ are internally stored as a linked list that allows to
bring points to the front of the list in constant time. We use the
sequence container \ccc{list} from STL~\cite{sl-stl-95}.
@macro <Min_circle_2 private data members> += @begin
list<Point> points; // doubly linked list of points
@end
The support set $S$ of at most three support points is stored in an
array \ccc{support_points}, the actual number of support points is
given by \ccc{n_support_points}. During the computations, the set of
support points coincides with the set $B$ appearing in the pseudocode
for $\mc(P,B)$, see Section~\ref{sec:algo}.
{\em Workaround:} The array of support points is allocated dynamically,
because the SGI compiler (mipspro CC 7.1) does not accept a static
array here.
@macro <Min_circle_2 private data members> += @begin
int n_support_points; // number of support points
Point* support_points; // array of support points
@end
Finally, an actual circle is stored in \ccc{min_circle}, by the end
of computation equal to $mc(P)$. During computation, this circle
equals the circle $mc$ appearing in the pseudocode for $\mc(P,B)$,
see Section~\ref{sec:algo}.
@macro <Min_circle_2 private data members> += @begin
Circle min_circle; // current circle
@end
@! ----------------------------------------------------------------------------
\subsection{Constructors and Destructor}
We provide several different constructors, which can be put into two
groups. The constructors in the first group, i.e. the more important
ones, build the smallest enclosing circle $mc(P)$ from a point set
$P$, given by a begin iterator and a past-the-end iterator. Usually
this would be done by a single member template, but since most
compilers do not support member templates yet, we provide specialized
constructors for C~arrays (using pointers as iterators) and for STL
sequence containers \ccc{vector<Point>} and \ccc{list<Point>}.
Actually, the constructors for a C~array and a \ccc{vector<point>} are
the same, since the random access iterator of \ccc{vector<Point>} is
implemented as \ccc{Point*}.
Both constructors of the first group copy the points into the
internal list \ccc{points}. If randomization is demanded, the points
are copied to a vector and shuffled at random, before being copied to
\ccc{points}. Finally the private member function $mc$ is called to
compute $mc(P)=mc(P,\emptyset)$.
@macro <Min_circle_2 constructors> += @begin
// STL-like constructor for C array and vector<Point>
CGAL_Min_circle_2( const Point* first,
const Point* last,
bool randomize = false,
CGAL_Random& random = CGAL_random,
I const& i = I())
: ico( i)
{
// allocate support points' array
support_points = new Point[ 3];
// compute number of points
if ( ( last-first) > 0) {
// store points
if ( randomize) {
// shuffle points at random
vector<Point> v( first, last);
random_shuffle( v.begin(), v.end(), random);
copy( v.begin(), v.end(), back_inserter( points)); }
else
copy( first, last, back_inserter( points)); }
// compute mc
mc( points.end(), 0);
}
// STL-like constructor for list<Point>
CGAL_Min_circle_2( list<Point>::const_iterator first,
list<Point>::const_iterator last,
bool randomize = false,
CGAL_Random& random = CGAL_random,
I const& i = I())
: ico( i)
{
// allocate support points' array
support_points = new Point[ 3];
// compute number of points
list<Point>::size_type n = 0;
__distance( first, last, n, bidirectional_iterator_tag());
if ( n > 0) {
// store points
if ( randomize) {
// shuffle points at random
vector<Point> v;
v.reserve( n);
copy( first, last, back_inserter( v));
random_shuffle( v.begin(), v.end(), random);
copy( v.begin(), v.end(), back_inserter( points)); }
else
copy( first, last, back_inserter( points)); }
// compute mc
mc( points.end(), 0);
}
@end
The remaining constructors are actually specializations of the
previous ones, building the smallest enclosing circle for up to three
points. The idea is the following: recall that for any point set $P$
there exists $S \subseteq P$, $|S| \leq 3$ with $mc(S) = mc(P)$ (in
fact, such a set $S$ is determined by the algorithm). Once $S$ has
been computed (or given otherwise), $mc(P)$ can easily be
reconstructed from $S$ in constant time. To make this reconstruction
more convenient, a constructor is available for each size of $|S|$,
ranging from 0 to 3. For $|S|=0$, we get the default constructor,
building $mc(\emptyset)$.
@macro <Min_circle_2 constructors> += @begin
// default constructor
inline
CGAL_Min_circle_2( I const& i = I())
: ico( i), n_support_points( 0)
{
// allocate support points' array
support_points = new Point[ 3];
CGAL_optimisation_postcondition( is_empty());
}
// constructor for one point
inline
CGAL_Min_circle_2( Point const& p, I const& i = I())
: ico( i), points( 1, p), n_support_points( 1), min_circle( p)
{
// allocate support points' array
support_points = new Point[ 3];
support_points[ 0] = p;
CGAL_optimisation_postcondition( is_degenerate());
}
// constructor for two points
inline
CGAL_Min_circle_2( Point const& p1, Point const& p2, I const& i = I())
: ico( i)
{
// allocate support points' array
support_points = new Point[ 3];
// store points
points.push_back( p1);
points.push_back( p2);
// compute mc
mc( points.end(), 0);
}
// constructor for three points
inline
CGAL_Min_circle_2( Point const& p1,
Point const& p2,
Point const& p3,
I const& i = I())
: ico( i)
{
// allocate support points' array
support_points = new Point[ 3];
// store points
points.push_back( p1);
points.push_back( p2);
points.push_back( p3);
// compute mc
mc( points.end(), 0);
}
@end
The destructor only frees the memory of the support points' array.
@macro <Min_circle_2 destructor> = @begin
inline
~CGAL_Min_circle_2( )
{
// free support points' array
delete[] support_points;
}
@end
@! ----------------------------------------------------------------------------
\subsection{Access Functions}
These functions are used to retrieve information about the current
status of the \ccc{CGAL_Min_circle_2<I>} object. They are all very
simple (and therefore \ccc{inline}) and mostly rely on corresponding
access functions of the data members of \ccc{CGAL_Min_circle_2<I>}.
First, we define the \ccc{number_of_...} methods.
@macro <Min_circle_2 access functions `number_of_...'> = @begin
// #points and #support points
inline
int
number_of_points( ) const
{
return( points.size());
}
inline
int
number_of_support_points( ) const
{
return( n_support_points);
}
@end
Then, we have the access functions for points and support points.
@macro <Min_circle_2 access functions> += @begin
// access to points and support points
inline
Point_iterator
points_begin( ) const
{
return( points.begin());
}
inline
Point_iterator
points_end( ) const
{
return( points.end());
}
inline
Support_point_iterator
support_points_begin( ) const
{
return( support_points);
}
inline
Support_point_iterator
support_points_end( ) const
{
return( support_points+n_support_points);
}
// random access for support points
inline
Point const&
support_point( int i) const
{
CGAL_optimisation_precondition( (i >= 0) &&
(i < number_of_support_points()));
return( support_points[ i]);
}
@end
Finally, the access function \ccc{circle}. Note that it is
\ccc{const} to the outside world, but internally we check the
orientation of the current circle and reverse it, if necessary
(logical constness).
@macro <Min_circle_2 access functions> += @begin
// circle
inline
Circle const&
circle( ) const
{
CGAL_optimisation_precondition( ! is_empty());
// ensure positive orientation
if ( min_circle.orientation() == CGAL_NEGATIVE)
CGAL_const_cast( Circle&, min_circle) = min_circle.opposite();
CGAL_optimisation_assertion(min_circle.orientation() == CGAL_POSITIVE);
return( min_circle);
}
@end
@! ----------------------------------------------------------------------------
\subsection{Predicates}
The predicates \ccc{is_empty} and \ccc{is_degenerate} are used in
preconditions and postconditions of some member functions. Therefore we define
them \ccc{inline} and put them in a separate macro.
@macro <Min_circle_2 predicates `is_...'> = @begin
// is_... predicates
inline
bool
is_empty( ) const
{
return( number_of_support_points() == 0);
}
inline
bool
is_degenerate( ) const
{
return( number_of_support_points() < 2);
}
@end
The remaining predicates perform in-circle tests, based on the
corresponding predicates of class \ccc{Circle}.
@macro <Min_circle_2 predicates> = @begin
// in-circle test predicates
inline
CGAL_Bounded_side
bounded_side( Point const& p) const
{
return( is_empty() ? CGAL_ON_UNBOUNDED_SIDE
: min_circle.bounded_side( p));
}
inline
bool
has_on_bounded_side( Point const& p) const
{
return( ( ! is_empty()) && ( min_circle.has_on_bounded_side( p)));
}
inline
bool
has_on_boundary( Point const& p) const
{
return( ( ! is_empty()) && ( min_circle.has_on_boundary( p)));
}
inline
bool
has_on_unbounded_side( Point const& p) const
{
return( ( is_empty()) || ( min_circle.has_on_unbounded_side( p)));
}
@end
@! ----------------------------------------------------------------------------
\subsection{Modifiers}
There is another way to build up $mc(P)$, other than by supplying
the point set $P$ at once. Namely, $mc(P)$ can be built up
incrementally, adding one point after another. If you look at the
pseudocode in the introduction, this comes quite naturally. The
modifying method \ccc{insert}, applied with point $p$ to a
\ccc{CGAL_Min_circle_2<I>} object representing $mc(P)$, computes
$mc(P \cup \{p\})$, where work has to be done only if $p$ lies
outside $mc(P)$. In this case, $mc(P \cup \{p\}) = mc(P,\{p\})$
holds, so the private member function \ccc{mc} is called with
support set $\{p\}$. After the insertion has been performed, $p$ is
moved to the front of the point list, just like in the pseudocode
in Section~\ref{sec:algo}.
@macro <Min_circle_2 modifiers> = @begin
void
insert( Point const& p)
{
// p not in current circle?
if ( has_on_unbounded_side( p)) {
// p new support point
support_points[ 0] = p;
// recompute mc
mc( points.end(), 1);
// store p as the first point in list
points.push_front( p); }
else
// append p to the end of the list
points.push_back( p);
}
@end
@! ----------------------------------------------------------------------------
\subsection{Validity Check}
A \ccc{CGAL_Min_circle_2<I>} object can be checked for
validity. This means, it is checked whether (a) the circle contains
all points of its defining set $P$, (b) the circle is the smallest
circle spanned by its support set, and (c) the support set is
minimal, i.e. no support point is redundant. The function
\ccc{is_valid} is mainly intended for debugging user supplied
interface classes. If \ccc{verbose} is \ccc{true}, some messages
concerning the performed checks are written to standard error
stream. The second parameter \ccc{level} is not used, we provide it
only for consistency with interfaces of other classes.
@macro <Min_circle_2 validity check> = @begin
bool
is_valid( bool verbose = true, int level = 0) const
{
CGAL_Verbose_ostream verr( verbose);
verr << endl;
verr << "CGAL_Min_circle_2<I>::" << endl;
verr << "is_valid( true, " << level << "):" << endl;
verr << " |P| = " << number_of_points()
<< ", |S| = " << number_of_support_points() << endl;
// containment check (a)
@<Min_circle_2 containment check>
// support set checks (b)+(c)
@<Min_circle_2 support set checks>
verr << " object is valid!" << endl;
return( true);
}
@end
The containment check (a) is easy to perform, just a loop over all
points in \ccc{points}.
@macro <Min_circle_2 containment check> = @begin
verr << " a) containment check..." << flush;
Point_iterator point_iter;
for ( point_iter = points_begin();
point_iter != points_end();
++point_iter)
if ( has_on_unbounded_side( *point_iter))
return( CGAL__optimisation_is_valid_fail( verr,
"circle does not contain all points"));
verr << "passed." << endl;
@end
To check the support set (b) and (c), we distinguish four cases with
respect to the number of support points, which may range from 0 to 3.
@macro <Min_circle_2 support set checks> = @begin
verr << " b)+c) support set checks..." << flush;
switch( number_of_support_points()) {
case 0:
@<Min_circle_2 check no support point>
break;
case 1:
@<Min_circle_2 check one support point>
break;
case 2: {
@<Min_circle_2 check two support points> }
break;
case 3: {
@<Min_circle_2 check three support points> }
break;
default:
return( CGAL__optimisation_is_valid_fail( verr,
"illegal number of support points, not between 0 and 3."));
};
verr << "passed." << endl;
@end
The case of no support point happens if and only if the defining
point set $P$ is empty.
@macro <Min_circle_2 check no support point> = @begin
if ( ! is_empty())
return( CGAL__optimisation_is_valid_fail( verr,
"P is nonempty, but there are no support points."));
@end
If the smallest enclosing circle has one support point $p$, it must
be equal to that point, i.e.\ its center must be $p$ and its radius
$0$.
@macro <Min_circle_2 check one support point> = @begin
if ( ( circle().center() != support_point( 0) ) ||
( ! CGAL_is_zero( circle().squared_radius())) )
return( CGAL__optimisation_is_valid_fail( verr,
"circle differs from the circle \
spanned by its single support point."));
@end
In case of two support points $p,q$, these points must form a diameter
of the circle. The support set $\{p,q\}$ is minimal if and only if
$p,q$ are distinct.
The diameter property is checked as follows. If $p$ and $q$ both lie
on the circle's boundary and if $p$, $q$ (knowing they are distinct)
and the circle's center are collinear, then $p$ and $q$ form a
diameter of the circle.
@macro <Min_circle_2 check two support points> = @begin
Point const& p( support_point( 0)),
q( support_point( 1));
// p equals q?
if ( p == q)
return( CGAL__optimisation_is_valid_fail( verr,
"the two support points are equal."));
// segment(p,q) is not diameter?
if ( ( ! circle().has_on_boundary( p) ) ||
( ! circle().has_on_boundary( q) ) ||
( ico.orientation( p, q, circle().center()) != CGAL_COLLINEAR) )
return( CGAL__optimisation_is_valid_fail( verr,
"circle does not have its two support points as diameter."));
@end
If the number of support points is three (and they are distinct and
not collinear), the circle through them is unique, and must therefore
equal \ccc{min_circle}. It is the smallest one containing the three
points if and only if the center of the circle lies inside or on the
boundary of the triangle defined by the three points. The support set
is minimal only if the center lies properly inside the triangle.
Both triangle properties are checked by comparing the orientatons of
three point triples, each containing two of the support points and the
center $z$ of \ccc{min_circle}, resp. If one of these orientations
equals \ccc{CGAL_COLLINEAR}, $z$ lies on the boundary of the triangle.
Otherwise, if two triples have opposite orientations, $z$ is not
contained in the triangle.
@macro <Min_circle_2 check three support points> = @begin
Point const& p( support_point( 0)),
q( support_point( 1)),
r( support_point( 2));
// p, q, r not pairwise distinct?
if ( ( p == q) || ( q == r) || ( r == p))
return( CGAL__optimisation_is_valid_fail( verr,
"at least two of the three support points are equal."));
// p, q, r collinear?
if ( ico.orientation( p, q, r) == CGAL_COLLINEAR)
return( CGAL__optimisation_is_valid_fail( verr,
"the three support points are collinear."));
// circle() not equal the unique circle through p,q,r (except orientation)?
Circle c( p, q, r);
Point const& z( circle().center());
if ( ( z != c.center() ) ||
( circle().squared_radius() != c.squared_radius()) )
return( CGAL__optimisation_is_valid_fail( verr,
"circle is not the unique circle \
through its three support points."));
// circle().center() on boundary of triangle(p,q,r)?
CGAL_Orientation o_pqz = ico.orientation( p, q, z);
CGAL_Orientation o_qrz = ico.orientation( q, r, z);
CGAL_Orientation o_rpz = ico.orientation( r, p, z);
if ( ( o_pqz == CGAL_COLLINEAR) ||
( o_qrz == CGAL_COLLINEAR) ||
( o_rpz == CGAL_COLLINEAR) )
return( CGAL__optimisation_is_valid_fail( verr,
"one of the three support points is redundant."));
// circle().center() not inside triangle(p,q,r)?
if ( ( o_pqz != o_qrz) || ( o_qrz != o_rpz) || ( o_rpz != o_pqz))
return( CGAL__optimisation_is_valid_fail( verr,
"circle's center is not in the \
convex hull of its three support points."));
@end
@! ----------------------------------------------------------------------------
\subsection{I/O}
@macro <Min_circle_2 I/O operators> = @begin
friend
ostream&
operator << ( ostream& os, CGAL_Min_circle_2<I> const& min_circle)
{
switch ( os.iword( CGAL_IO::mode)) {
case CGAL_IO::PRETTY:
os << endl;
os << "CGAL_Min_circle_2( |P| = " << min_circle.number_of_points()
<< ", |S| = " << min_circle.number_of_support_points() << endl;
os << " P = {" << endl;
os << " ";
copy( min_circle.points_begin(), min_circle.points_end(),
ostream_iterator<Point>( os, ",\n "));
os << "}" << endl;
os << " S = {" << endl;
os << " ";
copy( min_circle.support_points_begin(),
min_circle.support_points_end(),
ostream_iterator<Point>( os, ",\n "));
os << "}" << endl;
os << " min_circle = " << min_circle.circle() << endl;
os << ")" << endl;
break;
case CGAL_IO::ASCII:
copy( min_circle.points_begin(), min_circle.points.end(),
ostream_iterator<Point>( os, "\n"));
break;
case CGAL_IO::BINARY:
copy( min_circle.points_begin(), min_circle.points.end(),
ostream_iterator<Point>( os, " "));
break;
default:
CGAL_optimisation_assertion_msg( false, "CGAL_IO::mode invalid!");
break; }
return( os);
}
friend
istream&
operator >> ( istream& is, CGAL_Min_circle_2<I>& min_circle)
{
switch ( is.iword( CGAL_IO::mode)) {
case CGAL_IO::PRETTY:
break;
case CGAL_IO::ASCII:
case CGAL_IO::BINARY:
min_circle.points.erase( min_circle.points.begin(),
min_circle.points.end());
copy( istream_iterator<Point, ptrdiff_t>( is),
istream_iterator<Point, ptrdiff_t>(),
back_inserter( min_circle.points));
min_circle.mc( min_circle.points.end(), 0);
break;
default:
CGAL_optimisation_assertion_msg( false, "CGAL_IO::mode invalid!");
break; }
return( is);
}
@end
@! ----------------------------------------------------------------------------
\subsection{Private Member Function {\ccFont compute\_circle}}
This is the method for computing the basic case $\mc(\emptyset,B)$,
the set $B$ given by the first \ccc{n_support_points} in the array
\ccc{support_points}. It is realized by a simple case analysis,
noting that $|B| \leq 3$.
@macro <Min_circle_2 private member function `compute_circle'> = @begin
// compute_circle
inline
void
compute_circle( )
{
switch ( n_support_points) {
case 3:
min_circle = Circle( support_points[ 0],
support_points[ 1],
support_points[ 2]);
break;
case 2:
min_circle = Circle( support_points[ 0], support_points[ 1]);
break;
case 1:
min_circle = Circle( support_points[ 0]);
break;
case 0:
min_circle = Circle( );
break;
default:
CGAL_optimisation_assertion( ( n_support_points >= 0) &&
( n_support_points <= 3) ); }
}
@end
@! ----------------------------------------------------------------------------
\subsection{Private Member Function {\ccFont mc}}
This function computes the general circle $mc(P,B)$, where $P$
contains the points in the range
$[$\ccc{points.begin()}$,$\ccc{last}$)$ and $B$ is given by the first
\ccc{n_sp} support points in the array \ccc{support_points}. The
function is directly modelled after the pseudocode above.
@macro <Min_circle_2 private member function `mc'> = @begin
void
mc( Point_iterator const& last, int n_sp)
{
// compute circle through support points
n_support_points = n_sp;
compute_circle( );
if ( n_sp == 3) return;
// test first n points
list<Point>::iterator point_iter( points.begin());
for ( ; last != point_iter; ) {
Point const& p( *point_iter);
// p not in current circle?
if ( has_on_unbounded_side( p)) {
// recursive call with p as additional support point
support_points[ n_sp] = p;
mc( point_iter, n_sp+1);
// move current point to front
if ( point_iter != points.begin()) { // p not first?
points.push_front( p);
points.erase( point_iter++); }
else
++point_iter; }
else
++point_iter; }
}
@end
@! ============================================================================
@! Test
@! ============================================================================
\clearpage
\section{Test}
We test \ccc{CGAL_Min_circle_2} with the default interface class for
optimisation algorithms, using exact arithmetic, i.e.\ homogeneous
representation and number type \ccc{integer}.
@macro <Min_circle_2 test (includes and typedefs)> = @begin
#include <CGAL/Integer.h>
#include <CGAL/Homogeneous.h>
#include <CGAL/Optimisation_default_interface_2.h>
#include <CGAL/Min_circle_2.h>
#include <CGAL/Random.h>
// #include <CGAL/IO/ostream_2.h>
#include <CGAL/IO/new_iostream.h>
#include <CGAL/IO/Verbose_ostream.h>
#include <algo.h>
#include <assert.h>
#include <fstream.h>
#include <LEDA/REDEFINE_NAMES.h>
typedef integer NT;
#include <LEDA/UNDEFINE_NAMES.h>
typedef CGAL_Homogeneous<NT> R;
typedef CGAL_Optimisation_default_interface_2<R> I;
typedef CGAL_Min_circle_2<I> Min_circle;
typedef Min_circle::Point Point;
typedef Min_circle::Circle Circle;
@end
We call each function of class \ccc{CGAL_Min_circle_2<I>} at least once
to ensure code coverage. The command line option \ccc{-verbose}
enables verbose output.
@macro <Min_circle_2 test (verbose option)> = @begin
bool verbose = false;
if ( ( argc > 1) && ( strcmp( argv[ 1], "-verbose") == 0)) {
verbose = true;
--argc;
++argv; }
CGAL_Verbose_ostream verr( verbose);
@end
@macro <Min_circle_2 test (code coverage)> = @begin
// generate 10 points at random
CGAL_Random random_x, random_y;
Point random_points[ 10];
int i;
verr << "10 random points from [0,100)^2:" << endl;
for ( i = 0; i < 10; ++i)
random_points[ i] = Point( random_x( 100), random_y( 100));
if ( verbose)
for ( i = 0; i < 10; ++i)
cerr << i << ": " << random_points[ i] << endl;
verr << endl << "default constructor...";
{
Min_circle mc;
assert( mc.is_empty());
assert( mc.is_valid( verbose));
}
verr << endl << "one point constructor...";
{
Min_circle mc( random_points[ 0]);
assert( mc.is_degenerate());
assert( mc.is_valid( verbose));
}
verr << endl << "two points constructor...";
assert( Min_circle( random_points[ 1],
random_points[ 2]).is_valid( verbose));
verr << endl << "three points constructor...";
assert( Min_circle( random_points[ 3],
random_points[ 4],
random_points[ 5]).is_valid( verbose));
verr << endl << "Point* constructor (without randomization)...";
assert( Min_circle( random_points, random_points+9).is_valid( verbose));
verr << endl << "Point* constructor (with randomization)...";
Min_circle mc( random_points, random_points+9, true);
assert( mc.is_valid( verbose));
verr << endl << "list<Point>::const_iterator constructor...";
assert( Min_circle( mc.points_begin(), mc.points_end(), true).circle()
== mc.circle());
verr << endl << "#points...";
assert( mc.number_of_points() == 9);
verr << endl << "points access already called above.";
verr << endl << "support points access...";
Min_circle::Support_point_iterator iter( mc.support_points_begin());
for ( i = 0; i < mc.number_of_support_points(); ++i, ++iter)
assert( mc.support_point( i) == *iter);
assert( iter == mc.support_points_end());
verr << endl << "circle access...";
Circle circle( mc.circle());
verr << endl << "in-circle predicates...";
for ( i = 0; i < 9; ++i) {
Point const& p( random_points[ i]);
assert( ( mc.bounded_side( p) != CGAL_ON_UNBOUNDED_SIDE ) &&
( mc.has_on_bounded_side( p) || mc.has_on_boundary( p)) &&
( ! mc.has_on_unbounded_side( p) ) );
assert(
(mc.bounded_side(p) == circle.bounded_side(p) ) &&
(mc.has_on_bounded_side(p) == circle.has_on_bounded_side(p)) &&
(mc.has_on_boundary(p) == circle.has_on_boundary(p) ) &&
(mc.has_on_unbounded_side(p) == circle.has_on_unbounded_side(p)));}
verr << endl << "is_... predicates already called above.";
verr << endl << "modifiers...";
mc.insert( random_points[ 9]);
assert( mc.is_valid( verbose));
verr << endl << "validity check already called several times.";
verr << endl << "I/O...";
{
verr << endl << " writing `test_Min_circle_2.pretty'...";
ofstream os( "test_Min_circle_2.pretty");
os << CGAL_pretty << mc;
}
{
verr << endl << " writing `test_Min_circle_2.ascii'...";
ofstream os( "test_Min_circle_2.ascii");
os << CGAL_ascii << mc;
}
{
verr << endl << " writing `test_Min_circle_2.binary'...";
ofstream os( "test_Min_circle_2.binary");
os << CGAL_binary << mc;
}
{
verr << endl << " reading `test_Min_circle_2.ascii'...";
Min_circle mc_in;
ifstream is( "test_Min_circle_2.ascii");
is.iword( CGAL_IO::mode) = CGAL_IO::ASCII;
is >> mc_in;
assert( mc.circle() == mc_in.circle());
}
verr << endl;
@end
In addition, some data files can be given as command line
arguments. A data file contains pairs of \ccc{int}, namely the x- and
y-coordinates of a set of points. The first number in the file is the
number of points. A short description of the test set is given at the
end of each file.
@macro <Min_circle_2 test (external test sets)> = @begin
while ( argc > 1) {
// read points from file
verr << endl << "input file: `" << argv[ 1] << "'" << flush;
list<Point> points;
int n, x, y;
ifstream in( argv[ 1]);
assert( in >> n);
for ( i = 0; i < n; ++i) {
assert( in >> x >> y);
points.push_back( Point( x, y)); }
// compute and check min_circle
assert( Min_circle( points.begin(), points.end()).is_valid( verbose));
// next file
--argc;
++argv; }
@end
@! ==========================================================================
@! Files
@! ==========================================================================
\clearpage
\section{Files}
@file <Min_circle_2.h> = @begin
@<Min_circle_2 header>("include/CGAL/Min_circle_2.h")
#ifndef CGAL_MIN_CIRCLE_2_H
#define CGAL_MIN_CIRCLE_2_H
// Class declaration
// =================
@<Min_circle_2 declaration>
// Class interface
// ===============
// includes
#ifndef CGAL_RANDOM_H
# include <CGAL/Random.h>
#endif
#ifndef CGAL_OPTIMISATION_ASSERTIONS_H
# include <CGAL/optimisation_assertions.h>
#endif
#ifndef CGAL_OPTIMISATION_MISC_H
# include <CGAL/optimisation_misc.h>
#endif
#include <list.h>
#include <vector.h>
#include <algo.h>
#include <iostream.h>
@<Min_circle_2 interface>
#endif // CGAL_MIN_CIRLCE_2_H
@<end of file line>
@end
@file <test_Min_circle_2.C> = @begin
@<Min_circle_2 header>("test/test_Min_circle_2.C")
#define typename
#define CGAL_kernel_assertion CGAL_assertion
#define CGAL_kernel_precondition CGAL_precondition
#define CGAL_kernel_postcondition CGAL_postcondition
#define CGAL_nondegeneracy_assertion
#define CGAL_nondegeneracy_precondition(cond)
@<Min_circle_2 test (includes and typedefs)>
int
main( int argc, char* argv[])
{
// command line options
// --------------------
// option `-verbose'
@<Min_circle_2 test (verbose option)>
// code coverage
// -------------
@<Min_circle_2 test (code coverage)>
// external test sets
// -------------------
@<Min_circle_2 test (external test sets)>
}
@<end of file line>
@end
@i file_header.awlib
@macro <Min_circle_2 header>(1) many = @begin
@<file header>("2D Smallest Enclosing Circle",@1,"Min_circle_2",
"Bernd Gärtner, Sven Schönherr (sven@@inf.fu-berlin.de)",
"$Revision$","$Date$")
@end
@! ============================================================================
@! Bibliography
@! ============================================================================
\clearpage
\bibliographystyle{plain}
\bibliography{geom,cgal}
@! ===== EOF ==================================================================