mirror of https://github.com/CGAL/cgal
3449 lines
107 KiB
PHP
3449 lines
107 KiB
PHP
@! ============================================================================
|
|
@! The CGAL Library
|
|
@! Implementation: 2D Smallest Enclosing Ellipse
|
|
@! ----------------------------------------------------------------------------
|
|
@! file : web/Min_ellipse_2.aw
|
|
@! author: Bernd Gärtner, Sven Schönherr <sven@inf.ethz.ch>
|
|
@! ----------------------------------------------------------------------------
|
|
@! $CGAL_Chapter: Geometric Optimisation $
|
|
@! $CGAL_Package: Min_ellipse_2 WIP $
|
|
@! $Id$
|
|
@! $Date$
|
|
@! ============================================================================
|
|
|
|
@documentclass[twoside]{article}
|
|
@usepackage[latin1]{inputenc}
|
|
@usepackage{a4wide2}
|
|
@usepackage{amssymb}
|
|
@usepackage{path}
|
|
@usepackage{cc_manual,cc_manual_index}
|
|
@article
|
|
|
|
\input{cprog.sty}
|
|
\setlength{\parskip}{1ex}
|
|
|
|
@! 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{\me}{\texttt{me}}
|
|
|
|
\newcommand{\linebreakByHand}{\ccTexHtml{\linebreak[4]}{}}
|
|
\newcommand{ \newlineByHand}{\ccTexHtml{\\}{}}
|
|
\newcommand{\SaveSpaceByHand}{} %%%%% [2]{\ccTexHtml{#1}{#2}}
|
|
|
|
@! ============================================================================
|
|
@! Title
|
|
@! ============================================================================
|
|
|
|
\RCSdef{\rcsRevision}{$Id$}
|
|
\RCSdefDate{\rcsDate}{$Date$}
|
|
\newcommand{\cgalWIP}{{\footnotesize{} (\rcsRevision{} , \rcsDate) }}
|
|
|
|
@t vskip 5 mm
|
|
@t title titlefont centre "2D Smallest Enclosing Ellipse*"
|
|
@t vskip 1 mm
|
|
@t title smalltitlefont centre "Bernd Gärtner and Sven Schönherr"
|
|
\smallskip
|
|
\begin{center}
|
|
\begin{tabular}{l}
|
|
\verb+$CGAL_Chapter: Geometric Optimisation $+ \\
|
|
\verb+$CGAL_Package: Min_ellipse_2 WIP+\cgalWIP\verb+$+ \\
|
|
\end{tabular}
|
|
\end{center}
|
|
@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 ellipse of a finite point set $P$
|
|
in the plane. The class template \ccc{Min_ellipse_2} is implemented
|
|
as a semi-dynamic data structure, thus allowing to insert points while
|
|
maintaining the smallest enclosing ellipse. It is parameterized with a
|
|
traits class, that defines the abstract interface between the
|
|
optimisation algorithm and the primitives it uses. For ease of use, we
|
|
provide traits class adapters that interface the optimisation algorithm
|
|
with user supplied point classes.
|
|
|
|
This document is organized as follows. The algorithm is described in
|
|
Section~1. Section~2 contains the specifications as they appear in the
|
|
CGAL Reference Manual. Section~3 gives the implementations. 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
|
|
@! ============================================================================
|
|
|
|
\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
|
|
ellipse of a finite point set $P$ in the plane, denoted by $me(P)$, is
|
|
built up incrementally, adding one point after another. Assume $me(P)$
|
|
has been constructed, and we would like to obtain $me(P \cup \{p\})$, $p$
|
|
some new point. There are two cases: if $p$ already lies inside $me(P)$,
|
|
then $me(P \cup \{p\}) = me(P)$. Otherwise $p$ must lie on the boundary
|
|
of $me(P \cup \{p\})$ (this is proved in~\cite{w-sedbe-91a} and not hard
|
|
to see), so we need to compute $me(P,\{p\})$, the smallest ellipse
|
|
enclosing $P$ with $p$ on the boundary. This is recursively done in the
|
|
same manner. In general, for point sets $P$,$B$, define $me(P,B)$ as the
|
|
smallest ellipse enclosing $P$ that has the points of $B$ on the boundary
|
|
(if defined). Although the algorithm finally delivers a ellipse
|
|
$me(P,\emptyset)$, it internally deals with ellipses that have a possibly
|
|
nonempty set $B$. Here is the pseudo-code of Welzl's method. To compute
|
|
$me(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}{$\me(P,B)$:}
|
|
$me := \me(\emptyset,B)$ \\
|
|
\IF $|B| = 5$ \keyword{THEN} \RETURN $me$ \\
|
|
\FOR $i := 1$ \TO $n$ \DO
|
|
\IF $p_i \not\in me$ \THEN
|
|
$me := \me(\{p_1,\ldots,p_{i-1}\}, B \cup \{p_i\})$ \\
|
|
move $p_i$ to the front of $P$ \\
|
|
\END
|
|
\END
|
|
\RETURN $me$ \\
|
|
\end{pseudocode}
|
|
|
|
Note the following: (a) $|B|$ is always bounded by 5, thus the computation of
|
|
$me(\emptyset,B)$ is easy. In our implementation, it is done by the private
|
|
member function \ccc{compute_ellipse}. (b) One can check that the method
|
|
maintains the invariant `$me(P,B)$ exists'. This justifies termination if
|
|
$|B| = 5$, because then $me(P,B)$ must be the unique ellipse with the points
|
|
of $B$ on the boundary, and $me(P,B)$ exists if and only if this ellipse
|
|
contains the points of $P$. Thus, no subsequent in-ellipse tests are
|
|
necessary anymore (for details see~\cite{w-sedbe-91a}). (c) points which are
|
|
found to lie outside the current ellipse $me$ 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.
|
|
|
|
@! ============================================================================
|
|
@! Specifications
|
|
@! ============================================================================
|
|
|
|
@! \clearpage
|
|
@! \section{Specifications}
|
|
@!
|
|
@! \emph{Note:} Below some references are undefined, they refer to sections
|
|
@! in the \cgal\ Reference Manual.
|
|
@!
|
|
@! \renewcommand{\ccFont}{\tt}
|
|
@! \renewcommand{\ccEndFont}{}
|
|
@! \newcommand{\cgalColumnLayout}{\ccTexHtml{%
|
|
@! \ccSetThreeColumns{Oriented_side}{}{\hspace*{10cm}}
|
|
@! \ccPropagateThreeToTwoColumns}{}}
|
|
@! \newcommand{\cgalSetMinEllipseLayout}{%
|
|
@! \ccSetThreeColumns{Support_point_iterator}{}{returns
|
|
@! \ccc{ON_BOUNDED_SIDE}, \ccc{ON_BOUNDARY}, or \ccc{ON_UNBOUNDED_SIDE}}
|
|
@! % \ccSetThreeColumns{Support_point_iterator}{}{creates a variable
|
|
@! % \ccc{min_ellipse} of type \ccc{CGAL_Min_ellipse_2<Traits>}.}
|
|
@! \ccPropagateThreeToTwoColumns}
|
|
@! \newcommand{\cgalSetOptTraitsAdaptLayout}{\ccTexHtml{%
|
|
@! \ccSetThreeColumns{CGAL_Oriented_side}{}{returns constants
|
|
@! \ccc{CGAL_LEFT_TURN}, \ccc{CGAL_COLLINEAR}}
|
|
@! \ccPropagateThreeToTwoColumns}{}}
|
|
@! \input{../../doc_tex/basic/Optimisation/Min_ellipse_2.tex}
|
|
@! \input{../../doc_tex/basic/Optimisation/Optimisation_ellipse_2.tex}
|
|
@! \input{../../doc_tex/basic/Optimisation/Min_ellipse_2_traits_2.tex}
|
|
@! \input{../../doc_tex/basic/Optimisation/Min_ellipse_2_adapterC2.tex}
|
|
@! \input{../../doc_tex/basic/Optimisation/Min_ellipse_2_adapterH2.tex}
|
|
|
|
@! ============================================================================
|
|
@! Implementations
|
|
@! ============================================================================
|
|
|
|
\clearpage
|
|
\section{Implementations}
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
@! Class template Min_ellipse_2<Traits>
|
|
@! ----------------------------------------------------------------------------
|
|
|
|
\subsection{Class template \ccFont Min\_ellipse\_2<Traits>}
|
|
|
|
First, we declare the class template \ccc{Min_ellipse_2}.
|
|
|
|
@macro<Min_ellipse_2 declaration> = @begin
|
|
template < class Traits_ >
|
|
class Min_ellipse_2;
|
|
@end
|
|
|
|
The actual work of the algorithm is done in the private member
|
|
functions \ccc{me} and \ccc{compute_ellipse}. The former directly
|
|
realizes the pseudo-code of $\me(P,B)$, the latter solves the basic
|
|
case $\me(\emptyset,B)$, see Section~\ref{sec:algo}.
|
|
|
|
The class interface looks as follows.
|
|
|
|
@macro <Min_ellipse_2 interface> = @begin
|
|
template < class Traits_ >
|
|
class Min_ellipse_2 {
|
|
public:
|
|
@<Min_ellipse_2 public interface>
|
|
|
|
private:
|
|
// private data members
|
|
@<Min_ellipse_2 private data members>
|
|
|
|
// copying and assignment not allowed!
|
|
Min_ellipse_2( const Min_ellipse_2<Traits_>&);
|
|
Min_ellipse_2<Traits_>& operator = ( const Min_ellipse_2<Traits_>&);
|
|
|
|
@<dividing line>
|
|
|
|
// Class implementation
|
|
// ====================
|
|
|
|
public:
|
|
// Access functions and predicates
|
|
// -------------------------------
|
|
@<Min_ellipse_2 access functions `number_of_...'>
|
|
|
|
@<Min_ellipse_2 predicates `is_...'>
|
|
|
|
@<Min_ellipse_2 access functions>
|
|
|
|
@<Min_ellipse_2 predicates>
|
|
|
|
private:
|
|
// Private member functions
|
|
// ------------------------
|
|
@<Min_ellipse_2 private member function `compute_ellipse'>
|
|
|
|
@<Min_ellipse_2 private member function `me'>
|
|
|
|
public:
|
|
// Constructors
|
|
// ------------
|
|
@<Min_ellipse_2 constructors>
|
|
|
|
// Destructor
|
|
// ----------
|
|
@<Min_ellipse_2 destructor>
|
|
|
|
// Modifiers
|
|
// ---------
|
|
@<Min_ellipse_2 modifiers>
|
|
|
|
// Validity check
|
|
// --------------
|
|
@<Min_ellipse_2 validity check>
|
|
|
|
// Miscellaneous
|
|
// -------------
|
|
@<Min_ellipse_2 miscellaneous>
|
|
};
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Public Interface}
|
|
|
|
The functionality is described and documented in the specification
|
|
section, so we do not comment on it here.
|
|
|
|
@macro <Min_ellipse_2 public interface> = @begin
|
|
// types
|
|
typedef Traits_ Traits;
|
|
typedef typename Traits_::Point Point;
|
|
typedef typename Traits_::Ellipse Ellipse;
|
|
typedef typename std::list<Point>::const_iterator Point_iterator;
|
|
typedef const Point * Support_point_iterator;
|
|
|
|
/**************************************************************************
|
|
WORKAROUND: Some compilers are unable to match member functions defined
|
|
outside the class template. Therefore, all member functions are implemented
|
|
in the class interface.
|
|
|
|
// creation
|
|
template < class InputIterator >
|
|
Min_ellipse_2( InputIterator first,
|
|
InputIterator last,
|
|
bool randomize = false,
|
|
Random& random = default_random,
|
|
const Traits& traits = Traits());
|
|
|
|
Min_ellipse_2( const Traits& traits = Traits());
|
|
Min_ellipse_2( const Point& p,
|
|
const Traits& traits = Traits());
|
|
Min_ellipse_2( const Point& p,
|
|
const Point& q,
|
|
const Traits& traits = Traits());
|
|
Min_ellipse_2( const Point& p1,
|
|
const Point& p2,
|
|
const Point& p3,
|
|
const Traits& traits = Traits());
|
|
Min_ellipse_2( const Point& p1,
|
|
const Point& p2,
|
|
const Point& p3,
|
|
const Point& p4,
|
|
const Traits& traits = Traits());
|
|
Min_ellipse_2( const Point& p1,
|
|
const Point& p2,
|
|
const Point& p3,
|
|
const Point& p4,
|
|
const Point& p5,
|
|
const Traits& traits = Traits());
|
|
~Min_ellipse_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;
|
|
|
|
const Point& support_point( int i) const;
|
|
|
|
const Ellipse& ellipse( ) const;
|
|
|
|
// predicates
|
|
CGAL::Bounded_side bounded_side( const Point& p) const;
|
|
bool has_on_bounded_side ( const Point& p) const;
|
|
bool has_on_boundary ( const Point& p) const;
|
|
bool has_on_unbounded_side ( const Point& p) const;
|
|
|
|
bool is_empty ( ) const;
|
|
bool is_degenerate( ) const;
|
|
|
|
// modifiers
|
|
void insert( const Point& p);
|
|
void insert( const Point* first,
|
|
const Point* last );
|
|
void insert( std::list<Point>::const_iterator first,
|
|
std::list<Point>::const_iterator last );
|
|
void insert( std::istream_iterator<Point,std::ptrdiff_t> first,
|
|
std::istream_iterator<Point,std::ptrdiff_t> last );
|
|
void clear( );
|
|
|
|
// validity check
|
|
bool is_valid( bool verbose = false, int level = 0) const;
|
|
|
|
// miscellaneous
|
|
const Traits& traits( ) const;
|
|
**************************************************************************/
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Private Data Members}
|
|
|
|
First, the traits class object is stored.
|
|
|
|
@macro <Min_ellipse_2 private data members> += @begin
|
|
Traits tco; // traits 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_ellipse_2 private data members> += @begin
|
|
std::list<Point> points; // doubly linked list of points
|
|
@end
|
|
|
|
The support set $S$ of at most five 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 pseudo-code
|
|
for $\me(P,B)$, see Section~\ref{sec:algo}.
|
|
|
|
\emph{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_ellipse_2 private data members> += @begin
|
|
int n_support_points; // number of support points
|
|
Point* support_points; // array of support points
|
|
@end
|
|
|
|
Finally, the actual ellipse is stored in a variable \ccc{ellipse}
|
|
provided by the traits class object, by the end of computation equal to
|
|
$me(P)$. During computation, \ccc{tco.ellipse} equals the ellipse $me$
|
|
appearing in the pseudo-code for $\me(P,B)$, see Section~\ref{sec:algo}.
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{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 ellipse $me(P)$ from a point set
|
|
$P$, given by a begin iterator and a past-the-end iterator, realized
|
|
as a member template.
|
|
|
|
All 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 $me$ is called to compute
|
|
$me(P)=me(P,\emptyset)$.
|
|
|
|
@macro <Min_ellipse_2 constructors> += @begin
|
|
// STL-like constructor (member template)
|
|
template < class InputIterator >
|
|
Min_ellipse_2( InputIterator first,
|
|
InputIterator last,
|
|
bool randomize
|
|
#if !defined(_MSC_VER) || _MSC_VER > 1300
|
|
= false
|
|
#endif
|
|
,
|
|
Random& random = default_random,
|
|
const Traits& traits = Traits())
|
|
: tco( traits)
|
|
{
|
|
// allocate support points' array
|
|
support_points = new Point[ 5];
|
|
|
|
// range of points not empty?
|
|
if ( first != last) {
|
|
|
|
// store points
|
|
if ( randomize) {
|
|
|
|
// shuffle points at random
|
|
std::vector<Point> v( first, last);
|
|
std::random_shuffle( v.begin(), v.end(), random);
|
|
std::copy( v.begin(), v.end(),
|
|
std::back_inserter( points)); }
|
|
else
|
|
std::copy( first, last, std::back_inserter( points)); }
|
|
|
|
// compute me
|
|
me( points.end(), 0);
|
|
}
|
|
@end
|
|
|
|
The remaining constructors are actually specializations of the previous
|
|
ones, building the smallest enclosing ellipse for up to five points.
|
|
The idea is the following: recall that for any point set $P$ there
|
|
exists $S \subseteq P$, $|S| \leq 5$ with $me(S) = me(P)$ (in fact,
|
|
such a set $S$ is determined by the algorithm). Once $S$ has been
|
|
computed (or given otherwise), $me(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 5.
|
|
For $|S|=0$, we get the default constructor, building $me(\emptyset)$.
|
|
|
|
@macro <Min_ellipse_2 constructors> += @begin
|
|
|
|
// default constructor
|
|
inline
|
|
Min_ellipse_2( const Traits& traits = Traits())
|
|
: tco( traits), n_support_points( 0)
|
|
{
|
|
// allocate support points' array
|
|
support_points = new Point[ 5];
|
|
|
|
// initialize ellipse
|
|
tco.ellipse.set();
|
|
|
|
CGAL_optimisation_postcondition( is_empty());
|
|
}
|
|
|
|
// constructor for one point
|
|
inline
|
|
Min_ellipse_2( const Point& p, const Traits& traits = Traits())
|
|
: tco( traits), points( 1, p), n_support_points( 1)
|
|
{
|
|
// allocate support points' array
|
|
support_points = new Point[ 5];
|
|
|
|
// initialize ellipse
|
|
support_points[ 0] = p;
|
|
tco.ellipse.set( p);
|
|
|
|
CGAL_optimisation_postcondition( is_degenerate());
|
|
}
|
|
|
|
// constructor for two points
|
|
inline
|
|
Min_ellipse_2( const Point& p1, const Point& p2,
|
|
const Traits& traits = Traits())
|
|
: tco( traits)
|
|
{
|
|
// allocate support points' array
|
|
support_points = new Point[ 5];
|
|
|
|
// store points
|
|
points.push_back( p1);
|
|
points.push_back( p2);
|
|
|
|
// compute me
|
|
me( points.end(), 0);
|
|
|
|
CGAL_optimisation_postcondition( is_degenerate());
|
|
}
|
|
|
|
// constructor for three points
|
|
inline
|
|
Min_ellipse_2( const Point& p1, const Point& p2, const Point& p3,
|
|
const Traits& traits = Traits())
|
|
: tco( traits)
|
|
{
|
|
// allocate support points' array
|
|
support_points = new Point[ 5];
|
|
|
|
// store points
|
|
points.push_back( p1);
|
|
points.push_back( p2);
|
|
points.push_back( p3);
|
|
|
|
// compute me
|
|
me( points.end(), 0);
|
|
}
|
|
|
|
// constructor for four points
|
|
inline
|
|
Min_ellipse_2( const Point& p1, const Point& p2,
|
|
const Point& p3, const Point& p4,
|
|
const Traits& traits = Traits())
|
|
: tco( traits)
|
|
{
|
|
// allocate support points' array
|
|
support_points = new Point[ 5];
|
|
|
|
// store points
|
|
points.push_back( p1);
|
|
points.push_back( p2);
|
|
points.push_back( p3);
|
|
points.push_back( p4);
|
|
|
|
// compute me
|
|
me( points.end(), 0);
|
|
}
|
|
|
|
// constructor for five points
|
|
inline
|
|
Min_ellipse_2( const Point& p1, const Point& p2, const Point& p3,
|
|
const Point& p4, const Point& p5,
|
|
const Traits& traits = Traits())
|
|
: tco( traits)
|
|
{
|
|
// allocate support points' array
|
|
support_points = new Point[ 5];
|
|
|
|
// store points
|
|
points.push_back( p1);
|
|
points.push_back( p2);
|
|
points.push_back( p3);
|
|
points.push_back( p4);
|
|
points.push_back( p5);
|
|
|
|
// compute me
|
|
me( points.end(), 0);
|
|
}
|
|
@end
|
|
|
|
The destructor only frees the memory of the support points' array.
|
|
|
|
@macro <Min_ellipse_2 destructor> = @begin
|
|
inline
|
|
~Min_ellipse_2( )
|
|
{
|
|
// free support points' array
|
|
delete[] support_points;
|
|
}
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Access Functions}
|
|
|
|
These functions are used to retrieve information about the current
|
|
status of the \ccc{Min_ellipse_2<Traits>} object. They are all very
|
|
simple (and therefore \ccc{inline}) and mostly rely on corresponding
|
|
access functions of the data members of \ccc{Min_ellipse_2<Traits>}.
|
|
|
|
First, we define the \ccc{number_of_...} methods.
|
|
|
|
@macro <Min_ellipse_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_ellipse_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
|
|
const Point&
|
|
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{ellipse}.
|
|
|
|
@macro <Min_ellipse_2 access functions> += @begin
|
|
// ellipse
|
|
inline
|
|
const Ellipse&
|
|
ellipse( ) const
|
|
{
|
|
return( tco.ellipse);
|
|
}
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{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_ellipse_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() < 3);
|
|
}
|
|
@end
|
|
|
|
The remaining predicates perform in-ellipse tests, based on the
|
|
corresponding predicates of class \ccc{Ellipse}.
|
|
|
|
@macro <Min_ellipse_2 predicates> = @begin
|
|
// in-ellipse test predicates
|
|
inline
|
|
CGAL::Bounded_side
|
|
bounded_side( const Point& p) const
|
|
{
|
|
return( tco.ellipse.bounded_side( p));
|
|
}
|
|
|
|
inline
|
|
bool
|
|
has_on_bounded_side( const Point& p) const
|
|
{
|
|
return( tco.ellipse.has_on_bounded_side( p));
|
|
}
|
|
|
|
inline
|
|
bool
|
|
has_on_boundary( const Point& p) const
|
|
{
|
|
return( tco.ellipse.has_on_boundary( p));
|
|
}
|
|
|
|
inline
|
|
bool
|
|
has_on_unbounded_side( const Point& p) const
|
|
{
|
|
return( tco.ellipse.has_on_unbounded_side( p));
|
|
}
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Modifiers}
|
|
|
|
There is another way to build up $me(P)$, other than by supplying
|
|
the point set $P$ at once. Namely, $me(P)$ can be built up
|
|
incrementally, adding one point after another. If you look at the
|
|
pseudo-code in the introduction, this comes quite naturally. The
|
|
modifying method \ccc{insert}, applied with point $p$ to a
|
|
\ccc{Min_ellipse_2<Traits>} object representing $me(P)$,
|
|
computes $me(P \cup \{p\})$, where work has to be done only if $p$
|
|
lies outside $me(P)$. In this case, $me(P \cup \{p\}) = me(P,\{p\})$
|
|
holds, so the private member function \ccc{me} 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 pseudo-code in
|
|
Section~\ref{sec:algo}.
|
|
|
|
@macro <Min_ellipse_2 modifiers> += @begin
|
|
void
|
|
insert( const Point& p)
|
|
{
|
|
// p not in current ellipse?
|
|
if ( has_on_unbounded_side( p)) {
|
|
|
|
// p new support point
|
|
support_points[ 0] = p;
|
|
|
|
// recompute me
|
|
me( 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
|
|
|
|
Inserting a range of points is done by a single member template. In case
|
|
a compiler does not support this yet, we provide specialized \ccc{insert}
|
|
functions for C~arrays (using pointers as iterators), for STL sequence
|
|
containers \ccc{vector<Point>} and \ccc{list<Point>} and for the STL
|
|
input stream iterator \ccc{istream_iterator<Point>}. Actually, the
|
|
\ccc{insert} function 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*}.
|
|
|
|
The following \ccc{insert} functions perform a call \ccc{insert(p)} for
|
|
each point \ccc{p} in the range $[\mbox{\ccc{first}},\mbox{\ccc{last}})$.
|
|
|
|
@macro <Min_ellipse_2 modifiers> += @begin
|
|
template < class InputIterator >
|
|
void
|
|
insert( InputIterator first, InputIterator last)
|
|
{
|
|
for ( ; first != last; ++first)
|
|
insert( *first);
|
|
}
|
|
@end
|
|
|
|
The member function \ccc{clear} deletes all points from a
|
|
\ccc{Min_ellipse_2<Traits>} object and resets it to the
|
|
empty ellipse.
|
|
|
|
@macro <Min_ellipse_2 modifiers> += @begin
|
|
void
|
|
clear( )
|
|
{
|
|
points.erase( points.begin(), points.end());
|
|
n_support_points = 0;
|
|
tco.ellipse.set();
|
|
}
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Validity Check}
|
|
|
|
A \ccc{Min_ellipse_2<Traits>} object can be checked for validity. This
|
|
means, it is checked whether (a) the ellipse contains all points of its
|
|
defining set $P$, (b) the ellipse is the smallest ellipse spanned by its
|
|
support set, and (c) the support set is minimal, i.e.\ no support point is
|
|
redundant. (\emph{Note:} (b) and (c) are not yet implemented. Instead we
|
|
check if the support set lies on the boundary of the ellipse.) 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_ellipse_2 validity check> = @begin
|
|
bool
|
|
is_valid( bool verbose = false, int level = 0) const
|
|
{
|
|
using namespace std;
|
|
|
|
CGAL::Verbose_ostream verr( verbose);
|
|
verr << endl;
|
|
verr << "CGAL::Min_ellipse_2<Traits>::" << endl;
|
|
verr << "is_valid( true, " << level << "):" << endl;
|
|
verr << " |P| = " << number_of_points()
|
|
<< ", |S| = " << number_of_support_points() << endl;
|
|
|
|
// containment check (a)
|
|
@<Min_ellipse_2 containment check>
|
|
|
|
// support set checks (b)+(c) (not yet implemented)
|
|
@!<Min_ellipse_2 support set checks>
|
|
|
|
// alternative support set check
|
|
@<Min_ellipse_2 support set check>
|
|
|
|
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_ellipse_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,
|
|
"ellipse does not contain all points"));
|
|
verr << "passed." << endl;
|
|
@end
|
|
|
|
The alternative support set check is easy to perform, just a loop over all
|
|
support points in \ccc{support_points}.
|
|
|
|
@macro <Min_ellipse_2 support set check> = @begin
|
|
verr << " +) support set check..." << flush;
|
|
Support_point_iterator support_point_iter;
|
|
for ( support_point_iter = support_points_begin();
|
|
support_point_iter != support_points_end();
|
|
++support_point_iter)
|
|
if ( ! has_on_boundary( *support_point_iter))
|
|
return( CGAL::_optimisation_is_valid_fail( verr,
|
|
"ellipse does not have all \
|
|
support points on the boundary"));
|
|
verr << "passed." << endl;
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Miscellaneous}
|
|
|
|
The member function \ccc{traits} returns a const reference to the
|
|
traits class object.
|
|
|
|
@macro <Min_ellipse_2 miscellaneous> = @begin
|
|
inline
|
|
const Traits&
|
|
traits( ) const
|
|
{
|
|
return( tco);
|
|
}
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{I/O}
|
|
|
|
@macro <Min_ellipse_2 I/O operators declaration> = @begin
|
|
template < class Traits_ >
|
|
std::ostream&
|
|
operator << ( std::ostream& os, const Min_ellipse_2<Traits_>& me);
|
|
|
|
template < class Traits_ >
|
|
std::istream&
|
|
operator >> ( std::istream& is, Min_ellipse_2<Traits_>& me);
|
|
@end
|
|
|
|
@macro <Min_ellipse_2 I/O operators> = @begin
|
|
template < class Traits_ >
|
|
std::ostream&
|
|
operator << ( std::ostream& os,
|
|
const Min_ellipse_2<Traits_>& min_ellipse)
|
|
{
|
|
using namespace std;
|
|
|
|
typedef Min_ellipse_2<Traits_>::Point Point;
|
|
typedef ostream_iterator<Point> Os_it;
|
|
|
|
switch ( CGAL::get_mode( os)) {
|
|
|
|
case CGAL::IO::PRETTY:
|
|
os << endl;
|
|
os << "CGAL::Min_ellipse_2( |P| = "<<min_ellipse.number_of_points()
|
|
<< ", |S| = " << min_ellipse.number_of_support_points() << endl;
|
|
os << " P = {" << endl;
|
|
os << " ";
|
|
copy( min_ellipse.points_begin(), min_ellipse.points_end(),
|
|
Os_it( os, ",\n "));
|
|
os << "}" << endl;
|
|
os << " S = {" << endl;
|
|
os << " ";
|
|
copy( min_ellipse.support_points_begin(),
|
|
min_ellipse.support_points_end(),
|
|
Os_it( os, ",\n "));
|
|
os << "}" << endl;
|
|
os << " ellipse = " << min_ellipse.ellipse() << endl;
|
|
os << ")" << endl;
|
|
break;
|
|
|
|
case CGAL::IO::ASCII:
|
|
copy( min_ellipse.points_begin(), min_ellipse.points_end(),
|
|
Os_it( os, "\n"));
|
|
break;
|
|
|
|
case CGAL::IO::BINARY:
|
|
copy( min_ellipse.points_begin(), min_ellipse.points_end(),
|
|
Os_it( os));
|
|
break;
|
|
|
|
default:
|
|
CGAL_optimisation_assertion_msg( false,
|
|
"CGAL::get_mode( os) invalid!");
|
|
break; }
|
|
|
|
return( os);
|
|
}
|
|
|
|
template < class Traits_ >
|
|
std::istream&
|
|
operator >> ( std::istream& is, CGAL::Min_ellipse_2<Traits_>& min_ellipse)
|
|
{
|
|
using namespace std;
|
|
|
|
switch ( CGAL::get_mode( is)) {
|
|
|
|
case CGAL::IO::PRETTY:
|
|
cerr << endl;
|
|
cerr << "Stream must be in ascii or binary mode" << endl;
|
|
break;
|
|
|
|
case CGAL::IO::ASCII:
|
|
case CGAL::IO::BINARY:
|
|
typedef Min_ellipse_2<Traits_>::Point Point;
|
|
typedef istream_iterator<Point> Is_it;
|
|
min_ellipse.clear();
|
|
min_ellipse.insert( Is_it( is), Is_it());
|
|
break;
|
|
|
|
default:
|
|
CGAL_optimisation_assertion_msg( false, "CGAL::IO::mode invalid!");
|
|
break; }
|
|
|
|
return( is);
|
|
}
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Graphical Output}
|
|
|
|
@macro<Min_ellipse_2 graphical output operator> = @begin
|
|
#ifdef CGAL_MIN_ELLIPSE_2_H
|
|
#ifndef CGAL_IO_WINDOW_STREAM_MIN_ELLIPSE_2
|
|
#define CGAL_IO_WINDOW_STREAM_MIN_ELLIPSE_2
|
|
|
|
template< class Traits_ >
|
|
CGAL::Window_stream&
|
|
operator << ( CGAL::Window_stream &ws,
|
|
const CGAL::Min_ellipse_2<Traits_>& min_ellipse)
|
|
{
|
|
typedef CGAL::Min_ellipse_2<Traits_>::Point_iterator Point_iterator;
|
|
|
|
Point_iterator first( min_ellipse.points_begin());
|
|
Point_iterator last ( min_ellipse.points_end());
|
|
for ( ; first != last; ++first)
|
|
ws << *first;
|
|
return( ws << min_ellipse.ellipse());
|
|
}
|
|
#endif // CGAL_IO_WINDOW_STREAM_MIN_ELLIPSE_2
|
|
#endif // CGAL_MIN_ELLIPSE_2_H
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Private Member Function {\ccFont compute\_ellipse}}
|
|
|
|
This is the method for computing the basic case $\me(\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 5$.
|
|
|
|
@macro <Min_ellipse_2 private member function `compute_ellipse'> = @begin
|
|
// compute_ellipse
|
|
inline
|
|
void
|
|
compute_ellipse( )
|
|
{
|
|
switch ( n_support_points) {
|
|
case 5:
|
|
tco.ellipse.set( support_points[ 0],
|
|
support_points[ 1],
|
|
support_points[ 2],
|
|
support_points[ 3],
|
|
support_points[ 4]);
|
|
break;
|
|
case 4:
|
|
tco.ellipse.set( support_points[ 0],
|
|
support_points[ 1],
|
|
support_points[ 2],
|
|
support_points[ 3]);
|
|
break;
|
|
case 3:
|
|
tco.ellipse.set( support_points[ 0],
|
|
support_points[ 1],
|
|
support_points[ 2]);
|
|
break;
|
|
case 2:
|
|
tco.ellipse.set( support_points[ 0], support_points[ 1]);
|
|
break;
|
|
case 1:
|
|
tco.ellipse.set( support_points[ 0]);
|
|
break;
|
|
case 0:
|
|
tco.ellipse.set( );
|
|
break;
|
|
default:
|
|
CGAL_optimisation_assertion( ( n_support_points >= 0) &&
|
|
( n_support_points <= 5) ); }
|
|
}
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Private Member Function {\ccFont me}}
|
|
|
|
This function computes the general ellipse $me(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 modeled after the
|
|
pseudo-code above.
|
|
|
|
@macro <Min_ellipse_2 private member function `me'> = @begin
|
|
void
|
|
me( const Point_iterator& last, int n_sp)
|
|
{
|
|
// compute ellipse through support points
|
|
n_support_points = n_sp;
|
|
compute_ellipse();
|
|
if ( n_sp == 5) return;
|
|
|
|
// test first n points
|
|
typename std::list<Point>::iterator point_iter = points.begin();
|
|
for ( ; last != point_iter; ) {
|
|
const Point& p = *point_iter;
|
|
|
|
// p not in current ellipse?
|
|
if ( has_on_unbounded_side( p)) {
|
|
|
|
// recursive call with p as additional support point
|
|
support_points[ n_sp] = p;
|
|
me( point_iter, n_sp+1);
|
|
|
|
// move current point to front
|
|
points.splice( points.begin(), points, point_iter++); }
|
|
|
|
else
|
|
++point_iter; }
|
|
}
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
@! Class template Optimisation_ellipse_2<K>
|
|
@! ----------------------------------------------------------------------------
|
|
|
|
\subsection{Class template \ccFont Optimisation\_ellipse\_2<K>}
|
|
|
|
First, we declare the class template \ccc{Optimisation_ellipse_2},
|
|
|
|
@macro<Optimisation_ellipse_2 declaration> = @begin
|
|
template < class K_ >
|
|
class Optimisation_ellipse_2;
|
|
@end
|
|
|
|
The class interface looks as follows.
|
|
|
|
@macro <Optimisation_ellipse_2 interface> = @begin
|
|
template < class K_ >
|
|
class Optimisation_ellipse_2 {
|
|
/*
|
|
friend std::ostream& operator << <> (
|
|
std::ostream&, const Optimisation_ellipse_2<K_>&);
|
|
friend std::istream& operator >> <> (
|
|
std::istream&, Optimisation_ellipse_2<K_> &);
|
|
friend CGAL::Window_stream& operator << <> (
|
|
CGAL::Window_stream&, const Optimisation_ellipse_2<K_>&);
|
|
*/
|
|
public:
|
|
@<Optimisation_ellipse_2 public interface>
|
|
|
|
/* private: */
|
|
// private data members
|
|
@<Optimisation_ellipse_2 private data members>
|
|
|
|
@<dividing line>
|
|
|
|
// Class implementation
|
|
// ====================
|
|
|
|
public:
|
|
// Constructor
|
|
// -----------
|
|
@<Optimisation_ellipse_2 constructor>
|
|
|
|
// Set functions
|
|
// -------------
|
|
@<Optimisation_ellipse_2 set functions>
|
|
|
|
// Access functions
|
|
// ----------------
|
|
@<Optimisation_ellipse_2 access functions>
|
|
|
|
// Equality tests
|
|
// --------------
|
|
@<Optimisation_ellipse_2 equality tests>
|
|
|
|
// Predicates
|
|
// ----------
|
|
@<Optimisation_ellipse_2 predicates>
|
|
};
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Public Interface}
|
|
|
|
The functionality is described and documented in the specification
|
|
section, so we do not comment on it here.
|
|
|
|
@macro <Optimisation_ellipse_2 public interface> = @begin
|
|
// types
|
|
typedef K_ K;
|
|
typedef typename K_::RT RT;
|
|
typedef typename K_::FT FT;
|
|
typedef CGAL::Point_2<K> Point;
|
|
typedef CGAL::Conic_2<K> Conic;
|
|
|
|
/**************************************************************************
|
|
WORKAROUND: Some compilers are unable to match member functions defined
|
|
outside the class template. Therefore, all member functions are implemented
|
|
in the class interface.
|
|
|
|
// creation
|
|
Optimisation_ellipse_2( );
|
|
|
|
void set( );
|
|
void set( const Point& p);
|
|
void set( const Point& p, const Point& q);
|
|
void set( const Point& p1, const Point& p2, const Point& p3);
|
|
void set( const Point& p1, const Point& p2,
|
|
const Point& p3, const Point& p4);
|
|
void set( const Point& p1, const Point& p2,
|
|
const Point& p3, const Point& p4, const Point& p5);
|
|
|
|
// access functions
|
|
int number_of_boundary_points()
|
|
|
|
// equality tests
|
|
bool operator == ( const Optimisation_ellipse_2<K>& e) const;
|
|
bool operator != ( const Optimisation_ellipse_2<K>& e) const;
|
|
|
|
// predicates
|
|
CGAL::Bounded_side bounded_side( const Point& p) const;
|
|
bool has_on_bounded_side ( const Point& p) const;
|
|
bool has_on_boundary ( const Point& p) const;
|
|
bool has_on_unbounded_side ( const Point& p) const;
|
|
|
|
bool is_empty ( ) const;
|
|
bool is_degenerate( ) const;
|
|
**************************************************************************/
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Private Data Members}
|
|
|
|
The representation of the ellipse depends on the number of given
|
|
boundary points, stored in \ccc{n_boundary_points}.
|
|
|
|
@macro <Optimisation_ellipse_2 private data members> += @begin
|
|
int n_boundary_points; // number of boundary points
|
|
@end
|
|
|
|
In the degenerate cases with zero to two boundary points, the given
|
|
points, if any, are stored directly in \ccc{boundary_point1} and
|
|
\ccc{boundary_point2}, resp.
|
|
|
|
@macro <Optimisation_ellipse_2 private data members> += @begin
|
|
Point boundary_point1, boundary_point2; // two boundary points
|
|
@end
|
|
|
|
Given three or five points, the ellipse is represented as a conic,
|
|
using the class \ccc{Conic_2<K>}. The case with four boundary
|
|
points is the most complicated one, since in general a direct
|
|
representation with one conic has irrational
|
|
coordinates~\cite{gs-seefe-97a}. Therefore the ellipse is represented
|
|
implicitly as a linear combination of two conics.
|
|
|
|
@macro <Optimisation_ellipse_2 private data members> += @begin
|
|
Conic conic1, conic2; // two conics
|
|
@end
|
|
|
|
Finally, in the case of four boundary points, we need the gradient
|
|
vector of the linear combination for the volume derivative in the
|
|
in-ellipse test.
|
|
|
|
@macro <Optimisation_ellipse_2 private data members> += @begin
|
|
RT dr, ds, dt, du, dv, dw; // the gradient vector
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Constructor}
|
|
|
|
Only a default constructor is needed.
|
|
|
|
@macro <Optimisation_ellipse_2 constructor> = @begin
|
|
inline
|
|
Optimisation_ellipse_2( )
|
|
{ }
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Set Functions}
|
|
|
|
We provide set functions taking zero, one, two, three, four or five
|
|
boundary points. They all set the variable to the smallest ellipse
|
|
through the given points. \emph{Note:} The set function taking five
|
|
boundary points only uses the fifth point from its input together with
|
|
the two internally represented conics to compute the ellipse. The
|
|
algorithm in Section~\ref{sec:algo} guarantees that this set function
|
|
is only called if the current ellipse already has the first four
|
|
points as its boundary points.
|
|
|
|
@macro <Optimisation_ellipse_2 set functions> = @begin
|
|
inline
|
|
void
|
|
set( )
|
|
{
|
|
n_boundary_points = 0;
|
|
}
|
|
|
|
inline
|
|
void
|
|
set( const Point& p)
|
|
{
|
|
n_boundary_points = 1;
|
|
boundary_point1 = p;
|
|
}
|
|
|
|
inline
|
|
void
|
|
set( const Point& p, const Point& q)
|
|
{
|
|
n_boundary_points = 2;
|
|
boundary_point1 = p;
|
|
boundary_point2 = q;
|
|
}
|
|
|
|
inline
|
|
void
|
|
set( const Point& p1, const Point& p2, const Point& p3)
|
|
{
|
|
n_boundary_points = 3;
|
|
conic1.set_ellipse( p1, p2, p3);
|
|
}
|
|
|
|
inline
|
|
void
|
|
set( const Point& p1, const Point& p2, const Point& p3, const Point& p4)
|
|
{
|
|
n_boundary_points = 4;
|
|
Conic::set_two_linepairs( p1, p2, p3, p4, conic1, conic2);
|
|
dr = RT( 0);
|
|
ds = conic1.r() * conic2.s() - conic2.r() * conic1.s(),
|
|
dt = conic1.r() * conic2.t() - conic2.r() * conic1.t(),
|
|
du = conic1.r() * conic2.u() - conic2.r() * conic1.u(),
|
|
dv = conic1.r() * conic2.v() - conic2.r() * conic1.v(),
|
|
dw = conic1.r() * conic2.w() - conic2.r() * conic1.w();
|
|
}
|
|
|
|
inline
|
|
void
|
|
set( const Point&, const Point&,
|
|
const Point&, const Point&, const Point& p5)
|
|
{
|
|
n_boundary_points = 5;
|
|
conic1.set( conic1, conic2, p5);
|
|
conic1.analyse();
|
|
}
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Access Functions}
|
|
|
|
@macro <Optimisation_ellipse_2 access functions> = @begin
|
|
inline
|
|
int
|
|
number_of_boundary_points( ) const
|
|
{
|
|
return( n_boundary_points);
|
|
}
|
|
|
|
Conic_2< Cartesian< double > >
|
|
to_double( ) const
|
|
{
|
|
CGAL_optimisation_precondition( ! is_degenerate());
|
|
|
|
double t = 0.0;
|
|
|
|
if ( n_boundary_points == 4)
|
|
t = conic1.vol_minimum( dr, ds, dt, du, dv, dw);
|
|
|
|
Conic_2<K> c( conic1);
|
|
Conic_2< Cartesian<double> > e;
|
|
e.set( CGAL::to_double( c.r()) + t*CGAL::to_double( dr),
|
|
CGAL::to_double( c.s()) + t*CGAL::to_double( ds),
|
|
CGAL::to_double( c.t()) + t*CGAL::to_double( dt),
|
|
CGAL::to_double( c.u()) + t*CGAL::to_double( du),
|
|
CGAL::to_double( c.v()) + t*CGAL::to_double( dv),
|
|
CGAL::to_double( c.w()) + t*CGAL::to_double( dw));
|
|
|
|
return( e);
|
|
}
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Equality Tests}
|
|
|
|
@macro <Optimisation_ellipse_2 equality tests> = @begin
|
|
bool
|
|
operator == ( const Optimisation_ellipse_2<K>& e) const
|
|
{
|
|
if ( n_boundary_points != e.n_boundary_points)
|
|
return( false);
|
|
|
|
switch ( n_boundary_points) {
|
|
case 0:
|
|
return( true);
|
|
case 1:
|
|
return( boundary_point1 == e.boundary_point1);
|
|
case 2:
|
|
return( ( ( boundary_point1 == e.boundary_point1)
|
|
&& ( boundary_point2 == e.boundary_point2))
|
|
|| ( ( boundary_point1 == e.boundary_point2)
|
|
&& ( boundary_point2 == e.boundary_point1)));
|
|
case 3:
|
|
case 5:
|
|
return( conic1 == e.conic1);
|
|
case 4:
|
|
return( ( ( conic1 == e.conic1)
|
|
&& ( conic2 == e.conic2))
|
|
|| ( ( conic1 == e.conic2)
|
|
&& ( conic2 == e.conic1)));
|
|
default:
|
|
CGAL_optimisation_assertion( ( n_boundary_points >= 0)
|
|
&& ( n_boundary_points <= 5)); }
|
|
// keeps g++ happy
|
|
return( false);
|
|
}
|
|
|
|
inline
|
|
bool
|
|
operator != ( const Optimisation_ellipse_2<K>& e) const
|
|
{
|
|
return( ! operator == ( e));
|
|
}
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Predicates}
|
|
|
|
The following predicates perform in-ellipse tests and check for
|
|
emptiness and degeneracy, resp. The way to evaluate the in-ellipse
|
|
test depends on the number of boundary points and is realised by a
|
|
case analysis. Again, the case with four points is the most difficult
|
|
one.
|
|
|
|
@macro <Optimisation_ellipse_2 predicates> = @begin
|
|
inline
|
|
CGAL::Bounded_side
|
|
bounded_side( const Point& p) const
|
|
{
|
|
switch ( n_boundary_points) {
|
|
case 0:
|
|
return( CGAL::ON_UNBOUNDED_SIDE);
|
|
case 1:
|
|
return( ( p == boundary_point1) ?
|
|
CGAL::ON_BOUNDARY : CGAL::ON_UNBOUNDED_SIDE);
|
|
case 2:
|
|
return( ( p == boundary_point1)
|
|
|| ( p == boundary_point2)
|
|
|| ( CGAL::are_ordered_along_line(
|
|
boundary_point1, p, boundary_point2)) ?
|
|
CGAL::ON_BOUNDARY : CGAL::ON_UNBOUNDED_SIDE);
|
|
case 3:
|
|
case 5:
|
|
return( conic1.convex_side( p));
|
|
case 4: {
|
|
Conic c;
|
|
c.set( conic1, conic2, p);
|
|
c.analyse();
|
|
if ( ! c.is_ellipse()) {
|
|
c.set_ellipse( conic1, conic2);
|
|
c.analyse();
|
|
return( c.convex_side( p)); }
|
|
else {
|
|
int tau_star = c.vol_derivative( dr, ds, dt, du, dv, dw);
|
|
return( CGAL::Bounded_side( CGAL_NTS sign( tau_star))); } }
|
|
default:
|
|
CGAL_optimisation_assertion( ( n_boundary_points >= 0) &&
|
|
( n_boundary_points <= 5) ); }
|
|
// keeps g++ happy
|
|
return( CGAL::Bounded_side( 0));
|
|
}
|
|
|
|
inline
|
|
bool
|
|
has_on_bounded_side( const Point& p) const
|
|
{
|
|
return( bounded_side( p) == CGAL::ON_BOUNDED_SIDE);
|
|
}
|
|
|
|
inline
|
|
bool
|
|
has_on_boundary( const Point& p) const
|
|
{
|
|
return( bounded_side( p) == CGAL::ON_BOUNDARY);
|
|
}
|
|
|
|
inline
|
|
bool
|
|
has_on_unbounded_side( const Point& p) const
|
|
{
|
|
return( bounded_side( p) == CGAL::ON_UNBOUNDED_SIDE);
|
|
}
|
|
|
|
inline
|
|
bool
|
|
is_empty( ) const
|
|
{
|
|
return( n_boundary_points == 0);
|
|
}
|
|
|
|
inline
|
|
bool
|
|
is_degenerate( ) const
|
|
{
|
|
return( n_boundary_points < 3);
|
|
}
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{I/O}
|
|
|
|
@macro <Optimisation_ellipse_2 I/O operators declaration> = @begin
|
|
template < class K_ >
|
|
std::ostream&
|
|
operator << ( std::ostream&, const CGAL::Optimisation_ellipse_2<K_>&);
|
|
|
|
template < class K_ >
|
|
std::istream&
|
|
operator >> ( std::istream&, CGAL::Optimisation_ellipse_2<K_>&);
|
|
@end
|
|
|
|
@macro <Optimisation_ellipse_2 I/O operators> = @begin
|
|
template < class K_ >
|
|
std::ostream&
|
|
operator << ( std::ostream& os, const CGAL::Optimisation_ellipse_2<K_>& e)
|
|
{
|
|
const char* const empty = "";
|
|
const char* const pretty_head = "CGAL::Optimisation_ellipse_2( ";
|
|
const char* const pretty_sep = ", ";
|
|
const char* const pretty_tail = ")";
|
|
const char* const ascii_sep = " ";
|
|
|
|
const char* head = empty;
|
|
const char* sep = empty;
|
|
const char* tail = empty;
|
|
|
|
switch ( CGAL::get_mode( os)) {
|
|
case CGAL::IO::PRETTY:
|
|
head = pretty_head;
|
|
sep = pretty_sep;
|
|
tail = pretty_tail;
|
|
break;
|
|
case CGAL::IO::ASCII:
|
|
sep = ascii_sep;
|
|
break;
|
|
case CGAL::IO::BINARY:
|
|
break;
|
|
default:
|
|
CGAL_optimisation_assertion_msg( false,
|
|
"CGAL::get_mode( os) invalid!");
|
|
break; }
|
|
|
|
os << head << e.n_boundary_points;
|
|
switch ( e.n_boundary_points) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
os << sep << e.boundary_point1;
|
|
break;
|
|
case 2:
|
|
os << sep << e.boundary_point1
|
|
<< sep << e.boundary_point2;
|
|
break;
|
|
case 3:
|
|
case 5:
|
|
os << sep << e.conic1;
|
|
break;
|
|
case 4:
|
|
os << sep << e.conic1
|
|
<< sep << e.conic2;
|
|
break; }
|
|
os << tail;
|
|
|
|
return( os);
|
|
}
|
|
|
|
template < class K_ >
|
|
std::istream&
|
|
operator >> ( std::istream& is, CGAL::Optimisation_ellipse_2<K_>& e)
|
|
{
|
|
switch ( CGAL::get_mode( is)) {
|
|
|
|
case CGAL::IO::PRETTY:
|
|
cerr << std::endl;
|
|
cerr << "Stream must be in ascii or binary mode" << std::endl;
|
|
break;
|
|
|
|
case CGAL::IO::ASCII:
|
|
case CGAL::IO::BINARY:
|
|
CGAL::read( is, e.n_boundary_points);
|
|
switch ( e.n_boundary_points) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
is >> e.boundary_point1;
|
|
break;
|
|
case 2:
|
|
is >> e.boundary_point1
|
|
>> e.boundary_point2;
|
|
break;
|
|
case 3:
|
|
case 5:
|
|
is >> e.conic1;
|
|
break;
|
|
case 4:
|
|
is >> e.conic1
|
|
>> e.conic2;
|
|
break; }
|
|
break;
|
|
|
|
default:
|
|
CGAL_optimisation_assertion_msg( false,
|
|
"CGAL::get_mode( is) invalid!");
|
|
break; }
|
|
|
|
return( is);
|
|
}
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Graphical Output}
|
|
|
|
@macro<Optimisation_ellipse_2 graphical output operator> = @begin
|
|
#ifdef CGAL_OPTIMISATION_ELLIPSE_2_H
|
|
#ifndef CGAL_IO_WINDOW_STREAM_OPTIMISATION_ELLIPSE_2
|
|
#define CGAL_IO_WINDOW_STREAM_OPTIMISATION_ELLIPSE_2
|
|
|
|
template< class Traits_ >
|
|
CGAL::Window_stream&
|
|
operator << ( CGAL::Window_stream &ws,
|
|
const CGAL::Optimisation_ellipse_2<Traits_>& oe)
|
|
{
|
|
switch ( oe.n_boundary_points) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
ws << oe.boundary_point1;
|
|
break;
|
|
case 2: {
|
|
double px1( CGAL::to_double( oe.boundary_point1.x()));
|
|
double py1( CGAL::to_double( oe.boundary_point1.y()));
|
|
double px2( CGAL::to_double( oe.boundary_point2.x()));
|
|
double py2( CGAL::to_double( oe.boundary_point2.y()));
|
|
ws.draw_segment( px1, py1, px2, py2); }
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
ws << oe.to_double();
|
|
break;
|
|
default:
|
|
CGAL_optimisation_assertion( ( oe.n_boundary_points >= 0) &&
|
|
( oe.n_boundary_points <= 5) ); }
|
|
return( ws);
|
|
}
|
|
|
|
#endif // CGAL_IO_WINDOW_STREAM_OPTIMISATION_ELLIPSE_2
|
|
#endif // CGAL_OPTIMISATION_ELLIPSE_2_H
|
|
@end
|
|
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
@! Class template Min_ellipse_2_traits_2<K>
|
|
@! ----------------------------------------------------------------------------
|
|
|
|
\subsection{Class template \ccFont Min\_ellipse\_2\_traits\_2<K>}
|
|
|
|
First, we declare the class templates \ccc{Min_ellipse_2} and
|
|
\ccc{Min_ellipse_2_traits_2}.
|
|
|
|
@macro<Min_ellipse_2_traits_2 declarations> = @begin
|
|
template < class Traits_ >
|
|
class Min_ellipse_2;
|
|
|
|
template < class K_ >
|
|
class Min_ellipse_2_traits_2;
|
|
@end
|
|
|
|
Since the actual work of the traits class is done in the nested type
|
|
\ccc{Ellipse}, we implement the whole class template in its interface.
|
|
|
|
The variable \ccc{ellipse} containing the current ellipse is declared
|
|
\ccc{private} to disallow the user from directly accessing or modifying
|
|
it. Since the algorithm needs to access and modify the current ellipse,
|
|
it is declared \ccc{friend}.
|
|
|
|
@macro <Min_ellipse_2_traits_2 interface and implementation> = @begin
|
|
template < class K_ >
|
|
class Min_ellipse_2_traits_2 {
|
|
public:
|
|
// types
|
|
typedef K_ K;
|
|
typedef CGAL::Point_2<K> Point;
|
|
typedef CGAL::Optimisation_ellipse_2<K> Ellipse;
|
|
|
|
private:
|
|
// data members
|
|
Ellipse ellipse; // current ellipse
|
|
|
|
// friends
|
|
friend class CGAL::Min_ellipse_2< CGAL::Min_ellipse_2_traits_2<K> >;
|
|
|
|
public:
|
|
// creation (use default implementations)
|
|
// Min_ellipse_2_traits_2( );
|
|
// Min_ellipse_2_traits_2( Min_ellipse_2_traits_2<K> const&);
|
|
};
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
@! Class template Min_ellipse_2_adapterC2<PT,DA>
|
|
@! ----------------------------------------------------------------------------
|
|
|
|
\subsection{Class template \ccFont Min\_ellipse\_2\_adapterC2<PT,DA>}
|
|
|
|
First, we declare the class templates \ccc{Min_ellipse_2},
|
|
\ccc{Min_ellipse_2_adapterC2} and
|
|
\ccc{_Min_ellipse_2_adapterC2__Ellipse}.
|
|
|
|
@macro<Min_ellipse_2_adapterC2 declarations> = @begin
|
|
template < class Traits_ >
|
|
class Min_ellipse_2;
|
|
|
|
template < class PT_, class DA_ >
|
|
class Min_ellipse_2_adapterC2;
|
|
|
|
template < class PT_, class DA_ >
|
|
class _Min_ellipse_2_adapterC2__Ellipse;
|
|
@end
|
|
|
|
The actual work of the adapter is done in the nested class
|
|
\ccc{Ellipse}. Therefore, we implement the whole adapter in its
|
|
interface.
|
|
|
|
The variable \ccc{ellipse} containing the current ellipse is declared
|
|
\ccc{private} to disallow the user from directly accessing or modifying
|
|
it. Since the algorithm needs to access and modify the current ellipse,
|
|
it is declared \ccc{friend}.
|
|
|
|
@macro <Min_ellipse_2_adapterC2 interface and implementation> = @begin
|
|
template < class PT_, class DA_ >
|
|
class Min_ellipse_2_adapterC2 {
|
|
public:
|
|
// types
|
|
typedef PT_ PT;
|
|
typedef DA_ DA;
|
|
|
|
// nested types
|
|
typedef PT Point;
|
|
typedef _Min_ellipse_2_adapterC2__Ellipse<PT,DA> Ellipse;
|
|
|
|
private:
|
|
DA dao; // data accessor object
|
|
Ellipse ellipse; // current ellipse
|
|
friend class Min_ellipse_2< Min_ellipse_2_adapterC2<PT,DA> >;
|
|
|
|
public:
|
|
// creation
|
|
@<Min_ellipse_2_adapterC2 constructors>
|
|
|
|
// operations
|
|
@<Min_ellipse_2_adapterC2 operations>
|
|
};
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Constructors}
|
|
|
|
@macro <Min_ellipse_2_adapterC2 constructors> = @begin
|
|
Min_ellipse_2_adapterC2( const DA& da = DA())
|
|
: dao( da), ellipse( da)
|
|
{ }
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Operations}
|
|
|
|
@macro <Min_ellipse_2_adapterC2 operations> = @begin
|
|
CGAL::Orientation
|
|
orientation( const Point& p, const Point& q, const Point& r) const
|
|
{
|
|
typedef typename DA_::FT FT;
|
|
|
|
FT px;
|
|
FT py;
|
|
FT qx;
|
|
FT qy;
|
|
FT rx;
|
|
FT ry;
|
|
|
|
dao.get( p, px, py);
|
|
dao.get( q, qx, qy);
|
|
dao.get( r, rx, ry);
|
|
|
|
return( static_cast< CGAL::Orientation>(
|
|
CGAL_NTS sign( ( px-rx) * ( qy-ry) - ( py-ry) * ( qx-rx))));
|
|
}
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Nested Type \ccFont Ellipse}
|
|
|
|
@macro <Min_ellipse_2_adapterC2 nested type `Ellipse'> = @begin
|
|
template < class PT_, class DA_ >
|
|
std::ostream& operator << ( std::ostream& os,
|
|
const _Min_ellipse_2_adapterC2__Ellipse<PT_,DA_>& c);
|
|
|
|
template < class PT_, class DA_ >
|
|
std::istream& operator >> ( std::istream& is,
|
|
_Min_ellipse_2_adapterC2__Ellipse<PT_,DA_>& c);
|
|
|
|
template < class PT_, class DA_ >
|
|
class _Min_ellipse_2_adapterC2__Ellipse {
|
|
public:
|
|
// typedefs
|
|
typedef PT_ PT;
|
|
typedef DA_ DA;
|
|
|
|
typedef ConicCPA2< PT, DA> CT;
|
|
typedef typename DA_::FT FT;
|
|
|
|
private:
|
|
// data members
|
|
int n_boundary_points; // number of boundary points
|
|
PT boundary_point1, boundary_point2; // two boundary points
|
|
CT conic1, conic2; // two conics
|
|
FT dr, ds, dt, du, dv, dw; // the gradient vector
|
|
|
|
friend
|
|
std::ostream& operator << <> ( std::ostream& os,
|
|
const _Min_ellipse_2_adapterC2__Ellipse<PT_,DA_>& c);
|
|
|
|
friend
|
|
std::istream& operator >> <> ( std::istream& is,
|
|
_Min_ellipse_2_adapterC2__Ellipse<PT_,DA_>& c);
|
|
|
|
public:
|
|
// types
|
|
typedef PT Point;
|
|
|
|
// creation
|
|
_Min_ellipse_2_adapterC2__Ellipse( const DA& da)
|
|
: conic1( da), conic2( da)
|
|
{ }
|
|
|
|
void
|
|
set( )
|
|
{
|
|
n_boundary_points = 0;
|
|
}
|
|
|
|
void
|
|
set( const Point& p)
|
|
{
|
|
n_boundary_points = 1;
|
|
boundary_point1 = p;
|
|
}
|
|
|
|
void
|
|
set( const Point& p, const Point& q)
|
|
{
|
|
n_boundary_points = 2;
|
|
boundary_point1 = p;
|
|
boundary_point2 = q;
|
|
}
|
|
|
|
void
|
|
set( const Point& p1, const Point& p2, const Point& p3)
|
|
{
|
|
n_boundary_points = 3;
|
|
conic1.set_ellipse( p1, p2, p3);
|
|
}
|
|
|
|
void
|
|
set( const Point& p1, const Point& p2,
|
|
const Point& p3, const Point& p4)
|
|
{
|
|
n_boundary_points = 4;
|
|
CT::set_two_linepairs( p1, p2, p3, p4, conic1, conic2);
|
|
dr = FT( 0);
|
|
ds = conic1.r() * conic2.s() - conic2.r() * conic1.s(),
|
|
dt = conic1.r() * conic2.t() - conic2.r() * conic1.t(),
|
|
du = conic1.r() * conic2.u() - conic2.r() * conic1.u(),
|
|
dv = conic1.r() * conic2.v() - conic2.r() * conic1.v(),
|
|
dw = conic1.r() * conic2.w() - conic2.r() * conic1.w();
|
|
}
|
|
|
|
void
|
|
set( const Point&, const Point&,
|
|
const Point&, const Point&, const Point& p5)
|
|
{
|
|
n_boundary_points = 5;
|
|
conic1.set( conic1, conic2, p5);
|
|
conic1.analyse();
|
|
}
|
|
|
|
// predicates
|
|
CGAL::Bounded_side
|
|
bounded_side( const Point& p) const
|
|
{
|
|
switch ( n_boundary_points) {
|
|
case 0:
|
|
return( CGAL::ON_UNBOUNDED_SIDE);
|
|
case 1:
|
|
return( ( p == boundary_point1) ?
|
|
CGAL::ON_BOUNDARY : CGAL::ON_UNBOUNDED_SIDE);
|
|
case 2:
|
|
return( ( p == boundary_point1)
|
|
|| ( p == boundary_point2)
|
|
|| ( CGAL::are_ordered_along_lineC2( boundary_point1, p,
|
|
boundary_point2, conic1.da())) ?
|
|
CGAL::ON_BOUNDARY : CGAL::ON_UNBOUNDED_SIDE);
|
|
case 3:
|
|
case 5:
|
|
return( conic1.convex_side( p));
|
|
case 4: {
|
|
CT c( conic1.da());
|
|
c.set( conic1, conic2, p);
|
|
c.analyse();
|
|
if ( ! c.is_ellipse()) {
|
|
c.set_ellipse( conic1, conic2);
|
|
c.analyse();
|
|
return( c.convex_side( p)); }
|
|
else {
|
|
int tau_star = c.vol_derivative( dr, ds, dt, du, dv, dw);
|
|
return( CGAL::Bounded_side( CGAL_NTS sign( tau_star))); } }
|
|
default:
|
|
CGAL_optimisation_assertion( ( n_boundary_points >= 0) &&
|
|
( n_boundary_points <= 5) ); }
|
|
// keeps g++ happy
|
|
return( CGAL::Bounded_side( 0));
|
|
}
|
|
|
|
bool
|
|
has_on_bounded_side( const Point& p) const
|
|
{
|
|
return( bounded_side( p) == CGAL::ON_BOUNDED_SIDE);
|
|
}
|
|
|
|
bool
|
|
has_on_boundary( const Point& p) const
|
|
{
|
|
return( bounded_side( p) == CGAL::ON_BOUNDARY);
|
|
}
|
|
|
|
bool
|
|
has_on_unbounded_side( const Point& p) const
|
|
{
|
|
return( bounded_side( p) == CGAL::ON_UNBOUNDED_SIDE);
|
|
}
|
|
|
|
bool
|
|
is_empty( ) const
|
|
{
|
|
return( n_boundary_points == 0);
|
|
}
|
|
|
|
bool
|
|
is_degenerate( ) const
|
|
{
|
|
return( n_boundary_points < 3);
|
|
}
|
|
|
|
// additional operations for checking
|
|
bool
|
|
operator == (
|
|
const CGAL::_Min_ellipse_2_adapterC2__Ellipse<PT_,DA_>& e) const
|
|
{
|
|
if ( n_boundary_points != e.n_boundary_points)
|
|
return( false);
|
|
|
|
switch ( n_boundary_points) {
|
|
case 0:
|
|
return( true);
|
|
case 1:
|
|
return( boundary_point1 == e.boundary_point1);
|
|
case 2:
|
|
return( ( ( boundary_point1 == e.boundary_point1)
|
|
&& ( boundary_point2 == e.boundary_point2))
|
|
|| ( ( boundary_point1 == e.boundary_point2)
|
|
&& ( boundary_point2 == e.boundary_point1)));
|
|
case 3:
|
|
case 5:
|
|
return( conic1 == e.conic1);
|
|
case 4:
|
|
return( ( ( conic1 == e.conic1)
|
|
&& ( conic2 == e.conic2))
|
|
|| ( ( conic1 == e.conic2)
|
|
&& ( conic2 == e.conic1)));
|
|
default:
|
|
CGAL_optimisation_assertion( ( n_boundary_points >= 0)
|
|
&& ( n_boundary_points <= 5)); }
|
|
// keeps g++ happy
|
|
return( false);
|
|
}
|
|
|
|
bool
|
|
operator != (
|
|
const CGAL::_Min_ellipse_2_adapterC2__Ellipse<PT_,DA_>& e) const
|
|
{
|
|
return( ! ( *this == e));
|
|
}
|
|
};
|
|
|
|
// I/O
|
|
template < class PT_, class DA_ >
|
|
std::ostream&
|
|
operator << ( std::ostream& os,
|
|
const CGAL::_Min_ellipse_2_adapterC2__Ellipse<PT_,DA_>& e)
|
|
{
|
|
const char* const empty = "";
|
|
const char* const pretty_head =
|
|
"CGAL::Min_ellipse_2_adapterC2::Ellipse( ";
|
|
const char* const pretty_sep = ", ";
|
|
const char* const pretty_tail = ")";
|
|
const char* const ascii_sep = " ";
|
|
|
|
const char* head = empty;
|
|
const char* sep = empty;
|
|
const char* tail = empty;
|
|
|
|
switch ( CGAL::get_mode( os)) {
|
|
case CGAL::IO::PRETTY:
|
|
head = pretty_head;
|
|
sep = pretty_sep;
|
|
tail = pretty_tail;
|
|
break;
|
|
case CGAL::IO::ASCII:
|
|
sep = ascii_sep;
|
|
break;
|
|
case CGAL::IO::BINARY:
|
|
break;
|
|
default:
|
|
CGAL_optimisation_assertion_msg( false,
|
|
"CGAL::get_mode( os) invalid!");
|
|
break; }
|
|
|
|
os << head << e.n_boundary_points;
|
|
switch ( e.n_boundary_points) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
os << sep << e.boundary_point1;
|
|
break;
|
|
case 2:
|
|
os << sep << e.boundary_point1
|
|
<< sep << e.boundary_point2;
|
|
break;
|
|
case 3:
|
|
case 5:
|
|
os << sep << e.conic1;
|
|
break;
|
|
case 4:
|
|
os << sep << e.conic1
|
|
<< sep << e.conic2;
|
|
break; }
|
|
os << tail;
|
|
|
|
return( os);
|
|
}
|
|
|
|
template < class PT_, class DA_ >
|
|
std::istream&
|
|
operator >> ( std::istream& is,
|
|
CGAL::_Min_ellipse_2_adapterC2__Ellipse<PT_,DA_>& e)
|
|
{
|
|
switch ( CGAL::get_mode( is)) {
|
|
|
|
case CGAL::IO::PRETTY:
|
|
cerr << std::endl;
|
|
cerr << "Stream must be in ascii or binary mode" << std::endl;
|
|
break;
|
|
|
|
case CGAL::IO::ASCII:
|
|
case CGAL::IO::BINARY:
|
|
CGAL::read( is, e.n_boundary_points);
|
|
switch ( e.n_boundary_points) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
is >> e.boundary_point1;
|
|
break;
|
|
case 2:
|
|
is >> e.boundary_point1
|
|
>> e.boundary_point2;
|
|
break;
|
|
case 3:
|
|
case 5:
|
|
is >> e.conic1;
|
|
break;
|
|
case 4:
|
|
is >> e.conic1
|
|
>> e.conic2;
|
|
break; }
|
|
break;
|
|
|
|
default:
|
|
CGAL_optimisation_assertion_msg( false,
|
|
"CGAL::IO::mode invalid!");
|
|
break; }
|
|
|
|
return( is);
|
|
}
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
@! Class template Min_ellipse_2_adapterH2<PT,DA>
|
|
@! ----------------------------------------------------------------------------
|
|
|
|
\subsection{Class template \ccFont Min\_ellipse\_2\_adapterH2<PT,DA>}
|
|
|
|
First, we declare the class templates \ccc{Min_ellipse_2},
|
|
\ccc{Min_ellipse_2_adapterH2} and
|
|
\ccc{_Min_ellipse_2_adapterH2__Ellipse}.
|
|
|
|
@macro<Min_ellipse_2_adapterH2 declarations> = @begin
|
|
template < class Traits_ >
|
|
class Min_ellipse_2;
|
|
|
|
template < class PT_, class DA_ >
|
|
class Min_ellipse_2_adapterH2;
|
|
|
|
template < class PT_, class DA_ >
|
|
class _Min_ellipse_2_adapterH2__Ellipse;
|
|
@end
|
|
|
|
The actual work of the adapter is done in the nested class
|
|
\ccc{Ellipse}. Therefore, we implement the whole adapter in its
|
|
interface.
|
|
|
|
The variable \ccc{ellipse} containing the current ellipse is declared
|
|
\ccc{private} to disallow the user from directly accessing or modifying
|
|
it. Since the algorithm needs to access and modify the current ellipse,
|
|
it is declared \ccc{friend}.
|
|
|
|
@macro <Min_ellipse_2_adapterH2 interface and implementation> = @begin
|
|
template < class PT_, class DA_ >
|
|
class Min_ellipse_2_adapterH2 {
|
|
public:
|
|
// types
|
|
typedef PT_ PT;
|
|
typedef DA_ DA;
|
|
|
|
// nested types
|
|
typedef PT Point;
|
|
typedef _Min_ellipse_2_adapterH2__Ellipse<PT,DA> Ellipse;
|
|
|
|
private:
|
|
DA dao; // data accessor object
|
|
Ellipse ellipse; // current ellipse
|
|
friend class Min_ellipse_2< Min_ellipse_2_adapterH2<PT,DA> >;
|
|
|
|
public:
|
|
// creation
|
|
@<Min_ellipse_2_adapterH2 constructors>
|
|
|
|
// operations
|
|
@<Min_ellipse_2_adapterH2 operations>
|
|
};
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Constructors}
|
|
|
|
@macro <Min_ellipse_2_adapterH2 constructors> = @begin
|
|
Min_ellipse_2_adapterH2( const DA& da = DA())
|
|
: dao( da), ellipse( da)
|
|
{ }
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Operations}
|
|
|
|
@macro <Min_ellipse_2_adapterH2 operations> = @begin
|
|
CGAL::Orientation
|
|
orientation( const Point& p, const Point& q, const Point& r) const
|
|
{
|
|
typedef typename DA_::RT RT;
|
|
|
|
RT phx;
|
|
RT phy;
|
|
RT phw;
|
|
RT qhx;
|
|
RT qhy;
|
|
RT qhw;
|
|
RT rhx;
|
|
RT rhy;
|
|
RT rhw;
|
|
|
|
dao.get( p, phx, phy, phw);
|
|
dao.get( q, qhx, qhy, qhw);
|
|
dao.get( r, rhx, rhy, rhw);
|
|
|
|
return( static_cast< CGAL::Orientation>(
|
|
CGAL_NTS sign( ( phx*rhw - rhx*phw) * ( qhy*rhw - rhy*qhw)
|
|
- ( phy*rhw - rhy*phw) * ( qhx*rhw - rhx*qhw))));
|
|
}
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
\subsubsection{Nested Type \ccFont Ellipse}
|
|
|
|
@macro <Min_ellipse_2_adapterH2 nested type `Ellipse'> = @begin
|
|
template < class PT_, class DA_ >
|
|
std::ostream&
|
|
operator << ( std::ostream&,
|
|
const CGAL::_Min_ellipse_2_adapterH2__Ellipse<PT_,DA_>&);
|
|
|
|
template < class PT_, class DA_ >
|
|
std::istream&
|
|
operator >> ( std::istream&,
|
|
CGAL::_Min_ellipse_2_adapterH2__Ellipse<PT_,DA_>&);
|
|
|
|
template < class PT_, class DA_ >
|
|
class _Min_ellipse_2_adapterH2__Ellipse {
|
|
public:
|
|
// typedefs
|
|
typedef PT_ PT;
|
|
typedef DA_ DA;
|
|
|
|
typedef CGAL::ConicHPA2< PT, DA> CT;
|
|
typedef typename DA_::RT RT;
|
|
|
|
private:
|
|
// data members
|
|
int n_boundary_points; // number of boundary points
|
|
PT boundary_point1, boundary_point2; // two boundary points
|
|
CT conic1, conic2; // two conics
|
|
RT dr, ds, dt, du, dv, dw; // the gradient vector
|
|
|
|
friend std::ostream& operator << <> ( std::ostream&,
|
|
const _Min_ellipse_2_adapterH2__Ellipse<PT_,DA_>&);
|
|
|
|
friend std::istream& operator >> <> ( std::istream&,
|
|
_Min_ellipse_2_adapterH2__Ellipse<PT_,DA_>&);
|
|
|
|
public:
|
|
// types
|
|
typedef PT Point;
|
|
|
|
// creation
|
|
_Min_ellipse_2_adapterH2__Ellipse( const DA& da)
|
|
: conic1( da), conic2( da)
|
|
{ }
|
|
|
|
void
|
|
set( )
|
|
{
|
|
n_boundary_points = 0;
|
|
}
|
|
|
|
void
|
|
set( const Point& p)
|
|
{
|
|
n_boundary_points = 1;
|
|
boundary_point1 = p;
|
|
}
|
|
|
|
void
|
|
set( const Point& p, const Point& q)
|
|
{
|
|
n_boundary_points = 2;
|
|
boundary_point1 = p;
|
|
boundary_point2 = q;
|
|
}
|
|
|
|
void
|
|
set( const Point& p1, const Point& p2, const Point& p3)
|
|
{
|
|
n_boundary_points = 3;
|
|
conic1.set_ellipse( p1, p2, p3);
|
|
}
|
|
|
|
void
|
|
set( const Point& p1, const Point& p2,
|
|
const Point& p3, const Point& p4)
|
|
{
|
|
n_boundary_points = 4;
|
|
CT::set_two_linepairs( p1, p2, p3, p4, conic1, conic2);
|
|
dr = RT( 0);
|
|
ds = conic1.r() * conic2.s() - conic2.r() * conic1.s(),
|
|
dt = conic1.r() * conic2.t() - conic2.r() * conic1.t(),
|
|
du = conic1.r() * conic2.u() - conic2.r() * conic1.u(),
|
|
dv = conic1.r() * conic2.v() - conic2.r() * conic1.v(),
|
|
dw = conic1.r() * conic2.w() - conic2.r() * conic1.w();
|
|
}
|
|
|
|
void
|
|
set( const Point&, const Point&,
|
|
const Point&, const Point&, const Point& p5)
|
|
{
|
|
n_boundary_points = 5;
|
|
conic1.set( conic1, conic2, p5);
|
|
conic1.analyse();
|
|
}
|
|
|
|
// predicates
|
|
CGAL::Bounded_side
|
|
bounded_side( const Point& p) const
|
|
{
|
|
switch ( n_boundary_points) {
|
|
case 0:
|
|
return( CGAL::ON_UNBOUNDED_SIDE);
|
|
case 1:
|
|
return( ( p == boundary_point1) ?
|
|
CGAL::ON_BOUNDARY : CGAL::ON_UNBOUNDED_SIDE);
|
|
case 2:
|
|
return( ( p == boundary_point1)
|
|
|| ( p == boundary_point2)
|
|
|| ( CGAL::are_ordered_along_lineH2( boundary_point1, p,
|
|
boundary_point2, conic1.da())) ?
|
|
CGAL::ON_BOUNDARY : CGAL::ON_UNBOUNDED_SIDE);
|
|
case 3:
|
|
case 5:
|
|
return( conic1.convex_side( p));
|
|
case 4: {
|
|
CT c( conic1.da());
|
|
c.set( conic1, conic2, p);
|
|
c.analyse();
|
|
if ( ! c.is_ellipse()) {
|
|
c.set_ellipse( conic1, conic2);
|
|
c.analyse();
|
|
return( c.convex_side( p)); }
|
|
else {
|
|
int tau_star = c.vol_derivative( dr, ds, dt, du, dv, dw);
|
|
return( CGAL::Bounded_side( CGAL_NTS sign( tau_star))); } }
|
|
default:
|
|
CGAL_optimisation_assertion( ( n_boundary_points >= 0) &&
|
|
( n_boundary_points <= 5) ); }
|
|
// keeps g++ happy
|
|
return( CGAL::Bounded_side( 0));
|
|
}
|
|
|
|
bool
|
|
has_on_bounded_side( const Point& p) const
|
|
{
|
|
return( bounded_side( p) == CGAL::ON_BOUNDED_SIDE);
|
|
}
|
|
|
|
bool
|
|
has_on_boundary( const Point& p) const
|
|
{
|
|
return( bounded_side( p) == CGAL::ON_BOUNDARY);
|
|
}
|
|
|
|
bool
|
|
has_on_unbounded_side( const Point& p) const
|
|
{
|
|
return( bounded_side( p) == CGAL::ON_UNBOUNDED_SIDE);
|
|
}
|
|
|
|
bool
|
|
is_empty( ) const
|
|
{
|
|
return( n_boundary_points == 0);
|
|
}
|
|
|
|
bool
|
|
is_degenerate( ) const
|
|
{
|
|
return( n_boundary_points < 3);
|
|
}
|
|
|
|
// additional operations for checking
|
|
bool
|
|
operator == (
|
|
const CGAL::_Min_ellipse_2_adapterH2__Ellipse<PT_,DA_>& e) const
|
|
{
|
|
if ( n_boundary_points != e.n_boundary_points)
|
|
return( false);
|
|
|
|
switch ( n_boundary_points) {
|
|
case 0:
|
|
return( true);
|
|
case 1:
|
|
return( boundary_point1 == e.boundary_point1);
|
|
case 2:
|
|
return( ( ( boundary_point1 == e.boundary_point1)
|
|
&& ( boundary_point2 == e.boundary_point2))
|
|
|| ( ( boundary_point1 == e.boundary_point2)
|
|
&& ( boundary_point2 == e.boundary_point1)));
|
|
case 3:
|
|
case 5:
|
|
return( conic1 == e.conic1);
|
|
case 4:
|
|
return( ( ( conic1 == e.conic1)
|
|
&& ( conic2 == e.conic2))
|
|
|| ( ( conic1 == e.conic2)
|
|
&& ( conic2 == e.conic1)));
|
|
default:
|
|
CGAL_optimisation_assertion( ( n_boundary_points >= 0)
|
|
&& ( n_boundary_points <= 5)); }
|
|
// keeps g++ happy
|
|
return( false);
|
|
}
|
|
|
|
bool
|
|
operator != (
|
|
const CGAL::_Min_ellipse_2_adapterH2__Ellipse<PT_,DA_>& e) const
|
|
{
|
|
return( ! ( *this == e));
|
|
}
|
|
};
|
|
|
|
// I/O
|
|
template < class PT_, class DA_ >
|
|
std::ostream&
|
|
operator << ( std::ostream& os,
|
|
const CGAL::_Min_ellipse_2_adapterH2__Ellipse<PT_,DA_>& e)
|
|
{
|
|
const char* const empty = "";
|
|
const char* const pretty_head =
|
|
"CGAL::Min_ellipse_2_adapterH2::Ellipse( ";
|
|
const char* const pretty_sep = ", ";
|
|
const char* const pretty_tail = ")";
|
|
const char* const ascii_sep = " ";
|
|
|
|
const char* head = empty;
|
|
const char* sep = empty;
|
|
const char* tail = empty;
|
|
|
|
switch ( CGAL::get_mode( os)) {
|
|
case CGAL::IO::PRETTY:
|
|
head = pretty_head;
|
|
sep = pretty_sep;
|
|
tail = pretty_tail;
|
|
break;
|
|
case CGAL::IO::ASCII:
|
|
sep = ascii_sep;
|
|
break;
|
|
case CGAL::IO::BINARY:
|
|
break;
|
|
default:
|
|
CGAL_optimisation_assertion_msg( false,
|
|
"CGAL::get_mode( os) invalid!");
|
|
break; }
|
|
|
|
os << head << e.n_boundary_points;
|
|
switch ( e.n_boundary_points) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
os << sep << e.boundary_point1;
|
|
break;
|
|
case 2:
|
|
os << sep << e.boundary_point1
|
|
<< sep << e.boundary_point2;
|
|
break;
|
|
case 3:
|
|
case 5:
|
|
os << sep << e.conic1;
|
|
break;
|
|
case 4:
|
|
os << sep << e.conic1
|
|
<< sep << e.conic2;
|
|
break; }
|
|
os << tail;
|
|
|
|
return( os);
|
|
}
|
|
|
|
template < class PT_, class DA_ >
|
|
std::istream&
|
|
operator >> ( std::istream& is,
|
|
CGAL::_Min_ellipse_2_adapterH2__Ellipse<PT_,DA_>& e)
|
|
{
|
|
switch ( CGAL::get_mode( is)) {
|
|
|
|
case CGAL::IO::PRETTY:
|
|
cerr << std::endl;
|
|
cerr << "Stream must be in ascii or binary mode" << std::endl;
|
|
break;
|
|
|
|
case CGAL::IO::ASCII:
|
|
case CGAL::IO::BINARY:
|
|
CGAL::read( is, e.n_boundary_points);
|
|
switch ( e.n_boundary_points) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
is >> e.boundary_point1;
|
|
break;
|
|
case 2:
|
|
is >> e.boundary_point1
|
|
>> e.boundary_point2;
|
|
break;
|
|
case 3:
|
|
case 5:
|
|
is >> e.conic1;
|
|
break;
|
|
case 4:
|
|
is >> e.conic1
|
|
>> e.conic2;
|
|
break; }
|
|
break;
|
|
|
|
default:
|
|
CGAL_optimisation_assertion_msg( false,
|
|
"CGAL::IO::mode invalid!");
|
|
break; }
|
|
|
|
return( is);
|
|
}
|
|
@end
|
|
|
|
@! ============================================================================
|
|
@! Tests
|
|
@! ============================================================================
|
|
|
|
\clearpage
|
|
\section{Test}
|
|
|
|
We test \ccc{Min_ellipse_2} with the traits class implementation
|
|
for optimisation algorithms, using exact arithmetic, i.e.\ Cartesian
|
|
representation with number type \ccc{Quotient<Gmpz>} or
|
|
\ccc{Quotient<integer>} and homogeneous representation with
|
|
number type \ccc{Gmpz} or \ccc{integer}.
|
|
|
|
@macro <Min_ellipse_2 test (includes and typedefs)> = @begin
|
|
#include <CGAL/Cartesian.h>
|
|
#include <CGAL/Homogeneous.h>
|
|
#include <CGAL/Min_ellipse_2.h>
|
|
#include <CGAL/Min_ellipse_2_traits_2.h>
|
|
#include <CGAL/Min_ellipse_2_adapterC2.h>
|
|
#include <CGAL/Min_ellipse_2_adapterH2.h>
|
|
#include <CGAL/IO/Verbose_ostream.h>
|
|
#include <cassert>
|
|
#include <cstring>
|
|
#include <fstream>
|
|
|
|
#ifdef CGAL_USE_LEDA_FOR_OPTIMISATION_TEST
|
|
# include <CGAL/leda_integer.h>
|
|
typedef leda_integer Rt;
|
|
typedef CGAL::Quotient< leda_integer > Ft;
|
|
#else
|
|
# include <CGAL/Gmpz.h>
|
|
typedef CGAL::Gmpz Rt;
|
|
typedef CGAL::Quotient< CGAL::Gmpz > Ft;
|
|
#endif
|
|
|
|
typedef CGAL::Cartesian< Ft > KerC;
|
|
typedef CGAL::Homogeneous< Rt > KerH;
|
|
typedef CGAL::Min_ellipse_2_traits_2< KerC > TraitsC;
|
|
typedef CGAL::Min_ellipse_2_traits_2< KerH > TraitsH;
|
|
@end
|
|
|
|
The command line option \ccc{-verbose} enables verbose output.
|
|
|
|
@macro <Min_ellipse_2 test (verbose option)> = @begin
|
|
bool verbose = false;
|
|
if ( ( argc > 1) && ( std::strcmp( argv[ 1], "-verbose") == 0)) {
|
|
verbose = true;
|
|
--argc;
|
|
++argv; }
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
@! Code Coverage
|
|
@! ----------------------------------------------------------------------------
|
|
|
|
\subsection{Code Coverage}
|
|
|
|
We call each function of class \ccc{Min_ellipse_2<Traits>} at least
|
|
once to ensure code coverage.
|
|
|
|
@macro <Min_ellipse_2 test (code coverage)> = @begin
|
|
cover_Min_ellipse_2( verbose, TraitsC(), Rt());
|
|
cover_Min_ellipse_2( verbose, TraitsH(), Rt());
|
|
@end
|
|
|
|
@macro <Min_ellipse_2 test (code coverage test function)> = @begin
|
|
template < class Traits, class RT >
|
|
void
|
|
cover_Min_ellipse_2( bool verbose, const Traits&, const RT&)
|
|
{
|
|
using namespace std;
|
|
|
|
typedef CGAL::Min_ellipse_2< Traits > Min_ellipse;
|
|
typedef typename Min_ellipse::Point Point;
|
|
typedef typename Min_ellipse::Ellipse Ellipse;
|
|
|
|
CGAL::Verbose_ostream verr( verbose);
|
|
|
|
// generate `n' points at random
|
|
const int n = 20;
|
|
CGAL::Random random_x, random_y;
|
|
Point random_points[ n];
|
|
int i;
|
|
verr << n << " random points from [0,128)^2:" << endl;
|
|
for ( i = 0; i < n; ++i) {
|
|
random_points[ i] = Point( RT( random_x( 128)),
|
|
RT( random_y( 128)));
|
|
verr << i << ": " << random_points[ i] << endl;
|
|
}
|
|
|
|
// cover code
|
|
verr << endl << "default constructor...";
|
|
{
|
|
Min_ellipse me;
|
|
bool is_valid = me.is_valid( verbose);
|
|
bool is_empty = me.is_empty();
|
|
assert( is_valid);
|
|
assert( is_empty);
|
|
}
|
|
|
|
verr << endl << "one point constructor...";
|
|
{
|
|
Min_ellipse me( random_points[ 0]);
|
|
bool is_valid = me.is_valid( verbose);
|
|
bool is_degenerate = me.is_degenerate();
|
|
assert( is_valid);
|
|
assert( is_degenerate);
|
|
}
|
|
|
|
verr << endl << "two points constructor...";
|
|
{
|
|
Min_ellipse me( random_points[ 1],
|
|
random_points[ 2]);
|
|
bool is_valid = me.is_valid( verbose);
|
|
bool is_degenerate = me.is_degenerate();
|
|
assert( is_valid);
|
|
assert( is_degenerate);
|
|
}
|
|
|
|
verr << endl << "three points constructor...";
|
|
{
|
|
Min_ellipse me( random_points[ 3],
|
|
random_points[ 4],
|
|
random_points[ 5]);
|
|
bool is_valid = me.is_valid( verbose);
|
|
int num_pts = me.number_of_points();
|
|
assert( is_valid);
|
|
assert( num_pts == 3);
|
|
}
|
|
|
|
verr << endl << "four points constructor...";
|
|
{
|
|
Min_ellipse me( random_points[ 6],
|
|
random_points[ 7],
|
|
random_points[ 8],
|
|
random_points[ 9]);
|
|
bool is_valid = me.is_valid( verbose);
|
|
int num_pts = me.number_of_points();
|
|
assert( is_valid);
|
|
assert( num_pts == 4);
|
|
}
|
|
|
|
verr << endl << "five points constructor...";
|
|
{
|
|
Min_ellipse me( random_points[ 10],
|
|
random_points[ 11],
|
|
random_points[ 12],
|
|
random_points[ 13],
|
|
random_points[ 14]);
|
|
bool is_valid = me.is_valid( verbose);
|
|
int num_pts = me.number_of_points();
|
|
assert( is_valid);
|
|
assert( num_pts == 5);
|
|
}
|
|
|
|
verr << endl << "Point* constructor...";
|
|
Min_ellipse me( random_points, random_points+9, false);
|
|
{
|
|
Min_ellipse me2( random_points, random_points+9, true);
|
|
bool is_valid = me .is_valid( verbose);
|
|
bool is_valid2 = me2.is_valid( verbose);
|
|
assert( is_valid);
|
|
assert( is_valid2);
|
|
assert( me .number_of_points() == 9);
|
|
assert( me2.number_of_points() == 9);
|
|
assert( me.ellipse() == me2.ellipse());
|
|
}
|
|
|
|
verr << endl << "list<Point>::const_iterator constructor...";
|
|
{
|
|
Min_ellipse me1( me.points_begin(), me.points_end(), false);
|
|
Min_ellipse me2( me.points_begin(), me.points_end(), true);
|
|
bool is_valid1 = me1.is_valid( verbose);
|
|
bool is_valid2 = me2.is_valid( verbose);
|
|
assert( is_valid1);
|
|
assert( is_valid2);
|
|
assert( me1.number_of_points() == 9);
|
|
assert( me2.number_of_points() == 9);
|
|
assert( me.ellipse() == me1.ellipse());
|
|
assert( me.ellipse() == me2.ellipse());
|
|
}
|
|
|
|
verr << endl << "#points already called above.";
|
|
|
|
verr << endl << "points access already called above.";
|
|
|
|
verr << endl << "support points access...";
|
|
{
|
|
typedef typename Min_ellipse::Support_point_iterator
|
|
Support_point_iterator;
|
|
Point support_point;
|
|
Support_point_iterator iter( me.support_points_begin());
|
|
for ( i = 0; i < me.number_of_support_points(); ++i, ++iter) {
|
|
support_point = me.support_point( i);
|
|
assert( support_point == *iter); }
|
|
Support_point_iterator end_iter( me.support_points_end());
|
|
assert( iter == end_iter);
|
|
}
|
|
|
|
verr << endl << "ellipse access already called above...";
|
|
|
|
verr << endl << "in-ellipse predicates...";
|
|
{
|
|
Point p;
|
|
CGAL::Bounded_side bounded_side;
|
|
bool has_on_bounded_side;
|
|
bool has_on_boundary;
|
|
bool has_on_unbounded_side;
|
|
for ( i = 0; i < 9; ++i) {
|
|
p = random_points[ i];
|
|
bounded_side = me.bounded_side( p);
|
|
has_on_bounded_side = me.has_on_bounded_side( p);
|
|
has_on_boundary = me.has_on_boundary( p);
|
|
has_on_unbounded_side = me.has_on_unbounded_side( p);
|
|
assert( bounded_side != CGAL::ON_UNBOUNDED_SIDE);
|
|
assert( has_on_bounded_side || has_on_boundary);
|
|
assert( ! has_on_unbounded_side); }
|
|
}
|
|
|
|
verr << endl << "is_... predicates already called above.";
|
|
|
|
verr << endl << "single point insert...";
|
|
me.insert( random_points[ 9]);
|
|
{
|
|
bool is_valid = me.is_valid( verbose);
|
|
assert( is_valid);
|
|
assert( me.number_of_points() == 10);
|
|
}
|
|
|
|
verr << endl << "Point* insert...";
|
|
me.insert( random_points+10, random_points+n);
|
|
{
|
|
bool is_valid = me.is_valid( verbose);
|
|
assert( is_valid);
|
|
assert( me.number_of_points() == n);
|
|
}
|
|
|
|
verr << endl << "list<Point>::const_iterator insert...";
|
|
{
|
|
Min_ellipse me2;
|
|
me2.insert( me.points_begin(), me.points_end());
|
|
bool is_valid = me2.is_valid( verbose);
|
|
assert( is_valid);
|
|
assert( me2.number_of_points() == n);
|
|
|
|
verr << endl << "clear...";
|
|
me2.clear();
|
|
is_valid = me2.is_valid( verbose);
|
|
bool is_empty = me2.is_empty();
|
|
assert( is_valid);
|
|
assert( is_empty);
|
|
}
|
|
|
|
verr << endl << "validity check already called several times.";
|
|
|
|
verr << endl << "traits class access...";
|
|
{
|
|
Traits traits( me.traits());
|
|
}
|
|
|
|
verr << endl << "I/O...";
|
|
{
|
|
verr << endl << " writing `test_Min_ellipse_2.ascii'...";
|
|
ofstream os( "test_Min_ellipse_2.ascii");
|
|
CGAL::set_ascii_mode( os);
|
|
os << me;
|
|
}
|
|
{
|
|
verr << endl << " writing `test_Min_ellipse_2.pretty'...";
|
|
ofstream os( "test_Min_ellipse_2.pretty");
|
|
CGAL::set_pretty_mode( os);
|
|
os << me;
|
|
}
|
|
{
|
|
verr << endl << " writing `test_Min_ellipse_2.binary'...";
|
|
ofstream os( "test_Min_ellipse_2.binary");
|
|
CGAL::set_binary_mode( os);
|
|
os << me;
|
|
}
|
|
{
|
|
verr << endl << " reading `test_Min_ellipse_2.ascii'...";
|
|
Min_ellipse me_in;
|
|
ifstream is( "test_Min_ellipse_2.ascii");
|
|
CGAL::set_ascii_mode( is);
|
|
is >> me_in;
|
|
bool is_valid = me_in.is_valid( verbose);
|
|
assert( is_valid);
|
|
assert( me_in.number_of_points() == n);
|
|
assert( me_in.ellipse() == me.ellipse());
|
|
}
|
|
verr << endl;
|
|
}
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
@! Adapters
|
|
@! ----------------------------------------------------------------------------
|
|
|
|
\subsection{Traits Class Adapters}
|
|
|
|
We define two point classes (one with Cartesian, one with homogeneous
|
|
representation) and corresponding data accessors.
|
|
|
|
@macro <Min_ellipse_2 test (point classes)> = @begin
|
|
// 2D Cartesian point class
|
|
class MyPointC2;
|
|
|
|
std::ostream& operator << ( std::ostream&, const MyPointC2&);
|
|
std::istream& operator >> ( std::istream&, MyPointC2&);
|
|
|
|
class MyPointC2 {
|
|
public:
|
|
typedef ::Ft FT;
|
|
private:
|
|
FT x_;
|
|
FT y_;
|
|
public:
|
|
MyPointC2( ) { }
|
|
MyPointC2( const FT& x, const FT& y) : x_( x), y_( y) { }
|
|
|
|
const FT& x( ) const { return( x_); }
|
|
const FT& y( ) const { return( y_); }
|
|
|
|
bool
|
|
operator == ( const MyPointC2& p) const
|
|
{
|
|
return( ( x_ == p.x_) && ( y_ == p.y_));
|
|
}
|
|
|
|
bool
|
|
operator != ( const MyPointC2& p) const
|
|
{
|
|
return( ( x_ != p.x_) || ( y_ != p.y_));
|
|
}
|
|
|
|
friend
|
|
std::ostream& operator << ( std::ostream& os, const MyPointC2& p);
|
|
|
|
friend
|
|
std::istream& operator >> ( std::istream& is, MyPointC2& p);
|
|
};
|
|
|
|
std::ostream&
|
|
operator << ( std::ostream& os, const MyPointC2& p)
|
|
{
|
|
return( os << p.x_ << ' ' << p.y_);
|
|
}
|
|
|
|
std::istream&
|
|
operator >> ( std::istream& is, MyPointC2& p)
|
|
{
|
|
return( is >> p.x_ >> p.y_);
|
|
}
|
|
|
|
// 2D Cartesian point class data accessor
|
|
class MyPointC2DA {
|
|
public:
|
|
typedef ::Ft FT;
|
|
|
|
MyPointC2DA( ) { }
|
|
|
|
const FT& get_x( const MyPointC2& p) const { return( p.x()); }
|
|
const FT& get_y( const MyPointC2& p) const { return( p.y()); }
|
|
|
|
void
|
|
get( const MyPointC2& p, FT& x, FT& y) const
|
|
{
|
|
x = get_x( p);
|
|
y = get_y( p);
|
|
}
|
|
|
|
void
|
|
set( MyPointC2& p, const FT& x, const FT& y) const
|
|
{
|
|
p = MyPointC2( x, y);
|
|
}
|
|
};
|
|
|
|
|
|
// 2D homogeneous point class
|
|
class MyPointH2;
|
|
|
|
std::ostream& operator << ( std::ostream&, const MyPointH2&);
|
|
std::istream& operator >> ( std::istream&, MyPointH2&);
|
|
|
|
class MyPointH2 {
|
|
public:
|
|
typedef ::Rt RT;
|
|
private:
|
|
RT hx_;
|
|
RT hy_;
|
|
RT hw_;
|
|
public:
|
|
MyPointH2( ) { }
|
|
MyPointH2( const RT& hx, const RT& hy, const RT& hw = RT( 1))
|
|
: hx_( hx), hy_( hy), hw_( hw) { }
|
|
|
|
const RT& hx( ) const { return( hx_); }
|
|
const RT& hy( ) const { return( hy_); }
|
|
const RT& hw( ) const { return( hw_); }
|
|
|
|
bool
|
|
operator == ( const MyPointH2& p) const
|
|
{
|
|
return( ( hx_*p.hw_ == p.hx_*hw_) && ( hy_*p.hw_ == p.hy_*hw_));
|
|
}
|
|
|
|
bool
|
|
operator != ( const MyPointH2& p) const
|
|
{
|
|
return( ( hx_*p.hw_ != p.hx_*hw_) || ( hy_*p.hw_ != p.hy_*hw_));
|
|
}
|
|
|
|
friend
|
|
std::ostream& operator << ( std::ostream& os, const MyPointH2& p);
|
|
|
|
friend
|
|
std::istream& operator >> ( std::istream& is, MyPointH2& p);
|
|
};
|
|
|
|
std::ostream&
|
|
operator << ( std::ostream& os, const MyPointH2& p)
|
|
{
|
|
return( os << p.hx_ << ' ' << p.hy_ << ' ' << p.hw_);
|
|
}
|
|
|
|
std::istream&
|
|
operator >> ( std::istream& is, MyPointH2& p)
|
|
{
|
|
return( is >> p.hx_ >> p.hy_ >> p.hw_);
|
|
}
|
|
|
|
// 2D homogeneous point class data accessor
|
|
class MyPointH2DA {
|
|
public:
|
|
typedef ::Rt RT;
|
|
|
|
MyPointH2DA( ) { }
|
|
|
|
const RT& get_hx( const MyPointH2& p) const { return( p.hx()); }
|
|
const RT& get_hy( const MyPointH2& p) const { return( p.hy()); }
|
|
const RT& get_hw( const MyPointH2& p) const { return( p.hw()); }
|
|
|
|
void
|
|
get( const MyPointH2& p, RT& hx, RT& hy, RT& hw) const
|
|
{
|
|
hx = get_hx( p);
|
|
hy = get_hy( p);
|
|
hw = get_hw( p);
|
|
}
|
|
|
|
void
|
|
set( MyPointH2& p, const RT& hx, const RT& hy, const RT& hw) const
|
|
{
|
|
p = MyPointH2( hx, hy, hw);
|
|
}
|
|
};
|
|
@end
|
|
|
|
To test the traits class adapters we use the code coverage test function.
|
|
|
|
@macro <Min_ellipse_2 test (adapters test)> = @begin
|
|
typedef CGAL::Min_ellipse_2_adapterC2< MyPointC2, MyPointC2DA > AdapterC2;
|
|
typedef CGAL::Min_ellipse_2_adapterH2< MyPointH2, MyPointH2DA > AdapterH2;
|
|
cover_Min_ellipse_2( verbose, AdapterC2(), Rt());
|
|
cover_Min_ellipse_2( verbose, AdapterH2(), Rt());
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
@! External Test Sets
|
|
@! ----------------------------------------------------------------------------
|
|
|
|
\subsection{External Test Sets}
|
|
|
|
In addition, some data files can be given as command line arguments.
|
|
A data file contains pairs of \ccc{int}s, 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_ellipse_2 test (external test sets)> = @begin
|
|
while ( argc > 1) {
|
|
|
|
typedef CGAL::Min_ellipse_2< TraitsH > Min_ellipse;
|
|
typedef Min_ellipse::Point Point;
|
|
typedef Min_ellipse::Ellipse Ellipse;
|
|
|
|
CGAL::Verbose_ostream verr( verbose);
|
|
|
|
// read points from file
|
|
verr << std::endl << "input file: `" << argv[ 1] << "'" << std::flush;
|
|
|
|
std::list<Point> points;
|
|
int n, x, y;
|
|
std::ifstream in( argv[ 1]);
|
|
in >> n;
|
|
assert( in);
|
|
for ( int i = 0; i < n; ++i) {
|
|
in >> x >> y;
|
|
assert( in);
|
|
points.push_back( Point( x, y)); }
|
|
|
|
// compute and check min_ellipse
|
|
Min_ellipse me2( points.begin(), points.end(), false);
|
|
bool is_valid = me2.is_valid( verbose);
|
|
assert( is_valid);
|
|
|
|
// next file
|
|
--argc;
|
|
++argv; }
|
|
@end
|
|
|
|
@! ==========================================================================
|
|
@! Files
|
|
@! ==========================================================================
|
|
|
|
\clearpage
|
|
\section{Files}
|
|
|
|
@i share/namespace.awi
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
@! Min_ellipse_2.h
|
|
@! ----------------------------------------------------------------------------
|
|
|
|
\subsection{Min\_ellipse\_2.h}
|
|
|
|
@file <include/CGAL/Min_ellipse_2.h> = @begin
|
|
@<file header>(
|
|
"include/CGAL/Min_ellipse_2.h",
|
|
"2D Smallest Enclosing Ellipse")
|
|
|
|
#ifndef CGAL_MIN_ELLIPSE_2_H
|
|
#define CGAL_MIN_ELLIPSE_2_H
|
|
|
|
// includes
|
|
#include <CGAL/Optimisation/basic.h>
|
|
#include <CGAL/Random.h>
|
|
#include <list>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
|
|
@<namespace begin>("CGAL")
|
|
|
|
// Class declaration
|
|
// =================
|
|
@<Min_ellipse_2 declaration>
|
|
|
|
// Class interface
|
|
// ===============
|
|
@<Min_ellipse_2 interface>
|
|
|
|
// Function declarations
|
|
// =====================
|
|
// I/O
|
|
// ---
|
|
@<Min_ellipse_2 I/O operators declaration>
|
|
|
|
@<namespace end>("CGAL")
|
|
|
|
#ifdef CGAL_CFG_NO_AUTOMATIC_TEMPLATE_INCLUSION
|
|
# include <CGAL/Min_ellipse_2.C>
|
|
#endif
|
|
|
|
#endif // CGAL_MIN_ELLIPSE_2_H
|
|
|
|
@<end of file line>
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
@! Min_ellipse_2.C
|
|
@! ----------------------------------------------------------------------------
|
|
|
|
\subsection{Min\_ellipse\_2.C}
|
|
|
|
@file <include/CGAL/Min_ellipse_2.C> = @begin
|
|
@<file header>(
|
|
"include/CGAL/Min_ellipse_2.C",
|
|
"2D Smallest Enclosing Ellipse")
|
|
|
|
@<namespace begin>("CGAL")
|
|
|
|
// Class implementation (continued)
|
|
// ================================
|
|
// I/O
|
|
// ---
|
|
@<Min_ellipse_2 I/O operators>
|
|
|
|
@<namespace end>("CGAL")
|
|
|
|
@<end of file line>
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
@! Optimisation_ellipse_2.h
|
|
@! ----------------------------------------------------------------------------
|
|
|
|
\subsection{Optimisation\_ellipse\_2.h}
|
|
|
|
@file <include/CGAL/Optimisation_ellipse_2.h> = @begin
|
|
@<file header>(
|
|
"include/CGAL/Optimisation_ellipse_2.h",
|
|
"2D Optimisation Ellipse")
|
|
|
|
#ifndef CGAL_OPTIMISATION_ELLIPSE_2_H
|
|
#define CGAL_OPTIMISATION_ELLIPSE_2_H
|
|
|
|
// the following include is needed by `to_double()'
|
|
#ifndef CGAL_CARTESIAN_H
|
|
# include <CGAL/Cartesian.h>
|
|
#endif
|
|
|
|
// includes
|
|
#ifndef CGAL_POINT_2_H
|
|
# include <CGAL/Point_2.h>
|
|
#endif
|
|
#ifndef CGAL_CONIC_2_H
|
|
# include <CGAL/Conic_2.h>
|
|
#endif
|
|
#ifndef CGAL_OPTIMISATION_ASSERTIONS_H
|
|
# include <CGAL/Optimisation/assertions.h>
|
|
#endif
|
|
#ifndef CGAL_IO_FORWARD_DECL_WINDOW_STREAM_H
|
|
# include <CGAL/IO/forward_decl_window_stream.h>
|
|
#endif
|
|
|
|
@<namespace begin>("CGAL")
|
|
|
|
// Class declaration
|
|
// =================
|
|
@<Optimisation_ellipse_2 declaration>
|
|
|
|
// Class interface
|
|
// ===============
|
|
@<Optimisation_ellipse_2 interface>
|
|
|
|
// Function declarations
|
|
// =====================
|
|
// I/O
|
|
// ---
|
|
@<Optimisation_ellipse_2 I/O operators declaration>
|
|
|
|
@<namespace end>("CGAL")
|
|
|
|
#ifdef CGAL_CFG_NO_AUTOMATIC_TEMPLATE_INCLUSION
|
|
# include <CGAL/Optimisation_ellipse_2.C>
|
|
#endif
|
|
|
|
#endif // CGAL_OPTIMISATION_ELLIPSE_2_H
|
|
|
|
@<end of file line>
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
@! Optimisation_ellipse_2.C
|
|
@! ----------------------------------------------------------------------------
|
|
|
|
\subsection{Optimisation\_ellipse\_2.C}
|
|
|
|
@file <include/CGAL/Optimisation_ellipse_2.C> = @begin
|
|
@<file header>(
|
|
"include/CGAL/Optimisation_ellipse_2.C",
|
|
"2D Optimisation Ellipse")
|
|
|
|
@<namespace begin>("CGAL")
|
|
|
|
// Class implementation (continued)
|
|
// ================================
|
|
|
|
// I/O
|
|
// ---
|
|
@<Optimisation_ellipse_2 I/O operators>
|
|
|
|
@<namespace end>("CGAL")
|
|
|
|
@<end of file line>
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
@! Min_ellipse_2_traits_2.h
|
|
@! ----------------------------------------------------------------------------
|
|
|
|
\subsection{Min\_ellipse\_2\_traits\_2.h}
|
|
|
|
@file <include/CGAL/Min_ellipse_2_traits_2.h> = @begin
|
|
@<file header>(
|
|
"include/CGAL/Min_ellipse_2_traits_2.h",
|
|
"default traits class for 2D Smallest Enclosing Ellipse")
|
|
|
|
#ifndef CGAL_MIN_ELLIPSE_2_TRAITS_2_H
|
|
#define CGAL_MIN_ELLIPSE_2_TRAITS_2_H
|
|
|
|
// includes
|
|
#ifndef CGAL_POINT_2_H
|
|
# include <CGAL/Point_2.h>
|
|
#endif
|
|
#ifndef CGAL_OPTIMISATION_ELLIPSE_2_H
|
|
# include <CGAL/Optimisation_ellipse_2.h>
|
|
#endif
|
|
|
|
@<namespace begin>("CGAL")
|
|
|
|
// Class declarations
|
|
// ==================
|
|
@<Min_ellipse_2_traits_2 declarations>
|
|
|
|
// Class interface and implementation
|
|
// ==================================
|
|
@<Min_ellipse_2_traits_2 interface and implementation>
|
|
|
|
@<namespace end>("CGAL")
|
|
|
|
#endif // CGAL_MIN_ELLIPSE_2_TRAITS_2_H
|
|
|
|
@<end of file line>
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
@! Min_ellipse_2_adapterC2.h
|
|
@! ----------------------------------------------------------------------------
|
|
|
|
\subsection{Min\_ellipse\_2\_adapterC2.h}
|
|
|
|
@file <include/CGAL/Min_ellipse_2_adapterC2.h> = @begin
|
|
@<file header>(
|
|
"include/CGAL/Min_ellipse_2_adapterC2.h",
|
|
"traits class adapter for 2D Smallest Enclosing Ellipse")
|
|
|
|
#ifndef CGAL_MIN_ELLIPSE_2_ADAPTERC2_H
|
|
#define CGAL_MIN_ELLIPSE_2_ADAPTERC2_H
|
|
|
|
// includes
|
|
#ifndef CGAL_CONICCPA2_H
|
|
# include <CGAL/ConicCPA2.h>
|
|
#endif
|
|
#ifndef CGAL_OPTIMISATION_ASSERTIONS_H
|
|
# include <CGAL/Optimisation/assertions.h>
|
|
#endif
|
|
|
|
@<namespace begin>("CGAL")
|
|
|
|
// Class declarations
|
|
// ==================
|
|
@<Min_ellipse_2_adapterC2 declarations>
|
|
|
|
// Class interface and implementation
|
|
// ==================================
|
|
template < class PT, class DA >
|
|
bool
|
|
are_ordered_along_lineC2( const PT& p, const PT& q, const PT& r,
|
|
const DA& da)
|
|
{
|
|
typedef typename DA::FT FT;
|
|
|
|
FT px;
|
|
FT py;
|
|
FT qx;
|
|
FT qy;
|
|
FT rx;
|
|
FT ry;
|
|
|
|
da.get( p, px, py);
|
|
da.get( q, qx, qy);
|
|
da.get( r, rx, ry);
|
|
|
|
// p,q,r collinear?
|
|
if ( ! CGAL_NTS is_zero( ( px-rx) * ( qy-ry) - ( py-ry) * ( qx-rx)))
|
|
return( false);
|
|
|
|
// p,q,r vertical?
|
|
if ( px != rx)
|
|
return( ( ( px < qx) && ( qx < rx))
|
|
|| ( ( rx < qx) && ( qx < px)));
|
|
else
|
|
return( ( ( py < qy) && ( qy < ry))
|
|
|| ( ( ry < qy) && ( qy < py)));
|
|
}
|
|
|
|
@<Min_ellipse_2_adapterC2 interface and implementation>
|
|
|
|
// Nested type `Ellipse'
|
|
@<Min_ellipse_2_adapterC2 nested type `Ellipse'>
|
|
|
|
@<namespace end>("CGAL")
|
|
|
|
#endif // CGAL_MIN_ELLIPSE_2_ADAPTERC2_H
|
|
|
|
@<end of file line>
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
@! Min_ellipse_2_adapterH2.h
|
|
@! ----------------------------------------------------------------------------
|
|
|
|
\subsection{Min\_ellipse\_2\_adapterH2.h}
|
|
|
|
@file <include/CGAL/Min_ellipse_2_adapterH2.h> = @begin
|
|
@<file header>(
|
|
"include/CGAL/Min_ellipse_2_adapterH2.h",
|
|
"traits class adapter for 2D Smallest Enclosing Ellipse")
|
|
|
|
#ifndef CGAL_MIN_ELLIPSE_2_ADAPTERH2_H
|
|
#define CGAL_MIN_ELLIPSE_2_ADAPTERH2_H
|
|
|
|
// includes
|
|
#ifndef CGAL_CONICHPA2_H
|
|
# include <CGAL/ConicHPA2.h>
|
|
#endif
|
|
#ifndef CGAL_OPTIMISATION_ASSERTIONS_H
|
|
# include <CGAL/Optimisation/assertions.h>
|
|
#endif
|
|
|
|
@<namespace begin>("CGAL")
|
|
|
|
// Class declarations
|
|
// ==================
|
|
@<Min_ellipse_2_adapterH2 declarations>
|
|
|
|
// Class interface and implementation
|
|
// ==================================
|
|
template < class PT, class DA >
|
|
bool
|
|
are_ordered_along_lineH2( const PT& p, const PT& q, const PT& r,
|
|
const DA& da)
|
|
{
|
|
typedef typename DA::RT RT;
|
|
|
|
RT phx;
|
|
RT phy;
|
|
RT phw;
|
|
RT qhx;
|
|
RT qhy;
|
|
RT qhw;
|
|
RT rhx;
|
|
RT rhy;
|
|
RT rhw;
|
|
|
|
da.get( p, phx, phy, phw);
|
|
da.get( q, qhx, qhy, qhw);
|
|
da.get( r, rhx, rhy, rhw);
|
|
|
|
// p,q,r collinear?
|
|
if ( ! CGAL_NTS is_zero( ( phx*rhw - rhx*phw) * ( qhy*rhw - rhy*qhw)
|
|
- ( phy*rhw - rhy*phw) * ( qhx*rhw - rhx*qhw)))
|
|
return( false);
|
|
|
|
// p,q,r vertical?
|
|
if ( phx*rhw != rhx*phw)
|
|
return( ( ( phx*qhw < qhx*phw) && ( qhx*rhw < rhx*qhw))
|
|
|| ( ( rhx*qhw < qhx*rhw) && ( qhx*phw < phx*qhw)));
|
|
else
|
|
return( ( ( phy*qhw < qhy*phw) && ( qhy*rhw < rhy*qhw))
|
|
|| ( ( rhy*qhw < qhy*rhw) && ( qhy*phw < phy*qhw)));
|
|
}
|
|
|
|
@<Min_ellipse_2_adapterH2 interface and implementation>
|
|
|
|
// Nested type `Ellipse'
|
|
@<Min_ellipse_2_adapterH2 nested type `Ellipse'>
|
|
|
|
@<namespace end>("CGAL")
|
|
|
|
#endif // CGAL_MIN_ELLIPSE_2_ADAPTERH2_H
|
|
|
|
@<end of file line>
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
@! Min_ellipse_2_Window_stream.h
|
|
@! ----------------------------------------------------------------------------
|
|
|
|
\subsection{Min\_ellipse\_2\_Window\_stream.h}
|
|
|
|
@file <include/CGAL/IO/Min_ellipse_2_Window_stream.h> = @begin
|
|
@<file header>(
|
|
"include/CGAL/IO/Min_ellipse_2_Window_stream.h",
|
|
"graphical output to `leda_window' for Min_ellipse_2 algo.")
|
|
|
|
// Each of the following operators is individually
|
|
// protected against multiple inclusion.
|
|
|
|
// Window_stream I/O operators
|
|
// ===========================
|
|
// includes
|
|
#ifndef CGAL_CONIC_2_WINDOW_STREAM_H
|
|
# include <CGAL/IO/Conic_2_Window_stream.h>
|
|
#endif
|
|
|
|
// Optimisation_ellipse_2
|
|
// ----------------------
|
|
@<Optimisation_ellipse_2 graphical output operator>
|
|
|
|
// Min_ellipse_2
|
|
// -------------
|
|
@<Min_ellipse_2 graphical output operator>
|
|
|
|
@<end of file line>
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
@! test_Min_ellipse_2.C
|
|
@! ----------------------------------------------------------------------------
|
|
|
|
\subsection{test\_Min\_ellipse\_2.C}
|
|
|
|
@file <test/Min_ellipse_2/test_Min_ellipse_2.C> = @begin
|
|
@<file header>(
|
|
"test/Min_ellipse_2/test_Min_ellipse_2.C",
|
|
"test program for 2D Smallest Enclosing Ellipse")
|
|
|
|
@<Min_ellipse_2 test (includes and typedefs)>
|
|
|
|
// code coverage test function
|
|
// ---------------------------
|
|
@<Min_ellipse_2 test (code coverage test function)>
|
|
|
|
// point classes for adapters test
|
|
// -------------------------------
|
|
@<Min_ellipse_2 test (point classes)>
|
|
|
|
// main
|
|
// ----
|
|
int
|
|
main( int argc, char* argv[])
|
|
{
|
|
// command line options
|
|
// --------------------
|
|
// option `-verbose'
|
|
@<Min_ellipse_2 test (verbose option)>
|
|
|
|
// code coverage
|
|
// -------------
|
|
@<Min_ellipse_2 test (code coverage)>
|
|
|
|
// adapters test
|
|
// -------------
|
|
@<Min_ellipse_2 test (adapters test)>
|
|
|
|
// external test sets
|
|
// -------------------
|
|
@<Min_ellipse_2 test (external test sets)>
|
|
|
|
return( 0);
|
|
}
|
|
|
|
@<end of file line>
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
@! File Header
|
|
@! ----------------------------------------------------------------------------
|
|
|
|
\subsection*{File Header}
|
|
|
|
@i share/file_header.awi
|
|
|
|
And here comes the specific file header for the product files of this
|
|
web file.
|
|
|
|
@macro <file header>(2) many = @begin
|
|
@<copyright notice>
|
|
@<file name>(@1)
|
|
@<file description>(
|
|
"Min_ellipse_2",
|
|
"Geometric Optimisation",
|
|
"Min_ellipse_2",
|
|
"$Id$","$Date$",
|
|
"Sven Schönherr <sven@@inf.ethz.ch>, Bernd Gärtner",
|
|
"ETH Zürich (Bernd Gärtner <gaertner@@inf.ethz.ch>)",
|
|
"@2")
|
|
@end
|
|
|
|
@! ============================================================================
|
|
@! Bibliography
|
|
@! ============================================================================
|
|
|
|
\clearpage
|
|
\bibliographystyle{plain}
|
|
\bibliography{geom,../doc_tex/basic/Optimisation/cgal}
|
|
|
|
@! ===== EOF ==================================================================
|