mirror of https://github.com/CGAL/cgal
3384 lines
114 KiB
PHP
3384 lines
114 KiB
PHP
@! ============================================================================
|
|
@! The CGAL Library
|
|
@! Implementation: 2D Conic
|
|
@! ----------------------------------------------------------------------------
|
|
@! file : web/Optimisation/Conic_2.aw
|
|
@! author: Bernd Gärtner, Sven Schönherr <gaertner@inf.ethz.ch>
|
|
@! ----------------------------------------------------------------------------
|
|
@! $CGAL_Chapter: Geometric Optimisation $
|
|
@! $CGAL_Package: Min_ellipse_2 WIP $
|
|
@! $Revision$
|
|
@! $Date$
|
|
@! ============================================================================
|
|
|
|
@documentclass[twoside]{article}
|
|
@usepackage[latin1]{inputenc}
|
|
@usepackage{a4wide2}
|
|
@usepackage{epsf}
|
|
@usepackage{cc_manual}
|
|
@article
|
|
|
|
\setlength{\parskip}{1ex}
|
|
\setcounter{secnumdepth}{4}
|
|
\setcounter{tocdepth}{4}
|
|
|
|
@! ============================================================================
|
|
@! Title
|
|
@! ============================================================================
|
|
|
|
\RCSdef{\rcsRevision}{$Revision$}
|
|
\RCSdefDate{\rcsDate}{$Date$}
|
|
\newcommand{\cgalWIP}{{\footnotesize{} (\rcsRevision{} , \rcsDate) }}
|
|
|
|
@t vskip 5 mm
|
|
@t title titlefont centre "2D Conic*"
|
|
@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).}
|
|
|
|
@t table_of_contents
|
|
|
|
\renewcommand{\R}{I\!\!R}
|
|
\newcommand{\C}{{\cal C}}
|
|
\renewcommand{\E}{{\cal E}}
|
|
\newcommand{\Vol}{\mathop{\rm Vol}}
|
|
\renewcommand{\r}{{\cal R}}
|
|
\renewcommand{\d}{\partial}
|
|
\newcommand{\sgn}{\mathop{\rm sgn}}
|
|
\newcommand{\I}{{\bf i}}
|
|
\newcommand{\acos}{\mathop{\rm acos}}
|
|
\newcommand{\asin}{\mathop{\rm asin}}
|
|
\newtheorem{Definition}{Definition}[section]
|
|
\newtheorem{Lemma}[Definition]{Lemma}
|
|
|
|
@! ============================================================================
|
|
@section{Introduction}
|
|
@! ============================================================================
|
|
|
|
We define a class template @prg{Conic_2<R>} to store, access and
|
|
manipulate @em{conics} in the plane, a.k.a. @em{second order curves}.
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsection{Conics}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
For a given real vector $\r=(r,s,t,u,v,w)$, a conic
|
|
$\C=\C(\r)$ is the set of @em{homogeneous} points
|
|
$p = (x,y,h), h\neq 0$ satisfying
|
|
|
|
\begin{equation}
|
|
\label{conic_def_hom}
|
|
\r(p) := rx^2 + sy^2 + txy + uxh + vyh + wh^2 = 0,
|
|
\end{equation}
|
|
equivalently
|
|
|
|
\begin{equation}
|
|
\label{conic_hom}
|
|
(x, y, h)
|
|
\left( \begin{array}{ccc}
|
|
2r & t & u \\
|
|
t & 2s & v \\
|
|
u & v & 2w
|
|
\end{array} \right)
|
|
\left( \begin{array}{c}
|
|
x \\
|
|
y \\
|
|
h
|
|
\end{array} \right) = 0.
|
|
\end{equation}
|
|
|
|
$\r$ is called a @em{representation} of $\C$.
|
|
Note that the homogeneous point $(x,y,h)$ corresponds
|
|
to the Cartesian point $(x/h, y/h)$ in the plane. Also, any Cartesian
|
|
point $p=(x,y)$ can be identified with the homogeneous point $(x,y,1)$,
|
|
in which case (\ref{conic_def_hom}) assumes the form
|
|
\begin{equation}
|
|
\label{conic_def_cart}
|
|
\r(p) = rx^2 + sy^2 + txy + ux + vy + w = 0,
|
|
\end{equation}
|
|
equivalently
|
|
\begin{equation}
|
|
\label{conic_cart}
|
|
(x,y)
|
|
\left( \begin{array}{ccc}
|
|
2r & t \\
|
|
t & 2s
|
|
\end{array} \right)
|
|
\left( \begin{array}{c}
|
|
x \\
|
|
y
|
|
\end{array} \right)
|
|
+ (2u, 2v) \left( \begin{array}{c}
|
|
x \\
|
|
y
|
|
\end{array} \right)
|
|
+ 2w = 0.
|
|
\end{equation}
|
|
|
|
Thus, under the condition $h\neq 0$, homogeneous and Cartesian
|
|
representation are equivalent, and we frequently switch between them.
|
|
\footnote{The reason for scaling equations (\ref{conic_hom})
|
|
and (\ref{conic_cart}) by a factor of 2 is purely technical -- we
|
|
want to argue with division-free terms.}
|
|
|
|
$\C$ is called @em{trivial} if $\C=\R^2$ which is equivalent to
|
|
$\r=0$. $\C$ is @em{empty} if $\C=\emptyset$, i.e. if
|
|
(\ref{conic_def_hom}) has no real solutions $x,y,h$ with $h\neq 0$
|
|
(which happens e.g. in case of $\r = (1, 1, 0, 0, 0, 1)$, or
|
|
$\r=(0,0,0,0,0,1)$).
|
|
|
|
$\C$ is invariant under scaling its representation $\r$
|
|
by any nonzero factor. This means, a conic has five degrees of freedom,
|
|
and in fact it holds that any five points uniquely determine a nontrivial
|
|
conic passing through the points. Some care is in place: this does
|
|
@em{not} mean that five points uniquely determine the conic's
|
|
representation, up to scaling. For example, the $x$-axis is a conic
|
|
uniquely determined by any five points on it, but as a representation
|
|
we may choose $\{y=0\}$ or $\{y^2=0\}$. Recall that we have already seen
|
|
two representations of the empty conic which are not multiples of each
|
|
other.
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsection{Conic types}
|
|
@! ---------------------------------------------------------------------------
|
|
\label{types_sec}
|
|
The number
|
|
\begin{equation}
|
|
\label{det}
|
|
\det(\r) := \det \left( \begin{array}{cc} 2r & t \\ t & 2s \end{array}\right)
|
|
\end{equation}
|
|
determines the type of $\C(\r)$. If $\det(\r)>0$,
|
|
$\C$ is an @em{ellipse}, if $\det(\r)<0$, we get a @em{hyperbola}, and
|
|
for $\det(\r)=0$, a @em{parabola} is obtained. While the trivial conic
|
|
is a degenerate parabola equal to the whole plane, any nontrivial one
|
|
consists of at most two simple curves. As a special case, there is a
|
|
conic $\C=\{p\}$ for any point $p=(x_0,y_0)$. It can be specified
|
|
as
|
|
\[
|
|
\C = \{(x,y)\mid (x-x_0)^2 + (y-y_0)^2 = 0\},
|
|
\]
|
|
and a possible representation is
|
|
$\r=(1, 1, 0, -2x_0, -2y_0, x_0^2+y_0^2)$. This implies
|
|
$\det(\r)=4$, so $\C$ is a degenerate ellipse, see
|
|
subsection \ref{orientation_sec}.
|
|
|
|
Note that $\det(\r)> 0$ implies $r,s > 0$ or $r,s<0$ which is equivalent
|
|
to
|
|
\begin{equation}
|
|
M := \left( \begin{array}{cc} 2r & t \\ t & 2s \end{array}\right)
|
|
\end{equation}
|
|
being positive definite ($x^TMx>0$ for $x\neq 0$) or negative definite
|
|
($x^TMx<0$ for $x\neq 0$). In case of $\det(\r)<0$, $M$ is indefinite,
|
|
meaning that $x^TMx$ assumes positive and negative values.
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsection{Symmetry properties}
|
|
@! ---------------------------------------------------------------------------
|
|
\label{symmetry_sec}
|
|
|
|
If the conic $\C(\r)$ is not a parabola, the matrix
|
|
\[
|
|
M = \left(\begin{array}{cc} 2r & t \\ t & 2s \end{array}\right)
|
|
\]
|
|
is regular, and $\C$ has a unique center of symmetry $c$, given as
|
|
\[
|
|
c = -M^{-1}\left(\begin{array}{c} u \\ v \end{array}\right).
|
|
\]
|
|
|
|
With this definition, (\ref{conic_cart}) can
|
|
alternatively been written as
|
|
\begin{equation}
|
|
\label{center_form}
|
|
(p-c)^T M (p-c) + 2w-c^TMc = 0,
|
|
\end{equation}
|
|
$p=(x,y)^T$, from which the symmetry is obvious. In case of a
|
|
parabola, we get an axis of symmetry.
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsection{Orientation and degeneracy}
|
|
@! ---------------------------------------------------------------------------
|
|
\label{orientation_sec}
|
|
|
|
An @em{oriented conic} is a pair $\C_{\r} = (\C(\r),\r)$, i.e. a conic with a
|
|
particular representation. $\C_{\r}$ subdivides $\R^2\setminus \C(\r)$
|
|
into a @em{positive} side, formed by the set of points such that $\r(p)>0$,
|
|
and a @em{negative} side ($\r(p)<0$). Replacing $\r$ with $-\r$ leads to
|
|
an oriented conic with positive and negative sides interchanged. This
|
|
concept of assigning positive and negative sides is purely algebraic.
|
|
|
|
In addition, there is a geometric way of assigning sides to an oriented
|
|
conic which does not depend on $\r$ (like an oriented circle has a bounded
|
|
side, independent from its orientation). To this end, we define the
|
|
@em{convex side} of a conic $\C$ as the the union of the convex
|
|
connected components of $\R^2\setminus \C$. The @em{non-convex side} is
|
|
then just the union of the non-convex components. Figure \ref{orientations}
|
|
depicts the convex sides of an ellipse, hyperbola and parabola, labeled
|
|
with the letter `$c$'.
|
|
|
|
\begin{figure}
|
|
\begin{center}
|
|
\leavevmode
|
|
\epsfxsize=12cm
|
|
%\epsfbox{orientations.eps}
|
|
\end{center}
|
|
\caption{Ellipse, hyperbola and parabola with convex sides}
|
|
\label{orientations}
|
|
\end{figure}
|
|
|
|
A conic is defined to be @em{degenerate} if either its convex side or
|
|
its non-convex side is empty.
|
|
Let us discuss the possibilities for this. First, the trivial conic
|
|
is degenerate, with both sides being empty. The empty conic is degenerate,
|
|
with the non-convex side being empty. An ellipse is
|
|
degenerate if and only if it consists of just one point (and so the
|
|
convex side is empty). A hyperbola is degenerate if and only if it
|
|
contains its center of symmetry $c$. In this case, the hyperbola is a
|
|
pair of lines crossing at $c$, and so the non-convex side is empty.
|
|
A degenerate parabola is either a pair of parallel lines
|
|
or just one line. In both cases, the non-convex side is empty.
|
|
|
|
In the non-degenerate case, the classification of points by positive
|
|
and negative side coincides with the one by convex and non-convex side.
|
|
In the degenerate case, this exactly holds if positive or
|
|
negative side disappear, like for a degenerate ellipse (but not for a
|
|
degenerate hyperbola).
|
|
|
|
An oriented conic $\C_{\r}$ is said to have positive (negative)
|
|
orientation, if and only if the convex side coincides with the
|
|
positive (negative) side. If neither is the case, the orientation
|
|
is zero. Thus, a degenerate ellipse has nonzero orientation,
|
|
but a degenerate hyperbola has not.
|
|
|
|
While it is clear that positive and negative side of a conic are only
|
|
defined with respect to some representation, it is interesting to
|
|
note that even the partition of $\R^2\setminus\C_{\r}$ into
|
|
positive and negative side does in general depend on $\r$. Coming
|
|
back to the conic $\C = \{y=0\} = \{y^2=0\}$, the first representation
|
|
of it leads to nonempty positive and negative side, while in the second
|
|
one, the negative side is empty.
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsection{Ellipses and the volume formula}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
The volume of an ellipse $\E$, $\Vol(\E)$, is defined as the area of
|
|
its convex side. If $\E$ is non-degenerate and presented in center form
|
|
(\ref{center_form}), consider the matrix
|
|
$$A := M / (2w - c^TMc).$$ It is easy to see that $A$ is
|
|
invariant under scaling $\r$ by any nonzero factor. The following
|
|
holds.
|
|
\begin{Lemma}
|
|
\label{ellipse_volume}
|
|
$$\Vol(\E) = \frac{\pi}{\sqrt{\det(A)}}.$$
|
|
\end{Lemma}
|
|
For this note that $\det(M)>0$ implies $\det(A)>0$.
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@section{Class Template {\tt CGAL\_Conic\_2<R>}}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
An object of the class @prg{Conic_2<R>} stores an oriented
|
|
conic $\C_{\r} = (\C(\r),\r)$ by its representation $\r$. The template
|
|
parameter @prg{R} is the representation type (homogeneous or Cartesian). In
|
|
the sequel, a conic always means an oriented conic.
|
|
|
|
@macro <Conic_2 declaration> = @begin
|
|
template < class R>
|
|
class Conic_2;
|
|
|
|
@end
|
|
|
|
The class @prg{Conic_2<R>} has several member functions, some of which
|
|
can be considered general purpose (and are declared public), others are
|
|
more specialized and needed only by the class template
|
|
@prg{Optimisation_ellipse_2<R>} -- they appear as private methods.
|
|
|
|
@macro <Optimisation_ellipse_2 declaration> = @begin
|
|
template < class _R >
|
|
class Optimisation_ellipse_2;
|
|
@end
|
|
|
|
@macro <Optimisation_ellipse_2 I/O operator declaration> = @begin
|
|
template < class _R >
|
|
CGAL::Window_stream&
|
|
operator << ( CGAL::Window_stream&,
|
|
const CGAL::Optimisation_ellipse_2<_R>&);
|
|
@end
|
|
|
|
As a general rule, a method only qualifies for the public domain if
|
|
it can be specified without referring to particular values of $\r$, in
|
|
other words, if its behavior only depends on the equivalence class of
|
|
all positive multiples of $\r$. For example, this holds for the sign of
|
|
$\r(p)$, $p$ some point, but not for the value of $\r(p)$. As usual, there
|
|
is an exception to this rule: we have public methods to retrieve the
|
|
components of the representation $\r$.
|
|
|
|
All calls to member functions are
|
|
directly mapped to calls of corresponding methods declared by
|
|
the conic class @prg{R::Conic_2} of the representation type @prg{R}.
|
|
|
|
@macro <Conic_2 interface and implementation> = @begin
|
|
template < class _R>
|
|
class Conic_2 : public _R::Conic_2 {
|
|
|
|
friend class Optimisation_ellipse_2<_R>;
|
|
|
|
public:
|
|
|
|
// types
|
|
typedef _R R;
|
|
typedef typename _R::RT RT;
|
|
typedef typename _R::FT FT;
|
|
typedef typename _R::Conic_2 _Conic_2;
|
|
|
|
// construction
|
|
@<Conic_2 constructors>
|
|
|
|
// general access
|
|
@<Conic_2 general access methods>
|
|
|
|
// type related access
|
|
@<Conic_2 type access methods>
|
|
|
|
// orientation related access
|
|
@<Conic_2 orientation access methods>
|
|
|
|
// comparisons
|
|
@<Conic_2 comparison methods>
|
|
|
|
// set methods
|
|
@<Conic_2 set methods>
|
|
|
|
private:
|
|
@<Conic_2 private methods>
|
|
};
|
|
|
|
@end
|
|
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsection{Construction}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
We have a default constructor and a constructor to initialize a conic
|
|
at coordinate level, providing the components of a representation $\r$.
|
|
The orientation of the conic is determined by $\r$, as described in
|
|
subsection \ref{orientation_sec}.
|
|
|
|
@macro <Conic_2 constructors> = @begin
|
|
Conic_2 ()
|
|
{}
|
|
|
|
Conic_2 (RT r, RT s, RT t, RT u, RT v, RT w)
|
|
: R::Conic_2 (r, s, t, u, v, w)
|
|
{}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsection{General access}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
We provide access to the coordinates of the representation $\r$.
|
|
Even if $\C_{\r}$ was constructed from some specific characteristic
|
|
vector, the return coordinates do not necessarily belong to this
|
|
vector (but at least to a nonnegative multiple of it). For example,
|
|
if the coordinates are from some integer domain, the implementation might
|
|
decide to divide all of them by their gcd to get smaller numbers. (In this
|
|
implementation, this does not happen.)
|
|
|
|
@macro <Conic_2 general access methods> += @begin
|
|
RT r () const
|
|
{
|
|
return _Conic_2::r();
|
|
}
|
|
|
|
RT s () const
|
|
{
|
|
return _Conic_2::s();
|
|
}
|
|
|
|
RT t () const
|
|
{
|
|
return _Conic_2::t();
|
|
}
|
|
|
|
RT u () const
|
|
{
|
|
return _Conic_2::u();
|
|
}
|
|
|
|
RT v () const
|
|
{
|
|
return _Conic_2::v();
|
|
}
|
|
|
|
RT w () const
|
|
{
|
|
return _Conic_2::w();
|
|
}
|
|
|
|
@end
|
|
|
|
We can obtain the center of symmetry of $\C_{\r}$. Precondition is that
|
|
$\C$ is not a parabola.
|
|
|
|
@macro <Conic_2 general access methods> += @begin
|
|
CGAL::Point_2<R> center () const
|
|
{
|
|
return _Conic_2::center();
|
|
}
|
|
|
|
@end
|
|
|
|
The symmetry axis of a parabola and the two axes of an ellipse resp. a
|
|
hyperbola require irrational coordinates in general, therefore they cannot
|
|
be added here without further assumptions about the representation type
|
|
@prg{R}.
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsection{Type related access}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
Here we have access methods and predicates related to the type of
|
|
$\C_{\r}$.
|
|
We can either directly retrieve the type or ask whether $\C_{\r}$ is of
|
|
some specific type. To realize the former, we provide a suitable
|
|
enumeration type.
|
|
|
|
@macro <Conic_type declaration> = @begin
|
|
enum Conic_type
|
|
{
|
|
HYPERBOLA = -1,
|
|
PARABOLA,
|
|
ELLIPSE
|
|
};
|
|
|
|
@end
|
|
|
|
@macro <Conic_2 type access methods> += @begin
|
|
Conic_type conic_type () const
|
|
{
|
|
return _Conic_2::conic_type();
|
|
}
|
|
|
|
bool is_hyperbola () const
|
|
{
|
|
return _Conic_2::is_hyperbola();
|
|
}
|
|
|
|
bool is_parabola () const
|
|
{
|
|
return _Conic_2::is_parabola();
|
|
}
|
|
|
|
bool is_ellipse () const
|
|
{
|
|
return _Conic_2::is_ellipse();
|
|
}
|
|
|
|
@end
|
|
|
|
We have three more predicates that -- in combination with the above --
|
|
yield a finer access to the type, namely a test for emptiness, triviality
|
|
and degeneracy, where both the empty and the trivial conic are also
|
|
degenerate.
|
|
|
|
@macro <Conic_2 type access methods> += @begin
|
|
bool is_empty () const
|
|
{
|
|
return _Conic_2::is_empty();
|
|
}
|
|
|
|
bool is_trivial () const
|
|
{
|
|
return _Conic_2::is_trivial();
|
|
}
|
|
|
|
bool is_degenerate () const
|
|
{
|
|
return _Conic_2::is_degenerate();
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsection{Orientation related access}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
We can retrieve the orientation of a conic as defined in subsection
|
|
\ref{orientation_sec}. A notable difference to the class
|
|
@prg{Circle_2<R>} is that the orientation can be zero.
|
|
|
|
@macro <Conic_2 orientation access methods> += @begin
|
|
CGAL::Orientation orientation () const
|
|
{
|
|
return _Conic_2::orientation ();
|
|
}
|
|
|
|
@end
|
|
|
|
The following methods classify points according to the positive
|
|
and negative side of $\C_{\r}$. We can either retrieve the oriented
|
|
side directly or ask whether the point lies on some specific side.
|
|
|
|
@macro <Conic_2 orientation access methods> += @begin
|
|
CGAL::Oriented_side oriented_side (const CGAL::Point_2<R>& p) const
|
|
{
|
|
return _Conic_2::oriented_side (p);
|
|
}
|
|
|
|
bool has_on_positive_side (const CGAL::Point_2<R>& p) const
|
|
{
|
|
return _Conic_2::has_on_positive_side (p);
|
|
}
|
|
|
|
bool has_on_negative_side (const CGAL::Point_2<R>& p) const
|
|
{
|
|
return _Conic_2::has_on_negative_side (p);
|
|
}
|
|
|
|
bool has_on_boundary (const CGAL::Point_2<R>& p) const
|
|
{
|
|
return _Conic_2::has_on_boundary (p);
|
|
}
|
|
|
|
bool has_on (const CGAL::Point_2<R>& p) const
|
|
{
|
|
return _Conic_2::has_on (p);
|
|
}
|
|
|
|
@end
|
|
|
|
Then we have the classification according to convex and non-convex side.
|
|
Because this is a natural generalization of the classification according
|
|
to bounded and unbounded side, we `extend' the type @prg{Bounded_side}.
|
|
|
|
@macro <Convex_side declaration> = @begin
|
|
typedef CGAL::Bounded_side Convex_side;
|
|
const Convex_side ON_CONVEX_SIDE = CGAL::ON_BOUNDED_SIDE;
|
|
const Convex_side ON_NONCONVEX_SIDE = CGAL::ON_UNBOUNDED_SIDE;
|
|
|
|
@end
|
|
|
|
@macro <Conic_2 orientation access methods> += @begin
|
|
Convex_side convex_side (const CGAL::Point_2<R>& p) const
|
|
{
|
|
return _Conic_2::convex_side (p);
|
|
}
|
|
|
|
bool has_on_convex_side (const CGAL::Point_2<R>& p) const
|
|
{
|
|
return _Conic_2::has_on_convex_side (p);
|
|
}
|
|
|
|
bool has_on_nonconvex_side (const CGAL::Point_2<R>& p) const
|
|
{
|
|
return _Conic_2::has_on_nonconvex_side (p);
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsection{Comparison methods}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
We provide tests for equality and inequality of two conics.
|
|
|
|
@macro <Conic_2 comparison methods> = @begin
|
|
bool operator == ( const Conic_2<_R>& c) const
|
|
{
|
|
return _Conic_2::operator == ( (Conic_2)c);
|
|
}
|
|
|
|
bool operator != ( const Conic_2<_R>& c) const
|
|
{
|
|
return( ! operator == ( c));
|
|
}
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsection{Public set methods}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
Apart from the most basic way of constructing an oriented
|
|
conic from the coordinates of
|
|
a representation, there are methods at a higher level, building a conic
|
|
from points or other conics. Such methods appear here; they are not given
|
|
as constructors, but as `set' functions that modify an already existing
|
|
conic. But of course, there is also a set function that works on the
|
|
coordinate level.
|
|
|
|
@macro <Conic_2 set methods> += @begin
|
|
void set (RT r, RT s, RT t,
|
|
RT u, RT v, RT w)
|
|
{
|
|
_Conic_2::set (r, s, t, u, v, w);
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{Opposite conic}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
We can obtain the conic $\C_{-\r}$ of opposite orientation, having positive
|
|
and negative sides interchanged (convex and non-convex side stay the same,
|
|
of course). Note that if $\C_{\r}$ has zero orientation, so has $\C_{-\r}$.
|
|
|
|
@macro <Conic_2 set methods> += @begin
|
|
void set_opposite ()
|
|
{
|
|
_Conic_2::set_opposite();
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{Pair of lines through four points}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
The method @prg{set_linepair} builds the conic equal to union of the
|
|
lines $\overline{p_1p_2}$ and $\overline{p_3p_4}$. This is either a
|
|
degenerate hyperbola in case the two lines are not parallel, or a
|
|
degenerate parabola. The precondition is that $p_1\neq p_2$ and
|
|
$p_3\neq p_4$. The positive side is the region to the left resp. to
|
|
the right of both oriented lines $\overline{p_1p_2}, \overline{p_3p_4}$.
|
|
|
|
@macro <Conic_2 set methods> += @begin
|
|
void set_linepair (const CGAL::Point_2<R>& p1, const CGAL::Point_2<R>& p2,
|
|
const CGAL::Point_2<R>& p3, const CGAL::Point_2<R>& p4)
|
|
{
|
|
_Conic_2::set_linepair (p1, p2, p3, p4);
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{Smallest ellipse through three points}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
The following method @prg{set_ellipse} generates the ellipse of smallest
|
|
volume through $p_1,p_2$
|
|
and $p_3$. This is at the same time the unique ellipse through these points
|
|
whose center is equal to the center of gravity of the points.
|
|
Precondition is that $p_1,p_2,p_3$ are not collinear. The orientation of
|
|
the ellipse is the orientation of the point triple.
|
|
|
|
@macro <Conic_2 set methods> += @begin
|
|
void set_ellipse (const CGAL::Point_2<R>& p1, const CGAL::Point_2<R>& p2,
|
|
const CGAL::Point_2<R>& p3)
|
|
{
|
|
_Conic_2::set_ellipse (p1, p2, p3);
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{Some ellipse through four points in convex position}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
We have another @prg{set_ellipse} method, constructing an ellipse from
|
|
four given points, assumed to be in convex position (if not, the result
|
|
will still be some conic through the points, but not an ellipse).
|
|
The result will be just some ellipse through the points; in particular, it
|
|
will usually @em{not} be the smallest one passing through the points. The
|
|
orientation of the ellipse can be specified and defaults to positive.
|
|
|
|
@macro <Conic_2 set methods> += @begin
|
|
void set_ellipse (const CGAL::Point_2<R>& p1, const CGAL::Point_2<R>& p2,
|
|
const CGAL::Point_2<R>& p3, const CGAL::Point_2<R>& p4,
|
|
CGAL::Orientation o = POSITIVE)
|
|
{
|
|
_Conic_2::set_ellipse (p1, p2, p3, p4, o);
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{Unique conic through five points}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
The method @prg{set} generates the unique nontrivial conic containing
|
|
the points $p_1,p_2,p_3,p_4,p_5$. Precondition is that all points are
|
|
distinct. The orientation can be specified but is automatically set to
|
|
zero if the resulting conic has zero orientation.
|
|
|
|
@macro <Conic_2 set methods> += @begin
|
|
void set (const CGAL::Point_2<R>& p1, const CGAL::Point_2<R>& p2,
|
|
const CGAL::Point_2<R>& p3, const CGAL::Point_2<R>& p4,
|
|
const CGAL::Point_2<R>& p5,
|
|
CGAL::Orientation o = POSITIVE)
|
|
{
|
|
_Conic_2::set (p1, p2, p3, p4, p5, o);
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsection{Private methods}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{Linear combination of conics}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
The linear combination of two conics $\C_{\r_1}$ and $\C_{\r_2}$ with
|
|
coefficients $a_1,a_2$ is the conic with representation
|
|
$\r := a_1\r_1+a_2\r_2$. This is not a geometric operation,
|
|
because the result depends on $\r_1,\r_2$ and not only on
|
|
$\C_{\r_1}$ and $\C_{\r_2}$.
|
|
|
|
@macro <Conic_2 private methods> += @begin
|
|
void set_linear_combination (
|
|
const RT& a1, const Conic_2<R>& c1,
|
|
const RT& a2, const Conic_2<R>& c2)
|
|
{
|
|
_Conic_2::set_linear_combination (a1, c1, a2, c2);
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{Two pairs of lines through four points}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
The following method constructs two line-pairs through four
|
|
given points $p_1,p_2,p_3,p_4$, assumed to be in convex position. If
|
|
the points are enumerated in clockwise or counterclockwise order, these
|
|
will be the line-pairs $\overline{p_1p_2}\cup\overline{p_3p_4}$ and
|
|
$\overline{p_2p_3}\cup\overline{p_4p_1}$. Otherwise, points are renamed
|
|
first to achieve (counter)clockwise orientation. The two resulting
|
|
conics are passed to the method by reference, and their orientations
|
|
depend on the points as described in the method @prg{set_linepair}.
|
|
If $p_1,p_2,p_3,p_4$ are not in convex position, the method still
|
|
computes two line-pairs through the points, but it is not specified
|
|
which ones. This is a static method.
|
|
|
|
@macro <Conic_2 private methods> += @begin
|
|
static void set_two_linepairs (const CGAL::Point_2<R>& p1,
|
|
const CGAL::Point_2<R>& p2,
|
|
const CGAL::Point_2<R>& p3,
|
|
const CGAL::Point_2<R>& p4,
|
|
Conic_2<R>& pair1,
|
|
Conic_2<R>& pair2)
|
|
{
|
|
_Conic_2::set_two_linepairs (p1, p2, p3, p4, pair1, pair2);
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{Some ellipse from two pairs of lines}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
We have a method to construct an ellipse through $p_1,p_2,p_3,p_4$ in convex
|
|
position, using two line-pairs through the points as
|
|
obtained from a call to the method @prg{set_two_linepairs} with parameters
|
|
$p_1,p_2,p_3,p_4$. The orientation of the ellipse is not specified. In case
|
|
the argument conics have not been constructed by the method
|
|
@prg{set_two_linepairs} in the described way, the resulting
|
|
conic is unspecified.
|
|
|
|
@macro <Conic_2 private methods> += @begin
|
|
void set_ellipse (const Conic_2<R>& pair1,
|
|
const Conic_2<R>& pair2)
|
|
{
|
|
_Conic_2::set_ellipse (pair1, pair2);
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{Conic from two conics and a point}
|
|
@!----------------------------------------------------------------------------
|
|
|
|
Assuming that we already have two conics intersecting in four points,
|
|
the following alternative @prg{set} method is more efficient in constructing
|
|
the unique nontrivial conic through them and another point $p$. It accepts
|
|
two conics $\C_{\r_1}$ and $\C_{\r_2}$ and a point $p$, and computes some
|
|
conic $\C_{\r}$ that goes through $\C_{\r_1}\cap \C_{\r_2} \cup\{p\}$.
|
|
The method may construct the trivial conic.
|
|
This happens if one of $\C_{\r_1}$ and $\C_{\r_2}$ is
|
|
already trivial, $\C(\r_1)=\C(\r_2)$ holds, or if
|
|
$p\in \C_{\r_1}\cap \C_{\r_2}$. In case of nonzero orientation, the
|
|
actual orientation of $\C_{\r}$ is unspecified.
|
|
|
|
@macro <Conic_2 private methods> += @begin
|
|
void set (const Conic_2<R>& c1, const Conic_2<R>& c2,
|
|
const CGAL::Point_2<R>& p)
|
|
{
|
|
_Conic_2::set( c1, c2, p); analyse();
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{Volume derivative of an ellipse}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
Given an ellipse $\E$ with representation $\r$ and some vector
|
|
$\d \r := (\d r, \d s, \d t, \d u, \d v, \d w)$, define
|
|
\[
|
|
\E(\tau) = \C_{\r + \tau \d \r}.
|
|
\]
|
|
|
|
For small values of $\tau$, $\E(\tau)$ is still an ellipse. The method
|
|
@prg{vol_derivative} computes the sign of
|
|
\[
|
|
\frac{\partial}{\partial \tau} \Vol(\E(\tau))\left|_{\tau=0}\right.,
|
|
\]
|
|
i.e. decides how the volume develops when going from $\E(0)$ `in direction'
|
|
$\d\r$. If $\E$ is not an ellipse, the result is meaningless.
|
|
|
|
@macro <Conic_2 private methods> += @begin
|
|
CGAL::Sign vol_derivative (RT dr, RT ds,
|
|
RT dt, RT du,
|
|
RT dv, RT dw) const
|
|
{
|
|
return _Conic_2::vol_derivative (dr, ds, dt, du, dv, dw);
|
|
}
|
|
|
|
@end
|
|
|
|
A related method computes the value $\tau^*$ such that
|
|
$\Vol(\E(\tau))$ assumes its minimum over the set
|
|
$T=\{\tau \mid \E(\tau) \mbox{~is an ellipse}\}$. Precondition is
|
|
that this minimum exists. If so,
|
|
$\tau^*$ is a local extremum of the volume function. $\tau^*$ might be
|
|
irrational, but because the value is only used for drawing the ellipse
|
|
$\E(\tau^*)$, a @prg{double}-approximation suffices.
|
|
As before, if $\E$ is not an ellipse, the result is meaningless.
|
|
|
|
@macro <Conic_2 private methods> += @begin
|
|
double vol_minimum (RT dr, RT ds,
|
|
RT dt, RT du,
|
|
RT dv, RT dw) const
|
|
{
|
|
return _Conic_2::vol_minimum (dr, ds, dt, du, dv, dw);
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsection{IO routines}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
@macro <Conic_2 I/O routines> = @begin
|
|
template< class _R>
|
|
std::ostream& operator << ( std::ostream& os, const Conic_2<_R>& c)
|
|
{
|
|
return( os << c.r() << ' ' << c.s() << ' ' << c.t() << ' '
|
|
<< c.u() << ' ' << c.v() << ' ' << c.w());
|
|
}
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{Output to {\tt CGAL\_Window\_stream}}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
We provide an operator to write a conic to a
|
|
@prg{Window_stream}. The function is not extraordinarily
|
|
efficient but simple (and works without `understanding' the conic;
|
|
other methods -- like the one by Maxwell \& Baker -- need to
|
|
determine the conic type first, compute start values etc.). The
|
|
method works in image space, proceeding in two phases.
|
|
|
|
Phase 1 draws the conic in $x$-direction. This means that the width
|
|
of the output window is scanned pixel-wise, for any $x$-value
|
|
computing the at most two corresponding values $y_1,y_2$ such that
|
|
$(x,y_1),(x,y_2)\in \C$. (This is done by solving a quadratic
|
|
equation). The resulting pixels are stored for output, which is
|
|
triggered after all $x$-values have been processed in this way.
|
|
|
|
Phase 2 draws the conic in $y$-direction, proceeding
|
|
similarly. Phases 1 and 2 together ensure that there are no gaps in
|
|
the drawn curve(s).
|
|
|
|
@macro<Conic_2 graphical output operator> = @begin
|
|
#ifdef CGAL_CONIC_2_H
|
|
#ifndef CGAL_IO_WINDOW_STREAM_CONIC_2
|
|
#define CGAL_IO_WINDOW_STREAM_CONIC_2
|
|
|
|
template< class R >
|
|
CGAL::Window_stream&
|
|
operator << ( CGAL::Window_stream& ws, const CGAL::Conic_2<R>& c)
|
|
{
|
|
// length of a pixel in window-coordinates
|
|
double pixel = 1/ws.scale();
|
|
|
|
// pixel dimensions of window
|
|
int width = (int)((ws.xmax() - ws.xmin()) * ws.scale()) + 1,
|
|
height = (int)((ws.ymax() - ws.ymin()) * ws.scale()) + 1,
|
|
dim = std::max( width, height);
|
|
|
|
// pixel coordinates, stored for faster output
|
|
double *X = new double [2*dim];
|
|
double *Y = new double [2*dim];
|
|
|
|
// actual number of pixels to be drawn
|
|
int pixels;
|
|
|
|
// conic coordinates
|
|
double r = CGAL::to_double (c.r()),
|
|
s = CGAL::to_double (c.s()),
|
|
t = CGAL::to_double (c.t()),
|
|
u = CGAL::to_double (c.u()),
|
|
v = CGAL::to_double (c.v()),
|
|
w = CGAL::to_double (c.w());
|
|
|
|
// Phase I (drawing in x-direction)
|
|
pixels = 0;
|
|
// solve conic equation for y
|
|
if (s != 0.0)
|
|
for (double x = ws.xmin(); x <= ws.xmax(); x+=pixel) {
|
|
double discr = (t*t-4.0*r*s)*(x*x) + (2.0*t*v-4.0*s*u)*x +
|
|
v*v - 4.0*s*w;
|
|
if (discr >= 0.0) {
|
|
double y1 = (-t*x - v - CGAL::sqrt(discr))/(2.0*s);
|
|
double y2 = (-t*x - v + CGAL::sqrt(discr))/(2.0*s);
|
|
X[pixels] = x; Y[pixels++] = y1;
|
|
X[pixels] = x; Y[pixels++] = y2; } }
|
|
else
|
|
for (double x = ws.xmin(); x <= ws.xmax(); x+=pixel) {
|
|
double denom = t*x + v;
|
|
if (denom != 0.0) {
|
|
double y = -(r*x*x + u*x + w)/denom;
|
|
X[pixels] = x; Y[pixels++] = y; } }
|
|
ws.draw_pixels (pixels, X, Y);
|
|
|
|
// Phase II (drawing in y-direction)
|
|
pixels = 0;
|
|
// solve conic equation for x
|
|
if (r != 0.0)
|
|
for (double y = ws.ymin(); y <= ws.ymax(); y+=pixel) {
|
|
double discr = (t*t-4.0*r*s)*(y*y) + (2.0*t*u-4.0*r*v)*y +
|
|
u*u - 4.0*r*w;
|
|
if (discr >= 0.0) {
|
|
double x1 = (-t*y - u - CGAL::sqrt(discr))/(2.0*r);
|
|
double x2 = (-t*y - u + CGAL::sqrt(discr))/(2.0*r);
|
|
X[pixels] = x1; Y[pixels++] = y;
|
|
X[pixels] = x2; Y[pixels++] = y; } }
|
|
else
|
|
for (double y = ws.ymin(); y <= ws.ymax(); y+=pixel) {
|
|
double denom = t*y + u;
|
|
if (denom != 0.0) {
|
|
double x = -(s*y*y + v*y + w)/denom;
|
|
X[pixels] = x; Y[pixels++] = y; } }
|
|
ws.draw_pixels (pixels, X, Y);
|
|
|
|
// free memory
|
|
delete[] Y;
|
|
delete[] X;
|
|
|
|
return( ws);
|
|
}
|
|
|
|
#endif // CGAL_IO_WINDOW_STREAM_CONIC_2
|
|
#endif // CGAL_CONIC_2_H
|
|
@end
|
|
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@section{Class Templates {\tt CGAL\_ConicHPA2<PT,DA>}
|
|
and {\tt CGAL\_ConicCPA2<PT,DA>}}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
The two classes described in this section realize the functionality of
|
|
the general class @prg{Conic_2<R>}, distinguished between Cartesian and
|
|
homogeneous representation. Unlike it is practice in the kernel, the
|
|
template parameters are not field and/or ring type but point and data
|
|
accessor type. The reason is that the classes described here are also
|
|
used to build traits class adapters for the
|
|
class @prg{Min_ellipse_2<Traits>},
|
|
where the adapters themselves are templatized with a
|
|
point and data accessor type.
|
|
|
|
To realize this scheme, the representation type @prg{R} of the
|
|
class @prg{Conic_2<R>} is enhanced with a data accessor class -- it will
|
|
be the canonical one, realizing access to the coordinates of a
|
|
@prg{Point_2<R>} (see files @prg{homogeneous_rep.h} and
|
|
@prg{cartesian_rep.h}).
|
|
|
|
@macro <ConicHPA2 declaration> = @begin
|
|
template < class PT, class DA>
|
|
class ConicHPA2;
|
|
|
|
template < class PT, class DA>
|
|
class _Min_ellipse_2_adapterH2__Ellipse;
|
|
|
|
@end
|
|
|
|
|
|
@macro <ConicCPA2 declaration> = @begin
|
|
template < class PT, class DA>
|
|
class ConicCPA2;
|
|
|
|
template < class PT, class DA>
|
|
class _Min_ellipse_2_adapterC2__Ellipse;
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsection{Requirements for template parameters {\tt PT} and {\tt DA}}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
For @prg{ConicHPA2},
|
|
the data accessor type @prg{DA} must define a
|
|
ring type @prg{RT} and a function @prg{get} to access $x$-, $y$- and
|
|
$h$-coordinate of a point $p=(x,y,h)$ of type @prg{PT}. The type @prg{RT} is
|
|
the coordinate type of @prg{PT}, and we expect @prg{PT} to have a constructor
|
|
with three arguments $x,y,h$ of type @prg{RT}. In addition, we need an
|
|
assignment constructor (for the @prg{center} method). A minimal interface
|
|
of a type @prg{DA} for the homogeneous representation would look as follows.
|
|
|
|
@macro <ConicHPA2 DA requirements> zero = @begin
|
|
class DA
|
|
{
|
|
public:
|
|
// ring type
|
|
typedef /* some CGAL-conform number type */ RT;
|
|
// coordinate access
|
|
void get (const PT& p, RT& x, RT& y, RT& h);
|
|
};
|
|
|
|
@end
|
|
|
|
In the Cartesian case, @prg{DA} must declare a field type @prg{FT} and
|
|
access via @prg{get} to the $x$- and $y$-coordinate of a point $p=(x,y)$
|
|
of type @prg{FT}. @prg{PT} is expected to declare a constructor with two
|
|
arguments of type @prg{FT}. In addition, we need an assignment constructor.
|
|
A minimal interface of a type @prg{DA} in the Cartesian case would
|
|
look as follows.
|
|
|
|
@macro <ConicCPA2 DA requirements> zero = @begin
|
|
class DA
|
|
{
|
|
public:
|
|
// field type
|
|
typedef /* some CGAL-conform number type */ FT;
|
|
// coordinate access
|
|
void get (const PT& p, FT& x, FT& y);
|
|
};
|
|
|
|
@end
|
|
|
|
In both cases, class @prg{DA} needs to provide a default constructor.
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsection{Interfaces}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
The interfaces look similar to the interface of the class
|
|
@prg{Conic_2<R>} in the sense that for any public (private)
|
|
member function of @prg{Conic_2<R>}, we have a corresponding
|
|
public (private) member function here. These are the @em{high-level}
|
|
member functions.
|
|
|
|
In addition, there are
|
|
private data members to store the conic representation and a
|
|
couple of additional @em{low-level} protected member functions. The
|
|
reason for making them protected is that we do not want them to show
|
|
up explicitly in the interface of the class @prg{Conic_2<R>}, but
|
|
on the other hand, friends of @prg{Conic_2<R>} should be able to
|
|
use them.
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{{\tt CGAL\_ConicHPA2<PT,DA>}}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
@macro <ConicHPA2 interface and implementation> = @begin
|
|
template < class _PT, class _DA>
|
|
class ConicHPA2
|
|
{
|
|
public:
|
|
// types
|
|
typedef _PT PT;
|
|
typedef _DA DA;
|
|
typedef typename _DA::RT RT;
|
|
|
|
private:
|
|
friend class Conic_2< CGAL::Homogeneous<RT> >;
|
|
friend class _Min_ellipse_2_adapterH2__Ellipse<PT,DA>;
|
|
|
|
@<ConicHPA2 private data members>
|
|
@<ConicHPA2 private member functions>
|
|
|
|
protected:
|
|
@<ConicHPA2 protected member functions>
|
|
|
|
public:
|
|
@<ConicHPA2 public member functions>
|
|
};
|
|
@end
|
|
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{{\tt CGAL\_ConicCPA2<PT,DA>}}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
@macro <ConicCPA2 interface and implementation> = @begin
|
|
template < class _PT, class _DA>
|
|
class ConicCPA2
|
|
{
|
|
public:
|
|
// types
|
|
typedef _PT PT;
|
|
typedef _DA DA;
|
|
typedef typename _DA::FT FT;
|
|
|
|
private:
|
|
friend class Conic_2< CGAL::Cartesian<FT> >;
|
|
friend class _Min_ellipse_2_adapterC2__Ellipse<PT,DA>;
|
|
|
|
@<ConicCPA2 private data members>
|
|
@<ConicCPA2 private member functions>
|
|
|
|
protected:
|
|
@<ConicCPA2 protected member functions>
|
|
|
|
public:
|
|
@<ConicCPA2 public member functions>
|
|
|
|
};
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@section{Implementation of Class templates {\tt CGAL\_ConicCPA2<PT,DA>} and
|
|
{\tt CGAL\_ConicCPA2<PT,DA>}}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
We implement the classes in their interface to cope with insufficiencies
|
|
of the GNU compiler concerning scope operators in typenames. Because
|
|
the implementations of most member functions are very similar for
|
|
both representations, we always write them down in parallel.
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsection{Private data members}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
An oriented conic $\C_{\r}$ is stored by its representation $\r$ and
|
|
certain @em{derived data}. These data are
|
|
\begin{itemize}
|
|
\item the type of $\C_{\r}$,
|
|
\item the orientation of $\C_{\r}$,
|
|
\item degeneracy information, consisting of three flags indicating
|
|
whether $\C_{\r}$ is empty, trivial or degenerate.
|
|
\end{itemize}
|
|
Although type, orientation and degeneracy information can be retrieved from
|
|
$\r$, it is more efficient to store them, because for example, repeated convex
|
|
side tests on the same conic but with different points access these data
|
|
over and over again.
|
|
|
|
@macro <ConicHPA2 private data members> = @begin
|
|
DA dao;
|
|
RT _r, _s, _t, _u, _v, _w;
|
|
Conic_type type;
|
|
CGAL::Orientation o;
|
|
bool empty, trivial, degenerate;
|
|
|
|
@end
|
|
|
|
@macro <ConicCPA2 private data members> = @begin
|
|
DA dao;
|
|
FT _r, _s, _t, _u, _v, _w;
|
|
Conic_type type;
|
|
CGAL::Orientation o;
|
|
bool empty, trivial, degenerate;
|
|
|
|
@end
|
|
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsection{Low-level private member functions}
|
|
@! ---------------------------------------------------------------------------
|
|
\label{private_methods}
|
|
Let's start with the low-level members that do not have a counterpart
|
|
in the interface of the class @prg{Conic_2<R>}.
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{Determinant}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
The function @prg{det} just
|
|
computes $\det(\r)=4rs-t^2$, and as mentioned in subsection
|
|
\ref{types_sec}, this value determines the type of the conic.
|
|
|
|
@macro <ConicHPA2 protected member functions> += @begin
|
|
RT det () const
|
|
{
|
|
return RT(4)*s()*r() - t()*t();
|
|
}
|
|
|
|
@end
|
|
|
|
@macro <ConicCPA2 protected member functions> += @begin
|
|
FT det () const
|
|
{
|
|
return FT(4)*s()*r() - t()*t();
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{Conic analysis}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
This method is the most important low-level method. It
|
|
initializes the derived data from the representation $\r$, by first
|
|
determining the conic's type and then handling the three possible types in
|
|
a @prg{case} statement.
|
|
|
|
@macro <ConicHPA2 protected member functions> += @begin
|
|
void analyse( )
|
|
{
|
|
RT d = det();
|
|
type = (Conic_type)(CGAL_NTS sign(d));
|
|
switch (type) {
|
|
case HYPERBOLA:
|
|
{
|
|
@<analyse hyperbola, homogeneous case>
|
|
}
|
|
break;
|
|
case PARABOLA:
|
|
{
|
|
@<analyse parabola, homogeneous case>
|
|
}
|
|
break;
|
|
case ELLIPSE:
|
|
{
|
|
@<analyse ellipse, homogeneous case>
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@macro <ConicCPA2 protected member functions> += @begin
|
|
void analyse( )
|
|
{
|
|
FT d = det();
|
|
type = (Conic_type)(CGAL_NTS sign(d));
|
|
switch (type) {
|
|
case HYPERBOLA:
|
|
{
|
|
@<analyse hyperbola, Cartesian case>
|
|
}
|
|
break;
|
|
case PARABOLA:
|
|
{
|
|
@<analyse parabola, Cartesian case>
|
|
}
|
|
break;
|
|
case ELLIPSE:
|
|
{
|
|
@<analyse ellipse, Cartesian case>
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
Let us first deal with the case where
|
|
$\C_{\r}$ is a hyperbola or ellipse. Then we have seen in
|
|
subsection \ref{symmetry_sec} that $\C(\r)$ has a center of symmetry $c$ and
|
|
can be written in the form
|
|
\[
|
|
\{p\mid (p-c)^T M (p-c) + 2w-c^TMc = 0\},
|
|
\]
|
|
|
|
Moreover, $\C(\r)$ is degenerate if the center lies on the conic.
|
|
This is the case if and only if $z := 2w-c^TMc=0$. To compute this
|
|
value $z$, we go back to the formulas of subsection \ref{symmetry_sec}, where
|
|
we have seen that $$c = -M^{-1} \left(\begin{array}{c} u \\ v \end{array}
|
|
\right),$$
|
|
therefore
|
|
$$c^TMc = (u,v)M^{-1}\left(\begin{array}{c} u \\ v \end{array}
|
|
\right) = \frac{1}{\det(M)} (u,v) \left(
|
|
\begin{array}{cc} 2s & -t \\ -t & 2r \end{array}\right)
|
|
\left(\begin{array}{c} u \\ v \end{array} \right)
|
|
= \frac{1}{\det(M)}(2u^2 s + 2v^2 r - 2uvt).$$
|
|
This means,
|
|
\begin{equation}
|
|
\label{z}
|
|
z = 2 \left(w - \frac{1}{\det(M)}(u^2 s + v^2 r - uvt)\right).
|
|
\end{equation}
|
|
|
|
To avoid divisions, we consider the value $z'=\det(M)z/2$ which satisfies
|
|
$$
|
|
z' = \det(M) w - u^2 s - v^2 r + uvt.
|
|
$$
|
|
$z'$ has the same sign as $z$ in case of an ellipse, and opposite sign
|
|
in case of a hyperbola.
|
|
|
|
To proceed further, we note that only a parabola can be trivial.
|
|
Moreover, a hyperbola cannot be empty (the matrix $M$ is indefinite,
|
|
therefore $x^TMx$ assumes arbitrary real values). In case of an ellipse,
|
|
$M$ is either positive or negative definite, meaning $r>0$ or $r<0$.
|
|
In the former case, the ellipse is empty if and only if $z>0$, in the
|
|
latter case, $z<0$ leads to an empty ellipse. Summarizing, the ellipse
|
|
is empty iff $rz>0$, equivalently $rz'>0$
|
|
|
|
Now consider orientation. A hyperbola is in positive
|
|
orientation if and only if its center is on the negative
|
|
side, equivalently $z<0$, or $z'>0$. Similarly, the orientation
|
|
is negative in case of $z'<0$. For $z=z'=0$,
|
|
the hyperbola is degenerate, and so its orientation is zero.
|
|
|
|
A non-degenerate ellipse, on the other hand, has positive orientation
|
|
if and only if its center is in the positive side, equivalently, if
|
|
$z > 0$, or $z' > 0$. This is equivalent to $M$ being negative
|
|
definite, or $r<0$. In case of the degenerate ellipse $\E=\{c\}$, we have
|
|
$z=z'=0$, and the orientation is positive if and only if the negative side
|
|
is nonempty (the positive side must agree with the empty convex side). As
|
|
before, this is equivalent to $M$ being negative definite, or $r<0$.
|
|
|
|
In contrast to this, the empty ellipse has positive orientation if and
|
|
only if the negative side is empty (the positive side must agree
|
|
with the convex side, which is the whole plane in this case). For
|
|
this, we get the equivalent condition $r>0$.
|
|
|
|
@macro <analyse hyperbola, homogeneous case> = @begin
|
|
trivial = empty = false;
|
|
RT z_prime = d*w() - u()*u()*s() - v()*v()*r() + u()*v()*t();
|
|
o = (CGAL::Orientation)(CGAL_NTS sign (z_prime));
|
|
degenerate = (o == CGAL::ZERO);
|
|
|
|
@end
|
|
|
|
|
|
@macro <analyse hyperbola, Cartesian case> = @begin
|
|
trivial = empty = false;
|
|
FT z_prime = d*w() - u()*u()*s() - v()*v()*r() + u()*v()*t();
|
|
o = (CGAL::Orientation)(CGAL_NTS sign (z_prime));
|
|
degenerate = (o == CGAL::ZERO);
|
|
|
|
@end
|
|
|
|
|
|
@macro <analyse ellipse, homogeneous case> = @begin
|
|
trivial = false;
|
|
RT z_prime = d*w() - u()*u()*s() - v()*v()*r() + u()*v()*t();
|
|
if (CGAL_NTS is_positive (r())) {
|
|
empty = CGAL_NTS is_positive(CGAL_NTS sign (z_prime));
|
|
empty ? o = CGAL::POSITIVE : o = CGAL::NEGATIVE;
|
|
} else {
|
|
empty = CGAL_NTS is_negative(CGAL_NTS sign (z_prime));
|
|
empty ? o = CGAL::NEGATIVE : o = CGAL::POSITIVE;
|
|
}
|
|
degenerate = empty || CGAL_NTS is_zero (z_prime);
|
|
|
|
@end
|
|
|
|
@macro <analyse ellipse, Cartesian case> = @begin
|
|
trivial = false;
|
|
FT z_prime = d*w() - u()*u()*s() - v()*v()*r() + u()*v()*t();
|
|
if (CGAL_NTS is_positive (r())) {
|
|
empty = CGAL_NTS is_positive(CGAL_NTS sign (z_prime));
|
|
empty ? o = CGAL::POSITIVE : o = CGAL::NEGATIVE;
|
|
} else {
|
|
empty = CGAL_NTS is_negative(CGAL_NTS sign (z_prime));
|
|
empty ? o = CGAL::NEGATIVE : o = CGAL::POSITIVE;
|
|
}
|
|
degenerate = empty || CGAL_NTS is_zero (z_prime);
|
|
|
|
@end
|
|
|
|
In the parabola case, we proceed as follows, first observing that
|
|
$\det(M)=0$ implies $r,s\geq 0$ or $r,s\leq 0$.
|
|
Assume that $r\neq 0$. Then the conic equation (\ref{conic_def_hom}) can
|
|
be solved for $x$, obtaining
|
|
$$x = \frac{-ty - u \pm \sqrt{2(tu - 2rv)y +u^2 - 4rw}}{2r}.$$
|
|
|
|
The parabola is non-degenerate exactly if the factor $(tu - 2rv)$ is
|
|
nonzero, where we obtain a curved object. For $tu=2rv$, the parabola
|
|
is either empty (this happens for $u^2<4rw$ when the radicant becomes
|
|
negative), a single line (if $u^2=4rw$), or a pair of lines (for
|
|
$u^2>4rw$). Let us treat the degenerate case first.
|
|
|
|
In the empty case, the parabola has only one nonempty side, and the
|
|
orientation is determined by $w$. $w>0$ means positive orientation
|
|
(because then the point $(0,0)$ is on the positive side, which therefore
|
|
equals the convex side in this case), $w<0$ means negative
|
|
orientation. Because of $u^2<4rw$, the case $w=0$ cannot occur.
|
|
|
|
In case of $u^2 = 4rw$, the conic is given by
|
|
$$\r(p) = r(x + \frac{ty + u}{2r})^2,$$
|
|
and the orientation is positive if and only if $r>0$ (in which case
|
|
every point $p=(x,y)$ is on the positive side, equivalently on the convex
|
|
side).
|
|
|
|
For $u^2 > 4rw$, we get a pair of parallel lines, of zero orientation
|
|
(because both positive and negative sides are nonempty, while the
|
|
non-convex side is empty).
|
|
|
|
We can argue completely similar in the case $s\neq 0$. Here we get a
|
|
degeneracy exactly if $tv = 2su$, and the discriminant $v^2-4sw$ determines
|
|
whether the parabola is empty, equal to one line or to a pair of lines.
|
|
|
|
If $r=s=0$ (implying $t=0$), we have the trivial conic
|
|
if all other parameters are also zero. The trivial conic has
|
|
always positive orientation (because both positive and convex side
|
|
are empty). If $u=v=0$ but $w\neq 0$, we get the empty conic, where
|
|
the orientation is given by $w$. In any other case, we obtain a
|
|
single line, this time with zero orientation, because
|
|
it has nonempty positive and negative side but empty non-convex side.
|
|
|
|
Now consider the non-degenerate case. We claim that the orientation is
|
|
positive if and only if $r,s\leq 0$. To see this, note that in this case,
|
|
the parabola can be written in the form
|
|
$$\r(p) = -(\sqrt{-r} x - \sqrt{-s} y)^2 + ux + vy + w.$$
|
|
$\r(p)$ is a concave function which implies that if $\r(p_1),\r(p_2)>0$,
|
|
then also $\r(p)>0$, $p$ a convex combination of $p_1,p_2$. This means
|
|
that the positive side is a convex set, thus equal to the convex side.
|
|
|
|
@macro <analyse parabola, homogeneous case> = @begin
|
|
if (!CGAL_NTS is_zero (r())) {
|
|
trivial = false;
|
|
degenerate = (t()*u() == RT(2)*r()*v());
|
|
if (degenerate) {
|
|
CGAL::Sign discr = (CGAL::Sign)
|
|
CGAL_NTS sign(u()*u()-RT(4)*r()*w());
|
|
switch (discr) {
|
|
case CGAL::NEGATIVE:
|
|
empty = true;
|
|
o = (CGAL::Orientation)(CGAL_NTS sign (w()));
|
|
break;
|
|
case CGAL::ZERO:
|
|
empty = false;
|
|
o = (CGAL::Orientation)(CGAL_NTS sign (r()));
|
|
break;
|
|
case CGAL::POSITIVE:
|
|
empty = false;
|
|
o = CGAL::ZERO;
|
|
break;
|
|
}
|
|
} else {
|
|
empty = false;
|
|
o = (CGAL::Orientation)(-CGAL_NTS sign (r()));
|
|
}
|
|
} else if (!CGAL_NTS is_zero (s())) {
|
|
trivial = false;
|
|
degenerate = (t()*v() == RT(2)*s()*u());
|
|
if (degenerate) {
|
|
CGAL::Sign discr = (CGAL::Sign)
|
|
CGAL_NTS sign(v()*v()-RT(4)*s()*w());
|
|
switch (discr) {
|
|
case CGAL::NEGATIVE:
|
|
empty = true;
|
|
o = (CGAL::Orientation)(CGAL_NTS sign (w()));
|
|
break;
|
|
case CGAL::ZERO:
|
|
empty = false;
|
|
o = (CGAL::Orientation)(CGAL_NTS sign (s()));
|
|
break;
|
|
case CGAL::POSITIVE:
|
|
empty = false;
|
|
o = CGAL::ZERO;
|
|
break;
|
|
}
|
|
} else {
|
|
empty = false;
|
|
o = (CGAL::Orientation)(-CGAL_NTS sign (s()));
|
|
}
|
|
} else { // r=0, s=0
|
|
degenerate = true;
|
|
bool uv_zero = CGAL_NTS is_zero (u())
|
|
&& CGAL_NTS is_zero (v());
|
|
trivial = uv_zero && CGAL_NTS is_zero (w());
|
|
empty = uv_zero && !trivial;
|
|
if (empty)
|
|
o = (CGAL::Orientation)(CGAL_NTS sign (w()));
|
|
else if (trivial)
|
|
o = CGAL::POSITIVE;
|
|
else
|
|
o = CGAL::ZERO;
|
|
}
|
|
|
|
@end
|
|
|
|
@macro <analyse parabola, Cartesian case> = @begin
|
|
if (!CGAL_NTS is_zero (r())) {
|
|
trivial = false;
|
|
degenerate = (t()*u() == FT(2)*r()*v());
|
|
if (degenerate) {
|
|
CGAL::Sign discr = (CGAL::Sign)
|
|
CGAL_NTS sign(u()*u()-FT(4)*r()*w());
|
|
switch (discr) {
|
|
case CGAL::NEGATIVE:
|
|
empty = true;
|
|
o = (CGAL::Orientation)(CGAL_NTS sign (w()));
|
|
break;
|
|
case CGAL::ZERO:
|
|
empty = false;
|
|
o = (CGAL::Orientation)(CGAL_NTS sign (r()));
|
|
break;
|
|
case CGAL::POSITIVE:
|
|
empty = false;
|
|
o = CGAL::ZERO;
|
|
break;
|
|
}
|
|
} else {
|
|
empty = false;
|
|
o = (CGAL::Orientation)(-CGAL_NTS sign (r()));
|
|
}
|
|
} else if (!CGAL_NTS is_zero (s())) {
|
|
trivial = false;
|
|
degenerate = (t()*v() == FT(2)*s()*u());
|
|
if (degenerate) {
|
|
CGAL::Sign discr = (CGAL::Sign)
|
|
CGAL_NTS sign(v()*v()-FT(4)*s()*w());
|
|
switch (discr) {
|
|
case CGAL::NEGATIVE:
|
|
empty = true;
|
|
o = (CGAL::Orientation)(CGAL_NTS sign (w()));
|
|
break;
|
|
case CGAL::ZERO:
|
|
empty = false;
|
|
o = (CGAL::Orientation)(CGAL_NTS sign (s()));
|
|
break;
|
|
case CGAL::POSITIVE:
|
|
empty = false;
|
|
o = CGAL::ZERO;
|
|
break;
|
|
}
|
|
} else {
|
|
empty = false;
|
|
o = (CGAL::Orientation)(-CGAL_NTS sign (s()));
|
|
}
|
|
} else { // r=0, s=0
|
|
degenerate = true;
|
|
bool uv_zero = CGAL_NTS is_zero (u())
|
|
&& CGAL_NTS is_zero (v());
|
|
trivial = uv_zero && CGAL_NTS is_zero (w());
|
|
empty = uv_zero && !trivial;
|
|
if (empty)
|
|
o = (CGAL::Orientation)(CGAL_NTS sign (w()));
|
|
else if (trivial)
|
|
o = CGAL::POSITIVE;
|
|
else
|
|
o = CGAL::ZERO;
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{Conic evaluation}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
For $\C_{\r}$ given by $R=(r,s,t,u,v,w)$ and a point $p=(x,y,h)$, the
|
|
homogeneous evaluation returns the value
|
|
$\r(p) = rx^2+sy^2+txy+uxh+vyh+wh^2$.
|
|
|
|
@macro <ConicHPA2 protected member functions> += @begin
|
|
RT evaluate (const PT& p) const
|
|
{
|
|
RT x, y, h;
|
|
dao.get (p, x, y, h);
|
|
return r()*x*x + s()*y*y + t()*x*y + u()*x*h + v()*y*h + w()*h*h;
|
|
}
|
|
|
|
@end
|
|
|
|
The Cartesian version is obtained for $h=1$, i.e. it computes the value
|
|
$\r(p) = rx^2+sy^2+txy+ux+vy+w$.
|
|
|
|
@macro <ConicCPA2 protected member functions> += @begin
|
|
FT evaluate (const PT& p) const
|
|
{
|
|
FT x, y;
|
|
dao.get (p, x, y);
|
|
return r()*x*x + s()*y*y + t()*x*y + u()*x + v()*y + w();
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsection{High-level member functions}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{Construction}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
The construction from the representation proceeds by first setting
|
|
the vector components and then analysing the conic to obtain the derived
|
|
data.
|
|
|
|
@macro <ConicHPA2 public member functions> += @begin
|
|
ConicHPA2 ( const DA& da = DA()) : dao( da) { }
|
|
|
|
ConicHPA2 (RT r, RT s, RT t, RT u, RT v, RT w, const DA& da = DA())
|
|
: dao( da), _r(r), _s(s), _t(t), _u(u), _v(v), _w(w)
|
|
{
|
|
analyse();
|
|
}
|
|
|
|
@end
|
|
|
|
@macro <ConicCPA2 public member functions> += @begin
|
|
ConicCPA2 ( const DA& da = DA()) : dao( da) { }
|
|
|
|
ConicCPA2 (FT r, FT s, FT t, FT u, FT v, FT w, const DA& da = DA())
|
|
: dao( da), _r(r), _s(s), _t(t), _u(u), _v(v), _w(w)
|
|
{
|
|
analyse();
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{Data Accessor}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
@macro <ConicHPA2 public member functions> += @begin
|
|
const DA& da() const
|
|
{
|
|
return dao;
|
|
}
|
|
|
|
@end
|
|
|
|
@macro <ConicCPA2 public member functions> += @begin
|
|
const DA& da() const
|
|
{
|
|
return dao;
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{General access}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
The coordinate access is straightforward.
|
|
|
|
@macro <ConicHPA2 public member functions> += @begin
|
|
RT r() const { return _r;}
|
|
RT s() const { return _s;}
|
|
RT t() const { return _t;}
|
|
RT u() const { return _u;}
|
|
RT v() const { return _v;}
|
|
RT w() const { return _w;}
|
|
|
|
@end
|
|
|
|
|
|
@macro <ConicCPA2 public member functions> += @begin
|
|
FT r() const { return _r;}
|
|
FT s() const { return _s;}
|
|
FT t() const { return _t;}
|
|
FT u() const { return _u;}
|
|
FT v() const { return _v;}
|
|
FT w() const { return _w;}
|
|
|
|
@end
|
|
|
|
To obtain the center, recall from subsection \ref{symmetry_sec} that it
|
|
is given by the point
|
|
\[
|
|
c = -M^{-1}
|
|
\left(\begin{array}{c} u \\ v \end{array}\right)
|
|
= -\frac{1}{\det(M)}\left(\begin{array}{cc} 2s & -t \\ -t & 2r
|
|
\end{array}\right) \left(\begin{array}{c} u \\ v \end{array}\right)
|
|
= -\frac{1}{\det(M)}\left(\begin{array}{cc} 2s\cdot u -t\cdot v \\
|
|
2r\cdot v - t\cdot u\end{array}\right).
|
|
\]
|
|
|
|
In the homogeneous representation, the value $-\det(M)$ serves as the
|
|
$h$-component of the center, in the Cartesian representation, we divide
|
|
by it.
|
|
|
|
@macro <ConicHPA2 public member functions> += @begin
|
|
PT center () const
|
|
{
|
|
CGAL_optimisation_precondition (type != PARABOLA);
|
|
PT p;
|
|
RT two = RT(2);
|
|
dao.set( p, two*s()*u() - t()*v(), two*r()*v() - t()*u(), -det());
|
|
return p;
|
|
}
|
|
|
|
@end
|
|
|
|
@macro <ConicCPA2 public member functions> += @begin
|
|
PT center () const
|
|
{
|
|
CGAL_optimisation_precondition (type != PARABOLA);
|
|
PT p;
|
|
FT two = FT(2);
|
|
FT div = -det();
|
|
dao.set( p, (two*s()*u() - t()*v()) / div,
|
|
(two*r()*v() - t()*u()) / div);
|
|
return p;
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{Type Related Access}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
Because the conic stores its type and degeneracy information, this is
|
|
straightforward.
|
|
|
|
@macro <ConicHPA2 public member functions> += @begin
|
|
Conic_type conic_type () const
|
|
{
|
|
return type;
|
|
}
|
|
|
|
bool is_hyperbola () const
|
|
{
|
|
return (type == HYPERBOLA);
|
|
}
|
|
|
|
bool is_parabola () const
|
|
{
|
|
return (type == PARABOLA);
|
|
}
|
|
|
|
bool is_ellipse () const
|
|
{
|
|
return (type == ELLIPSE);
|
|
}
|
|
|
|
bool is_empty () const
|
|
{
|
|
return empty;
|
|
}
|
|
|
|
bool is_trivial () const
|
|
{
|
|
return trivial;
|
|
}
|
|
|
|
bool is_degenerate () const
|
|
{
|
|
return degenerate;
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@macro <ConicCPA2 public member functions> += @begin
|
|
Conic_type conic_type () const
|
|
{
|
|
return type;
|
|
}
|
|
|
|
bool is_hyperbola () const
|
|
{
|
|
return (type == HYPERBOLA);
|
|
}
|
|
|
|
bool is_parabola () const
|
|
{
|
|
return (type == PARABOLA);
|
|
}
|
|
|
|
bool is_ellipse () const
|
|
{
|
|
return (type == ELLIPSE);
|
|
}
|
|
|
|
bool is_empty () const
|
|
{
|
|
return empty;
|
|
}
|
|
|
|
bool is_trivial () const
|
|
{
|
|
return trivial;
|
|
}
|
|
|
|
bool is_degenerate () const
|
|
{
|
|
return degenerate;
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{Orientation Related Access}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
@macro <ConicHPA2 public member functions> += @begin
|
|
CGAL::Orientation orientation () const
|
|
{
|
|
return o;
|
|
}
|
|
|
|
@end
|
|
|
|
@macro <ConicCPA2 public member functions> += @begin
|
|
CGAL::Orientation orientation () const
|
|
{
|
|
return o;
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
The orientation queries just evaluate the conic at the given point,
|
|
using the private method @prg{evaluate}. Recall that $p$ is in the
|
|
positive resp. negative side iff $\r(p)>0$ resp. $\r(p)<0$.
|
|
|
|
@macro <ConicHPA2 public member functions> += @begin
|
|
CGAL::Oriented_side oriented_side (const PT& p) const
|
|
{
|
|
return (CGAL::Oriented_side)(CGAL_NTS sign (evaluate (p)));
|
|
}
|
|
|
|
bool has_on_positive_side (const PT& p) const
|
|
{
|
|
return (CGAL_NTS is_positive (evaluate(p)));
|
|
}
|
|
|
|
bool has_on_negative_side (const PT& p) const
|
|
{
|
|
return (CGAL_NTS is_negative (evaluate(p)));
|
|
}
|
|
|
|
bool has_on_boundary (const PT& p) const
|
|
{
|
|
return (CGAL_NTS is_zero (evaluate(p)));
|
|
}
|
|
|
|
bool has_on (const PT& p) const
|
|
{
|
|
return (CGAL_NTS is_zero (evaluate(p)));
|
|
}
|
|
|
|
@end
|
|
|
|
@macro <ConicCPA2 public member functions> += @begin
|
|
CGAL::Oriented_side oriented_side (const PT& p) const
|
|
{
|
|
return (CGAL::Oriented_side)(CGAL_NTS sign (evaluate (p)));
|
|
}
|
|
|
|
bool has_on_positive_side (const PT& p) const
|
|
{
|
|
return (CGAL_NTS is_positive (evaluate(p)));
|
|
}
|
|
|
|
bool has_on_negative_side (const PT& p) const
|
|
{
|
|
return (CGAL_NTS is_negative (evaluate(p)));
|
|
}
|
|
|
|
bool has_on_boundary (const PT& p) const
|
|
{
|
|
return (CGAL_NTS is_zero (evaluate(p)));
|
|
}
|
|
|
|
bool has_on (const PT& p) const
|
|
{
|
|
return (CGAL_NTS is_zero (evaluate(p)));
|
|
}
|
|
|
|
|
|
@end
|
|
|
|
Then we have the convex side queries. Under nonzero orientation,
|
|
the side is determined by a conic evaluation. If the orientation is
|
|
zero, we know that the non-convex side is empty, see subsection
|
|
\ref{orientation_sec}.
|
|
|
|
@macro <ConicHPA2 public member functions> += @begin
|
|
Convex_side convex_side (const PT& p) const
|
|
{
|
|
switch (o) {
|
|
case CGAL::POSITIVE:
|
|
return (Convex_side)( CGAL_NTS sign (evaluate (p)));
|
|
case CGAL::NEGATIVE:
|
|
return (Convex_side)(-CGAL_NTS sign (evaluate (p)));
|
|
case CGAL::ZERO:
|
|
return (Convex_side)( CGAL_NTS sign (CGAL_NTS abs (evaluate(p))));
|
|
}
|
|
// keeps g++ happy
|
|
return( Convex_side( 0));
|
|
}
|
|
|
|
bool has_on_convex_side (const PT& p) const
|
|
{
|
|
return (convex_side (p) == ON_CONVEX_SIDE);
|
|
}
|
|
|
|
bool has_on_nonconvex_side (const PT& p) const
|
|
{
|
|
return (convex_side (p) == ON_NONCONVEX_SIDE);
|
|
}
|
|
|
|
@end
|
|
|
|
@macro <ConicCPA2 public member functions> += @begin
|
|
Convex_side convex_side (const PT& p) const
|
|
{
|
|
switch (o) {
|
|
case CGAL::POSITIVE:
|
|
return (Convex_side)( CGAL_NTS sign (evaluate (p)));
|
|
case CGAL::NEGATIVE:
|
|
return (Convex_side)(-CGAL_NTS sign (evaluate (p)));
|
|
case CGAL::ZERO:
|
|
return (Convex_side)( CGAL_NTS sign (CGAL_NTS abs (evaluate(p))));
|
|
}
|
|
// keeps g++ happy
|
|
return( Convex_side( 0));
|
|
}
|
|
|
|
bool has_on_convex_side (const PT& p) const
|
|
{
|
|
return (convex_side (p) == ON_CONVEX_SIDE);
|
|
}
|
|
|
|
bool has_on_nonconvex_side (const PT& p) const
|
|
{
|
|
return (convex_side (p) == ON_NONCONVEX_SIDE);
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsection{Comparison methods}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
We provide tests for equality and inequality of two conics.
|
|
|
|
@macro <ConicHPA2 public member functions> += @begin
|
|
bool operator == ( const ConicHPA2<_PT,_DA>& c) const
|
|
{
|
|
// find coefficient != 0
|
|
RT factor1;
|
|
if ( ! CGAL_NTS is_zero( r())) factor1 = r(); else
|
|
if ( ! CGAL_NTS is_zero( s())) factor1 = s(); else
|
|
if ( ! CGAL_NTS is_zero( t())) factor1 = t(); else
|
|
if ( ! CGAL_NTS is_zero( u())) factor1 = u(); else
|
|
if ( ! CGAL_NTS is_zero( v())) factor1 = v(); else
|
|
if ( ! CGAL_NTS is_zero( w())) factor1 = w(); else
|
|
CGAL_optimisation_assertion_msg( false, "all coefficients zero");
|
|
|
|
// find coefficient != 0
|
|
RT factor2;
|
|
if ( ! CGAL_NTS is_zero( c.r())) factor2 = c.r(); else
|
|
if ( ! CGAL_NTS is_zero( c.s())) factor2 = c.s(); else
|
|
if ( ! CGAL_NTS is_zero( c.t())) factor2 = c.t(); else
|
|
if ( ! CGAL_NTS is_zero( c.u())) factor2 = c.u(); else
|
|
if ( ! CGAL_NTS is_zero( c.v())) factor2 = c.v(); else
|
|
if ( ! CGAL_NTS is_zero( c.w())) factor2 = c.w(); else
|
|
CGAL_optimisation_assertion_msg( false, "all coefficients zero");
|
|
|
|
return( ( r()*factor2 == c.r()*factor1)
|
|
&& ( s()*factor2 == c.s()*factor1)
|
|
&& ( t()*factor2 == c.t()*factor1)
|
|
&& ( u()*factor2 == c.u()*factor1)
|
|
&& ( v()*factor2 == c.v()*factor1)
|
|
&& ( w()*factor2 == c.w()*factor1));
|
|
}
|
|
|
|
@end
|
|
|
|
@macro <ConicCPA2 public member functions> += @begin
|
|
bool operator == ( const ConicCPA2<_PT,_DA>& c) const
|
|
{
|
|
// find coefficient != 0
|
|
FT factor1;
|
|
if ( ! CGAL_NTS is_zero( r())) factor1 = r(); else
|
|
if ( ! CGAL_NTS is_zero( s())) factor1 = s(); else
|
|
if ( ! CGAL_NTS is_zero( t())) factor1 = t(); else
|
|
if ( ! CGAL_NTS is_zero( u())) factor1 = u(); else
|
|
if ( ! CGAL_NTS is_zero( v())) factor1 = v(); else
|
|
if ( ! CGAL_NTS is_zero( w())) factor1 = w(); else
|
|
CGAL_optimisation_assertion_msg( false, "all coefficients zero");
|
|
|
|
// find coefficient != 0
|
|
FT factor2;
|
|
if ( ! CGAL_NTS is_zero( c.r())) factor2 = c.r(); else
|
|
if ( ! CGAL_NTS is_zero( c.s())) factor2 = c.s(); else
|
|
if ( ! CGAL_NTS is_zero( c.t())) factor2 = c.t(); else
|
|
if ( ! CGAL_NTS is_zero( c.u())) factor2 = c.u(); else
|
|
if ( ! CGAL_NTS is_zero( c.v())) factor2 = c.v(); else
|
|
if ( ! CGAL_NTS is_zero( c.w())) factor2 = c.w(); else
|
|
CGAL_optimisation_assertion_msg( false, "all coefficients zero");
|
|
|
|
return( ( r()*factor2 == c.r()*factor1)
|
|
&& ( s()*factor2 == c.s()*factor1)
|
|
&& ( t()*factor2 == c.t()*factor1)
|
|
&& ( u()*factor2 == c.u()*factor1)
|
|
&& ( v()*factor2 == c.v()*factor1)
|
|
&& ( w()*factor2 == c.w()*factor1));
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{Private Methods}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
A main difference between the public and the private set methods
|
|
is that the private ones do not analyse the conic. If a conic
|
|
is constructed in a sequence of calls to set functions, it is more
|
|
efficient to do an analysis only once for the final result, rather than
|
|
analysing any intermediate result. Therefore, the private set functions
|
|
also do not allow to specify an orientation, because in order to orient the
|
|
conic, an analysis would be necessary. Under this scheme, caution is in place,
|
|
of course: a conic constructed from a private set method is not fully
|
|
initialized, and calls to type and orientation related access functions
|
|
return undefined results. @prg{friend} classes need to care for this,
|
|
whenever they call a private set method.
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
\paragraph{Linear combination of conics} ~
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
@macro<ConicHPA2 private member functions> += @begin
|
|
void
|
|
set_linear_combination (const RT& a1, const ConicHPA2<PT,DA>& c1,
|
|
const RT& a2, const ConicHPA2<PT,DA>& c2)
|
|
{
|
|
_r = a1 * c1.r() + a2 * c2.r();
|
|
_s = a1 * c1.s() + a2 * c2.s();
|
|
_t = a1 * c1.t() + a2 * c2.t();
|
|
_u = a1 * c1.u() + a2 * c2.u();
|
|
_v = a1 * c1.v() + a2 * c2.v();
|
|
_w = a1 * c1.w() + a2 * c2.w();
|
|
}
|
|
|
|
@end
|
|
|
|
@macro<ConicCPA2 private member functions> += @begin
|
|
void
|
|
set_linear_combination (const FT& a1, const ConicCPA2<PT,DA>& c1,
|
|
const FT& a2, const ConicCPA2<PT,DA>& c2)
|
|
{
|
|
_r = a1 * c1.r() + a2 * c2.r();
|
|
_s = a1 * c1.s() + a2 * c2.s();
|
|
_t = a1 * c1.t() + a2 * c2.t();
|
|
_u = a1 * c1.u() + a2 * c2.u();
|
|
_v = a1 * c1.v() + a2 * c2.v();
|
|
_w = a1 * c1.w() + a2 * c2.w();
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
\paragraph{Two pairs of lines through four points} ~
|
|
@!----------------------------------------------------------------------------
|
|
|
|
Here is the method to get two line-pairs from four given points. It just
|
|
brings the points into (counter)clockwise order and then calls the
|
|
@prg{set_linepair} method for their two conic arguments, supplying the
|
|
points in the right order. The reordering needs access to the
|
|
orientations of certain point triples. For points $p_i=(x_i,y_i,h_i)$,
|
|
$i=1\ldots3$, this orientation is given by the sign of the determinant
|
|
$$\det \left(
|
|
\begin{array}{ccc}
|
|
x_1 & x_2 & x_3 \\
|
|
y_1 & y_2 & y_3 \\
|
|
h_1 & h_2 & h_3
|
|
\end{array}\right),$$
|
|
see Lemma \ref{det_lemma} below.
|
|
The following macros define this determinant (the Cartesian version is
|
|
obtained by setting $h_i=1,i=1\ldots 3$).
|
|
|
|
@macro <h_orientation>(3) many = @begin
|
|
(CGAL::Orientation)(CGAL_NTS sign
|
|
(-h@1*x@3*y@2+h@3*x@1*y@2
|
|
+h@1*x@2*y@3-h@2*x@1*y@3
|
|
+h@2*x@3*y@1-h@3*x@2*y@1))
|
|
@end
|
|
|
|
@macro <c_orientation>(3) many = @begin
|
|
(CGAL::Orientation)(CGAL_NTS sign
|
|
(-x@3*y@2+x@1*y@2
|
|
+x@2*y@3-x@1*y@3
|
|
+x@3*y@1-x@2*y@1))
|
|
@end
|
|
|
|
@macro<ConicHPA2 private member functions> += @begin
|
|
static void set_two_linepairs (const PT& p1,
|
|
const PT& p2,
|
|
const PT& p3,
|
|
const PT& p4,
|
|
ConicHPA2<PT,DA>& pair1,
|
|
ConicHPA2<PT,DA>& pair2)
|
|
{
|
|
RT x1, y1, h1, x2, y2, h2, x3, y3, h3, x4, y4, h4;
|
|
const DA& da = pair1.da();
|
|
da.get (p1, x1, y1, h1);
|
|
da.get (p2, x2, y2, h2);
|
|
da.get (p3, x3, y3, h3);
|
|
da.get (p4, x4, y4, h4);
|
|
|
|
CGAL::Orientation side1_24 = @<h_orientation>("2", "4", "1"),
|
|
side3_24 = @<h_orientation>("2", "4", "3");
|
|
if (side1_24 != side3_24) {
|
|
// (counter)clockwise order
|
|
pair1.set_linepair (p1, p2, p3, p4);
|
|
pair2.set_linepair (p2, p3, p4, p1);
|
|
} else {
|
|
CGAL::Orientation side1_32 = @<h_orientation>("3", "2", "1");
|
|
if (side1_32 != side3_24) {
|
|
// p1, p2 need to be swapped
|
|
pair1.set_linepair (p2, p1, p3, p4);
|
|
pair2.set_linepair (p1, p3, p4, p2);
|
|
} else {
|
|
// p2, p3 need to be swapped
|
|
pair1.set_linepair (p1, p3, p2, p4);
|
|
pair2.set_linepair (p3, p2, p4, p1);
|
|
}
|
|
}
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@macro<ConicCPA2 private member functions> += @begin
|
|
static void set_two_linepairs (const PT& p1,
|
|
const PT& p2,
|
|
const PT& p3,
|
|
const PT& p4,
|
|
ConicCPA2<PT,DA>& pair1,
|
|
ConicCPA2<PT,DA>& pair2)
|
|
{
|
|
FT x1, y1, x2, y2, x3, y3, x4, y4;
|
|
const DA& da = pair1.da();
|
|
da.get (p1, x1, y1);
|
|
da.get (p2, x2, y2);
|
|
da.get (p3, x3, y3);
|
|
da.get (p4, x4, y4);
|
|
|
|
CGAL::Orientation side1_24 = @<c_orientation>("2", "4", "1"),
|
|
side3_24 = @<c_orientation>("2", "4", "3");
|
|
if (side1_24 != side3_24) {
|
|
// (counter)clockwise order
|
|
pair1.set_linepair (p1, p2, p3, p4);
|
|
pair2.set_linepair (p2, p3, p4, p1);
|
|
} else {
|
|
CGAL::Orientation side1_32 = @<c_orientation>("3", "2", "1");
|
|
if (side1_32 != side3_24) {
|
|
// p1, p2 need to be swapped
|
|
pair1.set_linepair (p2, p1, p3, p4);
|
|
pair2.set_linepair (p1, p3, p4, p2);
|
|
} else {
|
|
// p2, p3 need to be swapped
|
|
pair1.set_linepair (p1, p3, p2, p4);
|
|
pair2.set_linepair (p3, p2, p4, p1);
|
|
}
|
|
}
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
\paragraph{Some ellipse from two pairs of lines} ~
|
|
@!----------------------------------------------------------------------------
|
|
|
|
Assuming that we have constructed two line-pairs $\C_{\r_1}, \C_{\r_2}$
|
|
using the method @prg{set_two_linepairs} with points $p_1,p_2,p_3,p_4$
|
|
in convex position, an ellipse through the points can be obtained as a
|
|
linear combination $\C_{\r}$, $\r = \lambda \r_1 + \mu \r_2$, where
|
|
\begin{eqnarray*}
|
|
\lambda &=& \det({\r_2}) - 2(r_1s_2+r_2s_1) + t_1t_2, \\
|
|
\mu &=& \det({\r_1}) - 2(r_1s_2+r_2s_1) + t_1t_2,
|
|
\end{eqnarray*}
|
|
$r_i,\ldots,w_i$ the components of $\r_i,i=1\ldots 2$.
|
|
|
|
@macro<ConicHPA2 private member functions> += @begin
|
|
void set_ellipse (const ConicHPA2<PT,DA>& pair1,
|
|
const ConicHPA2<PT,DA>& pair2)
|
|
{
|
|
RT b = RT(2) * (pair1.r() * pair2.s() + pair1.s() * pair2.r()) -
|
|
pair1.t() * pair2.t();
|
|
set_linear_combination (pair2.det()-b, pair1,
|
|
pair1.det()-b, pair2);
|
|
}
|
|
|
|
@end
|
|
|
|
@macro<ConicCPA2 private member functions> += @begin
|
|
void set_ellipse (const ConicCPA2<PT,DA>& pair1,
|
|
const ConicCPA2<PT,DA>& pair2)
|
|
{
|
|
FT b = FT(2) * (pair1.r() * pair2.s() + pair1.s() * pair2.r()) -
|
|
pair1.t() * pair2.t();
|
|
set_linear_combination (pair2.det()-b, pair1,
|
|
pair1.det()-b, pair2);
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
\paragraph{Conic from two conics and a point} ~
|
|
@!----------------------------------------------------------------------------
|
|
|
|
If $\C_{\r_1}, \C_{\r_2}$ are two conics, $p$ some point, it is easy to see
|
|
that the conic $\C_{\r}$ with $$\r = \r_2(p) \r_1 - \r_1(p) \r_2$$ is
|
|
a conic containing the set $(\C_{\r_1} \cap \C_{\r_2}) \cup \{p\}.$ Exactly
|
|
this conic is constructed here.
|
|
|
|
@macro<ConicHPA2 private member functions> += @begin
|
|
void set (const ConicHPA2<PT,DA>& c1,
|
|
const ConicHPA2<PT,DA>& c2,
|
|
const PT& p)
|
|
{
|
|
set_linear_combination (c2.evaluate(p), c1, -c1.evaluate(p), c2);
|
|
}
|
|
|
|
@end
|
|
|
|
@macro<ConicCPA2 private member functions> += @begin
|
|
void set (const ConicCPA2<PT,DA>& c1,
|
|
const ConicCPA2<PT,DA>& c2,
|
|
const PT& p)
|
|
{
|
|
set_linear_combination (c2.evaluate(p), c1, -c1.evaluate(p), c2);
|
|
}
|
|
|
|
@end
|
|
|
|
@!----------------------------------------------------------------------------
|
|
\paragraph{Volume derivative of an ellipse} ~
|
|
@!----------------------------------------------------------------------------
|
|
|
|
Let $r(\tau),s(\tau), t(\tau), u(\tau), v(\tau), w(\tau)$ denote the
|
|
parameters of $\E(\tau)$. Omitting the parameter $\tau$ for the sake of
|
|
readability, we have
|
|
\begin{eqnarray*}
|
|
r &=& r_0 + \tau \d r, \\
|
|
s &=& s_0 + \tau \d s, \\
|
|
t &=& t_0 + \tau \d t, \\
|
|
u &=& u_0 + \tau \d u, \\
|
|
v &=& v_0 + \tau \d v, \\
|
|
w &=& w_0 + \tau \d w,
|
|
\end{eqnarray*}
|
|
$r_0,\ldots,w_0$ the representation of $\E$, $\d r,\ldots, \d w$ the
|
|
given values $\d\r$.
|
|
|
|
Recall that $$\Vol(\E(\tau)) = \pi / \sqrt{\det(M / (2w - c^TMc))}.$$ This
|
|
implies
|
|
\[
|
|
\sgn\left( \frac{\d}{\d \tau} \left. \Vol(\E(\tau))\right|_{\tau=0}\right) =
|
|
-\sgn \left(\frac{\d}{\d \tau} \left. \det(M / (2w - c^TMc))
|
|
\right|_{\tau=0}\right).
|
|
\]
|
|
We have $\det(M / (2w - c^TMc)) = d / z^2$,
|
|
where
|
|
\begin{eqnarray*}
|
|
d &=& \det(M) = 4rs -t^2, \\
|
|
z &=& 2w - c^TMc = 2 \left(w - \frac{1}{d} (u^2 s - uvt + v^2r)\right),
|
|
\end{eqnarray*}
|
|
see also (\ref{z}).
|
|
Hence
|
|
$$\frac{d}{z^2} = \frac{d^3}{4q^2}, \quad q = dw - u^2 s + uvt - v^2r.$$
|
|
|
|
It follows that
|
|
\[
|
|
\sgn\left(\frac{\d}{\d \tau} \Vol(\E(\tau))\right)
|
|
= -\sgn\left(\frac{d^2(3d'q-2dq')}{4q^3}\right)
|
|
= -\sgn \left( 3d'q-2dq'\right) \sgn (q).
|
|
\]
|
|
|
|
Consider the term $3d'q-2dq'=0$. We have
|
|
|
|
\begin{eqnarray*}
|
|
d &=& a_2\tau^2 + a_1\tau + a_0, \\
|
|
q &=& b_3\tau^3 + b_2\tau^2 + b_1\tau + b,
|
|
\end{eqnarray*}
|
|
where
|
|
\begin{eqnarray*}
|
|
a_2 &=& 4\d r\d s - \d t^2, \\
|
|
a_1 &=& 4r_0\d s + 4s_0\d r - 2t_0\d t, \\
|
|
a_0 &=& 4r_0s_0-t_0^2, \\
|
|
b_3 &=& (4\d r\d s - \d t^2)\d w - \d u^2 \d s - \d v^2 \d r+\d t \d u \d v, \\
|
|
b_2 &=& (4r_0\d s+4\d rs_0-2t_0\d t)\d w+(4\d r\d s-\d t^2)w_0-2u_0\d u\d s \\
|
|
& & -\d u^2s_0-2 v_0\d v\d r-\d v^2r_0+(u_0\d v+\d uv_0)\d t+\d u\d vt_0, \\
|
|
b_1 &=& (4r_0s_0-t_0^2)\d w+(4r_0\d s+4\d rs_0-2t_0\d t)w_0-u_0^2\d s \\
|
|
& & -2u_0\d u s_0-v_0^2\d r-2v_0\d vr_0+u_0v_0\d t+(u_0\d v+\d uv_0)t_0, \\
|
|
b_0 &=& (4r_0s_0-t_0^2)w_0-u_0^2s_0-v_0^2r_0+u_0v_0t_0
|
|
\end {eqnarray*}
|
|
|
|
Furthermore,
|
|
$$3d'q-2dq' = c_3\tau^3 + c_2\tau^2 + c_1\tau + c_0,$$
|
|
with
|
|
\begin{eqnarray*}
|
|
c_3 &=& -3a_1b_3 + 2a_2b_2, \\
|
|
c_2 &=& -6a_0b_3 - a_1b_2 + 4a_2b_1, \\
|
|
c_1 &=& -4a_0b_2 + a_1b_1 + 6a_2b_0, \\
|
|
c_0 &=& -2a_0b_1 + 3a_1b_0.
|
|
\end{eqnarray*}
|
|
|
|
The desired sign of the volume derivative is obtained by
|
|
evaluating the expression $$-\sgn(3d'q-2dq')\sgn(q)$$ at $\tau=0$,
|
|
where we get $3d'q-2dq' = c_0.$
|
|
Moreover, since $q = \det(M)(2w-c^TMc)/2$ with $\det(M)>0$, the sign of
|
|
$q$ is positive if and only if $2w-c^TMc$ is positive, equivalently
|
|
if the ellipse $\E(0)$ is in positive orientation (see also the description
|
|
of the method @prg{analyse}). This means, in case of positive orientation,
|
|
the sign of $-\sgn(3d'q-2dq')\sgn(q)$ equals the sign of $-c_0$, otherwise,
|
|
we return the sign of $c_0$. To compute $c_0$, we only need the values
|
|
$a_1,a_0,b_1,b_0$ from above.
|
|
|
|
@macro<ConicHPA2 private member functions> += @begin
|
|
CGAL::Sign vol_derivative (RT dr, RT ds, RT dt,
|
|
RT du, RT dv, RT dw) const
|
|
{
|
|
RT a1 = RT(4)*r()*ds+RT(4)*dr*s()-RT(2)*t()*dt,
|
|
a0 = RT(4)*r()*s()-t()*t(),
|
|
b1 = (RT(4)*r()*s()-t()*t())*dw+(RT(4)*r()*ds+RT(4)*dr*s()-
|
|
RT(2)*t()*dt)*w()-u()*u()*ds -
|
|
RT(2)*u()*du*s()-v()*v()*dr-RT(2)*v()*dv*r()+u()*v()*dt+
|
|
(u()*dv+du*v())*t(),
|
|
b0 = (RT(4)*r()*s()-t()*t())*w()
|
|
-u()*u()*s()-v()*v()*r()+u()*v()*t(),
|
|
c0 = -RT(2)*a0*b1 + RT(3)*a1*b0;
|
|
|
|
return CGAL::Sign (-CGAL_NTS sign (c0)*o);
|
|
}
|
|
|
|
@end
|
|
|
|
@macro<ConicCPA2 private member functions> += @begin
|
|
CGAL::Sign vol_derivative (FT dr, FT ds, FT dt,
|
|
FT du, FT dv, FT dw) const
|
|
{
|
|
FT a1 = FT(4)*r()*ds+FT(4)*dr*s()-FT(2)*t()*dt,
|
|
a0 = FT(4)*r()*s()-t()*t(),
|
|
b1 = (FT(4)*r()*s()-t()*t())*dw+(FT(4)*r()*ds+FT(4)*dr*s()-
|
|
FT(2)*t()*dt)*w()-u()*u()*ds -
|
|
FT(2)*u()*du*s()-v()*v()*dr-FT(2)*v()*dv*r()+u()*v()*dt+
|
|
(u()*dv+du*v())*t(),
|
|
b0 = (FT(4)*r()*s()-t()*t())*w()
|
|
-u()*u()*s()-v()*v()*r()+u()*v()*t(),
|
|
c0 = -FT(2)*a0*b1 + FT(3)*a1*b0;
|
|
|
|
return CGAL::Sign (-CGAL_NTS sign (c0)*o);
|
|
}
|
|
|
|
@end
|
|
|
|
To find the value $\tau^*$ such that $E(\tau^*)$ is the ellipse of smallest
|
|
volume, we apply the Cardano formula to find the roots of the polynomial
|
|
$p(\tau)=c_3\tau^3 + c_2\tau^2 + c_1\tau + c_0 = 0$. This is done
|
|
by the function @prg{solve_cubic} which returns all (at most three)
|
|
real roots. We then select the one which leads to largest (positive)
|
|
volume.
|
|
|
|
Here is an outline of the Cardano formula, for the polynomial
|
|
$p(\tau)$, assuming that $c_3\neq 0$.
|
|
\begin{enumerate}
|
|
\item Divide by $c_3$ to normalize the equation, leading to
|
|
$$p(\tau) = \tau^3 + \gamma_2 \tau^2 + \gamma_1 \tau + \gamma_0,\quad
|
|
\gamma_i := c_i/c_3, ~i=0,\ldots,2.$$
|
|
\item Eliminate the quadratic term by substituting $\tau := x - \gamma_2/3$.
|
|
This leads to
|
|
$$p(x) = x^3 + a x + b, \quad a = \gamma_1 - \frac{\gamma_2^2}{3},~
|
|
b = \frac{2}{27}\gamma_2^3 - \frac{1}{3}\gamma_1\gamma_2 + \gamma_0.$$
|
|
If $a=0$, $p$ has only one real root, namely $x_1 = \sqrt[3]{-b}$, so
|
|
let's assume $a\neq 0$.
|
|
\item Define $$D := (a/3)^3 + (b/2)^2$$ and let $u$ be any number such
|
|
that $$u^3 = -b/2 + \sqrt{D}.$$
|
|
Note that $u^3$ is a solution of the quadratic equation
|
|
$$x^2 + bx - \left(\frac{a}{3}\right)^3 = 0.$$
|
|
This means, if $a\neq 0$, then also $u\neq 0$.
|
|
If the discriminant $D$ is negative, $u$ is a complex number,
|
|
otherwise it must be chosen as the real number
|
|
$$u := \sqrt[3]{-b/2 + \sqrt{D}}.$$
|
|
\item Let $u$ be of the form $u = u_R - u_I \I$ (we possibly have $u_I=0$).
|
|
$p$ has always a real root, given by
|
|
$$x_1 = u_R \left(1 - \frac{a}{3\|u\|^2}\right).$$
|
|
\item If $D > 0$, the two other roots are complex. If
|
|
$D \leq 0$, $p$ has two more real roots,
|
|
given by
|
|
\begin{eqnarray*}
|
|
x_2 & = & -\frac{1}{2}\left(1 - \frac{a}{3\|u\|^2}\right)(u_R-u_I\sqrt{3}),\\
|
|
x_3 & = & -\frac{1}{2}\left(1 - \frac{a}{3\|u\|^2}\right)(u_R+u_I\sqrt{3}).
|
|
\end{eqnarray*}
|
|
If $D=0$, we have $u_I=0$ and these two roots coincide.
|
|
\end{enumerate}
|
|
It follows that if $D\geq 0$, the roots of $p$ can be found by using only
|
|
$\sqrt{~}$ and $\sqrt[3]{~}$ operations over the real numbers. If $D<0$,
|
|
it can be shown that this is not possible, and we need to approximate
|
|
the values $u_R,u_I$ in some other way. For this, we need to solve the
|
|
equation $$u^3 = C := -b/2 + i\sqrt{-D}$$
|
|
for $u$. Expressing $C$ in polar coordinates $(r,\phi)$ we get
|
|
\begin{eqnarray*}
|
|
r &=& \|C\| = \sqrt{b^2/4 -D} = \sqrt{-(a/3)^3}, \\
|
|
\cos \phi &=& -\frac{b/2}{\|C\|},
|
|
\end{eqnarray*}
|
|
where $0\leq \phi < \pi$ because of $\sqrt{-D}>0$.
|
|
Therefore, a possible choice for $u$ is $u=(r', \phi')$ with
|
|
\begin{eqnarray*}
|
|
r' &=& \sqrt{-a/3}, \\
|
|
\phi' &=& \acos \left(-\frac{b/2}{\|C\|}\right)/3.
|
|
\end{eqnarray*}
|
|
This gives
|
|
\begin{eqnarray*}
|
|
u_R &=& r' \cos \phi', \\
|
|
u_I &=& r' \sin \phi',
|
|
\end{eqnarray*}
|
|
|
|
Note that this implies $\|u\|^2 = -a/3$, showing that the roots of $p(x)$
|
|
assume the form
|
|
\begin{eqnarray*}
|
|
x_1 &=& 2u_R, \\
|
|
x_2 &=& -(u_R -u_I\sqrt{3}),\\
|
|
x_3 &=& -(u_R +u_I\sqrt{3}).
|
|
\end{eqnarray*}
|
|
If $u^3$ is real, however, this is not true, and we really need to evaluate
|
|
the factor $$\alpha := \left(1 - \frac{a}{3\|u\|^2}\right)
|
|
= \left(1 - \frac{a}{3u^2}\right)$$ in this case to obtain
|
|
the roots of $p(x)$. In any case, we originally wanted to have the roots
|
|
of $p(\tau)$. Using the substitution formula $\tau=x-\gamma_2/3$, these
|
|
roots are given by $\tau_i = x_i -\gamma_2/3, i=1,\ldots,3$.
|
|
|
|
The function @prg{solve_cubic} returns the number of distinct
|
|
real roots of $p(\tau) = c_3\tau^3+c_2\tau^2+c_1\tau+c_0=0$ and stores
|
|
them consecutively in @prg{r1}, @prg{r2} and @prg{r3}. Precondition is that
|
|
$p(\tau)$ is not a constant function.
|
|
|
|
@macro <function solve_cubic> zero = @begin
|
|
template < class NT >
|
|
int solve_cubic (NT c3, NT c2, NT c1, NT c0,
|
|
NT& r1, NT& r2, NT& r3)
|
|
{
|
|
if (c3 == 0.0) {
|
|
// quadratic equation
|
|
if (c2 == 0) {
|
|
// linear equation
|
|
CGAL_optimisation_precondition (c1 != 0);
|
|
r1 = -c0/c1;
|
|
return 1;
|
|
}
|
|
NT D = c1*c1-4*c2*c0;
|
|
if (D < 0.0)
|
|
// only complex roots
|
|
return 0;
|
|
if (D == 0.0) {
|
|
// one real root
|
|
r1 = -c1/(2.0*c2);
|
|
return 1;
|
|
}
|
|
// two real roots
|
|
r1 = (-c1 + CGAL::sqrt(D))/(2.0*c2);
|
|
r2 = (-c1 - CGAL::sqrt(D))/(2.0*c2);
|
|
return 2;
|
|
}
|
|
|
|
// cubic equation
|
|
// define the gamma_i
|
|
NT g2 = c2/c3,
|
|
g1 = c1/c3,
|
|
g0 = c0/c3;
|
|
|
|
// define a, b
|
|
NT a = g1 - g2*g2/3.0,
|
|
b = 2.0*g2*g2*g2/27.0 - g1*g2/3.0 + g0;
|
|
|
|
if (a == 0) {
|
|
// one real root
|
|
/***** r1 = cbrt(-b) - g2/3.0; *****/
|
|
r1 = exp(log(-b)/3.0) - g2/3.0;
|
|
return 1;
|
|
}
|
|
|
|
// define D
|
|
NT D = a*a*a/27.0 + b*b/4.0;
|
|
if (D >= 0.0) {
|
|
// real case
|
|
/***** NT u = cbrt(-b/2.0 + CGAL::sqrt(D)), *****/
|
|
NT u = exp(log(-b/2.0 + CGAL::sqrt(D))),
|
|
alpha = 1.0 - a/(3.0*u*u);
|
|
if (D == 0) {
|
|
// two distinct real roots
|
|
r1 = u*alpha - g2/3.0;
|
|
r2 = -0.5*alpha*u - g2/3.0;
|
|
return 2;
|
|
}
|
|
// one real root
|
|
r1 = u*alpha - g2/3.0;
|
|
return 1;
|
|
}
|
|
// complex case
|
|
NT r_prime = CGAL::sqrt(-a/3),
|
|
phi_prime = acos (-b/(2.0*r_prime*r_prime*r_prime))/3.0,
|
|
u_R = r_prime * cos (phi_prime),
|
|
u_I = r_prime * sin (phi_prime);
|
|
// three distinct real roots
|
|
r1 = 2.0*u_R - g2/3.0;
|
|
r2 = -u_R + u_I*CGAL::sqrt(3.0) - g2/3.0;
|
|
r3 = -u_R - u_I*CGAL::sqrt(3.0) - g2/3.0;
|
|
return 3;
|
|
}
|
|
|
|
@end
|
|
|
|
Here comes the actual computation of the volume minimum. To this end,
|
|
we compute the coefficients $c_3,c_2,c_1,c_0$ and find the roots
|
|
of $p(\tau)$. Among the roots we then select the one which leads
|
|
to the smallest volume,
|
|
equivalently to the largest value of $\det(M/(2w-c^TMc))=d^3/4q^2$.
|
|
The coefficients of $d$ and $q$ have been computed before in order
|
|
to find the roots, so we can directly evaluate the determinant, using
|
|
@prg{double} approximations of the coefficients.
|
|
|
|
|
|
@macro<ConicHPA2 private member functions> += @begin
|
|
double vol_minimum (RT dr, RT ds, RT dt, RT du, RT dv, RT dw) const
|
|
{
|
|
RT a2 = RT(4)*dr*ds-dt*dt,
|
|
a1 = RT(4)*r()*ds+RT(4)*dr*s()-RT(2)*t()*dt,
|
|
a0 = RT(4)*r()*s()-t()*t(),
|
|
b3 = (RT(4)*dr*ds-dt*dt)*dw-du*du*ds-dv*dv*dr+du*dv*dt,
|
|
b2 = (RT(4)*r()*ds+RT(4)*dr*s()-RT(2)*t()*dt)*dw+
|
|
(RT(4)*dr*ds-dt*dt)*w()-RT(2)*u()*du*ds-du*du*s()-
|
|
RT(2)*v()*dv*dr-dv*dv*r()+(u()*dv+du*v())*dt+du*dv*t(),
|
|
b1 = (RT(4)*r()*s()-t()*t())*dw+(RT(4)*r()*ds+RT(4)*dr*s()-
|
|
RT(2)*t()*dt)*w()-u()*u()*ds -
|
|
RT(2)*u()*du*s()-v()*v()*dr-RT(2)*v()*dv*r()+u()*v()*dt+
|
|
(u()*dv+du*v())*t(),
|
|
b0 = (RT(4)*r()*s()-t()*t())*w()
|
|
-u()*u()*s()-v()*v()*r()+u()*v()*t(),
|
|
c3 = -RT(3)*a1*b3 + RT(2)*a2*b2,
|
|
c2 = -RT(6)*a0*b3 - a1*b2 + RT(4)*a2*b1,
|
|
c1 = -RT(4)*a0*b2 + a1*b1 + RT(6)*a2*b0,
|
|
c0 = -RT(2)*a0*b1 + RT(3)*a1*b0;
|
|
|
|
if ( CGAL_NTS is_zero( c0)) return 0;// E(0) is the smallest ellipse
|
|
|
|
double roots[3];
|
|
int nr_roots = solve_cubic
|
|
(CGAL::to_double(c3), CGAL::to_double(c2),
|
|
CGAL::to_double(c1), CGAL::to_double(c0),
|
|
roots[0], roots[1], roots[2]);
|
|
CGAL_optimisation_precondition (nr_roots > 0); // minimum exists
|
|
return best_value (roots, nr_roots,
|
|
CGAL::to_double(a2), CGAL::to_double(a1),
|
|
CGAL::to_double(a0), CGAL::to_double(b3),
|
|
CGAL::to_double(b2), CGAL::to_double(b1),
|
|
CGAL::to_double(b0));
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@macro<ConicCPA2 private member functions> += @begin
|
|
double vol_minimum (FT dr, FT ds, FT dt, FT du, FT dv, FT dw) const
|
|
{
|
|
FT a2 = FT(4)*dr*ds-dt*dt,
|
|
a1 = FT(4)*r()*ds+FT(4)*dr*s()-FT(2)*t()*dt,
|
|
a0 = FT(4)*r()*s()-t()*t(),
|
|
b3 = (FT(4)*dr*ds-dt*dt)*dw-du*du*ds-dv*dv*dr+du*dv*dt,
|
|
b2 = (FT(4)*r()*ds+FT(4)*dr*s()-FT(2)*t()*dt)*dw+
|
|
(FT(4)*dr*ds-dt*dt)*w()-FT(2)*u()*du*ds-du*du*s()-
|
|
FT(2)*v()*dv*dr-dv*dv*r()+(u()*dv+du*v())*dt+du*dv*t(),
|
|
b1 = (FT(4)*r()*s()-t()*t())*dw+(FT(4)*r()*ds+FT(4)*dr*s()-
|
|
FT(2)*t()*dt)*w()-u()*u()*ds -
|
|
FT(2)*u()*du*s()-v()*v()*dr-FT(2)*v()*dv*r()+u()*v()*dt+
|
|
(u()*dv+du*v())*t(),
|
|
b0 = (FT(4)*r()*s()-t()*t())*w()
|
|
-u()*u()*s()-v()*v()*r()+u()*v()*t(),
|
|
c3 = -FT(3)*a1*b3 + FT(2)*a2*b2,
|
|
c2 = -FT(6)*a0*b3 - a1*b2 + FT(4)*a2*b1,
|
|
c1 = -FT(4)*a0*b2 + a1*b1 + FT(6)*a2*b0,
|
|
c0 = -FT(2)*a0*b1 + FT(3)*a1*b0;
|
|
|
|
if ( CGAL_NTS is_zero( c0)) return 0;// E(0) is the smallest ellipse
|
|
|
|
double roots[3];
|
|
int nr_roots = solve_cubic
|
|
(CGAL::to_double(c3), CGAL::to_double(c2),
|
|
CGAL::to_double(c1), CGAL::to_double(c0),
|
|
roots[0], roots[1], roots[2]);
|
|
CGAL_optimisation_precondition (nr_roots > 0); // minimum exists
|
|
return best_value (roots, nr_roots,
|
|
CGAL::to_double(a2), CGAL::to_double(a1),
|
|
CGAL::to_double(a0), CGAL::to_double(b3),
|
|
CGAL::to_double(b2), CGAL::to_double(b1),
|
|
CGAL::to_double(b0));
|
|
}
|
|
|
|
@end
|
|
|
|
The function @prg{best_root} returns the value in its argument array
|
|
which leads to the largest determinant $d^3/q^2$. A precondition was that
|
|
an ellipse of smallest volume exists, so the largest determinant must
|
|
be positive.
|
|
|
|
@macro <function best_value> = @begin
|
|
template < class NT >
|
|
NT best_value (NT *values, int nr_values,
|
|
NT a2, NT a1, NT a0,
|
|
NT b3, NT b2, NT b1, NT b0)
|
|
{
|
|
bool det_positive = false;
|
|
NT d, q, max_det = 0, det, best = -1;
|
|
for (int i=0; i<nr_values; ++i) {
|
|
NT x = values[i];
|
|
d = (a2*x+a1)*x+a0;
|
|
q = ((b3*x+b2)*x+b1)*x+b0;
|
|
det = d*d*d/(q*q);
|
|
if (det > 0.0)
|
|
if (!det_positive || (det > max_det)) {
|
|
max_det = det;
|
|
best = x;
|
|
det_positive = true;
|
|
}
|
|
}
|
|
CGAL_optimisation_precondition (det_positive);
|
|
return best;
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsubsection{Public Set Methods}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
Here is the set method at coordinate level.
|
|
|
|
@macro <ConicHPA2 public member functions> += @begin
|
|
void set (RT r_, RT s_, RT t_, RT u_, RT v_, RT w_)
|
|
{
|
|
_r = r_; _s = s_; _t = t_; _u = u_; _v = v_; _w = w_;
|
|
analyse();
|
|
}
|
|
|
|
@end
|
|
|
|
@macro <ConicCPA2 public member functions> += @begin
|
|
void set (FT r_, FT s_, FT t_, FT u_, FT v_, FT w_)
|
|
{
|
|
_r = r_; _s = s_; _t = t_; _u = u_; _v = v_; _w = w_;
|
|
analyse();
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
\paragraph{Opposite Conic} ~
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
The method @prg{set_opposite} just flips the representation $\r$ and the
|
|
orientation, all other derived data are taken over.
|
|
|
|
@macro <ConicHPA2 public member functions> += @begin
|
|
void set_opposite ()
|
|
{
|
|
_r = -r(); _s = -s(); _t = -t(); _u = -u(); _v = -v(); _w = -w();
|
|
o = CGAL::opposite(orientation());
|
|
}
|
|
|
|
@end
|
|
|
|
@macro <ConicCPA2 public member functions> += @begin
|
|
void set_opposite ()
|
|
{
|
|
_r = -r(); _s = -s(); _t = -t(); _u = -u(); _v = -v(); _w = -w();
|
|
o = CGAL::opposite(orientation());
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
\paragraph{Pair of lines through four points} ~
|
|
@! ---------------------------------------------------------------------------
|
|
Given $p_1 = (x_1, y_1, h_1)$,
|
|
$p_2 = (x_2, y_2, h_2)$, $p_3 = (x_3, y_3, h_3)$,
|
|
$p_4 = (x_4, y_4, h_4)$, we develop a formula for representing the
|
|
pair of lines $\overline{p_1p_2}$, $\overline{p_3p_4}$ (forming a
|
|
degenerate hyperbola) in the form of (\ref{conic_def_hom}). To this end,
|
|
let $p=(x,y,h)$ be any point and define
|
|
\begin{equation}
|
|
\label{orientation_det}
|
|
[p_i, p_j, p] := \det
|
|
\left(
|
|
\begin{array}{ccc}
|
|
x_i & x_j & x \\
|
|
y_1 & y_2 & y \\
|
|
h_1 & h_2 & h
|
|
\end{array}\right).
|
|
\end{equation}
|
|
It is well known that $[p_i,p_j,p]$ records the orientation of the point
|
|
triple: let $\ell$ be the oriented line through $p_i$ and $p_j$. Then
|
|
the following holds.
|
|
\begin{Lemma}
|
|
\label{det_lemma}
|
|
\[
|
|
p \mbox{~lies}
|
|
\left\{ \begin{array}{c}
|
|
\mbox{to the left of} \\
|
|
\mbox{on} \\
|
|
\mbox{to the right of}
|
|
\end{array} \right\} \ell \Leftrightarrow
|
|
[p_i,p_j,p]
|
|
\left\{\begin{array}{c}
|
|
>0 \\ = 0 \\ <0
|
|
\end{array}\right\}.
|
|
\]
|
|
\end{Lemma}
|
|
|
|
In particular, $p$ lies on $\overline{p_1p_2} \cup \overline{p_3p_4}$
|
|
iff $[p_1,p_2,p][p_3,p_4,p]=0$, and this expression turns out to be
|
|
of the form (\ref{conic_def_hom}), where @prg{Maple} gives us the concrete
|
|
values of $r,s,t,u,v,w$. Note that we must have $p_1\neq p_2$ and
|
|
$p_3\neq p_4$ to obtain reasonable results (and this was a precondition).
|
|
|
|
\begin{eqnarray*}
|
|
r &=& (y_1h_2-h_1y_2)(y_3h_4-h_3y_4), \\
|
|
s &=& (h_1x_2-x_1h_2)(h_3x_4-x_3h_4), \\
|
|
t &=& (h_1x_2-x_1h_2)(y_3h_4-h_3y_4)+(y_1h_2-h_1y_2)(h_3x_4-x_3h_4), \\
|
|
u &=& (-y_1x_2+x_1y_2)(y_3h_4-h_3y_4)+(y_1h_2-h_1y_2)(-y_3x_4+x_3y_4), \\
|
|
v &=& (-y_1x_2+x_1y_2)(h_3x_4-x_3h_4)+(h_1x_2-x_1h_2)(-y_3x_4+x_3y_4), \\
|
|
w &=& (-y_1x_2+x_1y_2)(-y_3x_4+x_3y_4).
|
|
\end{eqnarray*}
|
|
|
|
@macro <ConicHPA2 public member functions> += @begin
|
|
void set_linepair (const PT& p1, const PT& p2, const PT& p3,
|
|
const PT& p4, const DA& da = DA())
|
|
{
|
|
RT x1, y1, h1, x2, y2, h2, x3, y3, h3, x4, y4, h4;
|
|
da.get (p1, x1, y1, h1);
|
|
da.get (p2, x2, y2, h2);
|
|
da.get (p3, x3, y3, h3);
|
|
da.get (p4, x4, y4, h4);
|
|
|
|
// precondition: p1 != p2, p3 != p4
|
|
CGAL_optimisation_precondition
|
|
( ((x1*h2 != x2*h1) || (y1*h2 != y2*h1)) &&
|
|
((x3*h4 != x4*h3) || (y3*h4 != y4*h3)) );
|
|
|
|
RT h1x2_x1h2 = h1*x2-x1*h2;
|
|
RT h3x4_x3h4 = h3*x4-x3*h4;
|
|
RT y1h2_h1y2 = y1*h2-h1*y2;
|
|
RT y3h4_h3y4 = y3*h4-h3*y4;
|
|
RT x1y2_y1x2 = x1*y2-y1*x2;
|
|
RT x3y4_y3x4 = x3*y4-y3*x4;
|
|
|
|
_r = y1h2_h1y2 * y3h4_h3y4;
|
|
_s = h1x2_x1h2 * h3x4_x3h4;
|
|
_t = h1x2_x1h2 * y3h4_h3y4 + y1h2_h1y2 * h3x4_x3h4;
|
|
_u = x1y2_y1x2 * y3h4_h3y4 + y1h2_h1y2 * x3y4_y3x4;
|
|
_v = x1y2_y1x2 * h3x4_x3h4 + h1x2_x1h2 * x3y4_y3x4;
|
|
_w = x1y2_y1x2 * x3y4_y3x4;
|
|
|
|
analyse();
|
|
}
|
|
|
|
@end
|
|
|
|
For the Cartesian representation we proceed completely similar,
|
|
replacing values $h_1,\ldots,h_4$ by 1.
|
|
|
|
@macro <ConicCPA2 public member functions> += @begin
|
|
void set_linepair (const PT& p1, const PT& p2, const PT& p3, const PT& p4)
|
|
{
|
|
FT x1, y1, x2, y2, x3, y3, x4, y4;
|
|
dao.get (p1, x1, y1);
|
|
dao.get (p2, x2, y2);
|
|
dao.get (p3, x3, y3);
|
|
dao.get (p4, x4, y4);
|
|
|
|
// precondition: p1 != p2, p3 != p4
|
|
CGAL_optimisation_precondition
|
|
( ((x1 != x2) || (y1 != y2)) &&
|
|
((x3 != x4) || (y3 != y4)) );
|
|
|
|
FT x2_x1 = x2-x1;
|
|
FT x4_x3 = x4-x3;
|
|
FT y1_y2 = y1-y2;
|
|
FT y3_y4 = y3-y4;
|
|
FT x1y2_y1x2 = x1*y2-y1*x2;
|
|
FT x3y4_y3x4 = x3*y4-y3*x4;
|
|
|
|
_r = y1_y2 * y3_y4;
|
|
_s = x2_x1 * x4_x3;
|
|
_t = x2_x1 * y3_y4 + y1_y2 * x4_x3;
|
|
_u = x1y2_y1x2 * y3_y4 + y1_y2 * x3y4_y3x4;
|
|
_v = x1y2_y1x2 * x4_x3 + x2_x1 * x3y4_y3x4;
|
|
_w = x1y2_y1x2 * x3y4_y3x4;
|
|
|
|
analyse();
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
\paragraph{Smallest ellipse through three points} ~
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
Given $p_1 = (x_1, y_1, h_1)$,
|
|
$p_2 = (x_2, y_2, h_2)$, $p_3 = (x_3, y_3, h_3)$, we give a formula for
|
|
representing the ellipse of smallest volume containing $p_1,p_2,p_3$
|
|
in the form of (\ref{conic_def_hom}). For this, we use the following
|
|
well-known formula for this ellipse in case of Cartesian points.
|
|
\begin{Lemma}
|
|
Let $q_1,q_2,q_3$ be non-collinear Cartesian points. Then the smallest
|
|
ellipse containing $q_1,q_2,q_3$ can be written as the set of points
|
|
$q=(x,y)$ satisfying
|
|
\begin{equation}
|
|
\label{ellipse_center_form}
|
|
(q-c)^T M (q-c) = 1,
|
|
\end{equation}
|
|
where $$c = \frac{1}{3}\sum_{i=1}^3q_i, \quad
|
|
M^{-1} = \frac{2}{3} \sum_{i=1}^3 (q_i-c)(q_i-c)^T.$$
|
|
\end{Lemma}
|
|
|
|
To apply this Lemma to points $(p_1,p_2,p_3)$, we define
|
|
$q_i = (x_i/h_i, y_i/h_i)$, $i=1,\ldots,3$ and observe that
|
|
(\ref{ellipse_center_form}) can be written as
|
|
\[
|
|
q^T M q - 2q^TMc + c^TMc - 1 = 0,
|
|
\]
|
|
from which a representation in form of (\ref{conic_cart}) is obtained via
|
|
\[
|
|
\left( \begin{array}{cc} 2r & t \\ t & 2s \end{array}\right) := M,
|
|
\quad \left(\begin{array}{c} u \\ v \end{array}\right) := -Mc,
|
|
\quad 2w := c^TMc - 1.
|
|
\]
|
|
Using @prg{Maple}, we get the following values for $\r=(r,s,t,u,v,w)$.
|
|
|
|
\begin{eqnarray*}
|
|
r &=& 3(y_1^2h_2^2h_3^2-y_1h_2h_3^2y_2h_1-y_1h_2^2h_3y_3h_1+y_2^2
|
|
h_1^2h_3^2-y_2h_1^2h_3y_3h_2+y_3^2h_1^2h_2^2) / d, \\
|
|
s &=& 3(x_1^2h_2^2h_3^2-x_1h_2h_3^2x_2h_1-x_1h_2^2h_3x_3h_1+x_2^2
|
|
h_1^2h_3^2-x_2h_1^2h_3x_3h_2+x_3^2h_1^2h_2^2) / d, \\
|
|
t &=& 3(-2x_1h_2^2h_3^2y_1+x_1h_2h_3^2y_2h_1+x_1h_2^2h_3y_3h_1+
|
|
x_2h_1h_3^2y_1h_2\\
|
|
&~&-2x_2h_1^2h_3^2y_2+x_2h_1^2h_3y_3h_2+x_3h_1h_2^2
|
|
y_1h_3+x_3h_1^2h_2y_2h_3-2x_3h_1^2h_2^2y_3) / d, \\
|
|
u &=& -3(y_2^2h_1^2h_3x_3-y_2h_1^2h_3y_3x_2-y_2h_1^2y_3h_2x_3+
|
|
y_3^2h_1h_2^2x_1+y_3^2h_1^2h_2x_2+y_1^2h_2h_3^2x_2\\
|
|
&~& +y_1^2h_2^2h_3x_3-y_1
|
|
h_2h_3^2y_2x_1-y_1h_3^2y_2h_1x_2-y_1h_2^2h_3y_3x_1-y_1h_2^2y_3
|
|
h_1x_3+y_2^2h_1h_3^2x_1) / d, \\
|
|
v &=& -3(-x_1h_2h_3^2y_1x_2-x_1h_2^2h_3y_1x_3+x_1^2h_2h_3^2y_2-x_1
|
|
h_3^2y_2h_1x_2+x_1^2h_2^2h_3y_3-x_1h_2^2y_3h_1x_3\\
|
|
&~&+x_2^2h_1
|
|
h_3^2y_1-x_2h_1^2h_3y_2x_3+x_2^2h_1^2h_3y_3-x_2h_1^2y_3h_2x_3+x_3^2h_1
|
|
h_2^2y_1+x_3^2h_1^2h_2y_2) / d, \\
|
|
w &=& 3(x_2h_3x_3h_2y_1^2-x_1h_2h_3x_3y_1y_2-x_2h_1h_3x_3y_1y_2+
|
|
x_3^2h_1h_2y_1y_2-x_1h_2h_3x_2y_1y_3+x_2^2h_1h_3y_1y_3\\
|
|
&~&-x_2h_1x_3
|
|
h_2y_1y_3+x_1h_3x_3h_1y_2^2+x_1^2h_2h_3y_2y_3-x_1h_3x_2h_1y_2
|
|
y_3-x_1h_2x_3h_1y_2y_3+x_1h_2x_2h_1y_3^2) / d,
|
|
\end{eqnarray*}
|
|
where
|
|
$\delta = \left(\det \left(
|
|
\begin{array}{ccc}
|
|
x_1 & x_2 & x_3 \\
|
|
y_1 & y_2 & y_3 \\
|
|
h_1 & h_2 & h_3
|
|
\end{array}\right)\right)^2 =
|
|
(-h_1x_3y_2+x_1y_2h_3+h_1x_2y_3-x_1y_3h_2+h_2x_3y_1-x_2y_1h_3)^2.$
|
|
|
|
After pre-computing the values
|
|
\[
|
|
x_i^2, y_i^2, x_ih_i, y_ih_i, h_i^2,
|
|
\]
|
|
for $i=1,\ldots,3$, the components of the vector $\delta\r/3$ are easy
|
|
to obtain. Note that this vector is a legal representation of the ellipse
|
|
if $\delta\neq 0$. This is the case if and only if $p_1,p_2,p_3$ are
|
|
non-collinear, see Lemma \ref{det_lemma}. Moreover, one can show that
|
|
the formulas above determine an ellipse of negative orientation, regardless
|
|
of the point triple orientation. This means, if the orientation
|
|
was positive, we still need to flip the representation.
|
|
|
|
@macro<ConicHPA2 public member functions> += @begin
|
|
void set_ellipse (const PT& p1, const PT& p2, const PT& p3)
|
|
{
|
|
RT x1, y1, h1, x2, y2, h2, x3, y3, h3;
|
|
dao.get (p1, x1, y1, h1);
|
|
dao.get (p2, x2, y2, h2);
|
|
dao.get (p3, x3, y3, h3);
|
|
|
|
// precondition: p1, p2, p3 not collinear
|
|
RT det = -h1*x3*y2+h3*x1*y2+h1*x2*y3-h2*x1*y3+h2*x3*y1-h3*x2*y1;
|
|
CGAL_optimisation_precondition (!CGAL_NTS is_zero (det));
|
|
|
|
RT x1x1 = x1*x1, y1y1 = y1*y1,
|
|
x2x2 = x2*x2, y2y2 = y2*y2,
|
|
x3x3 = x3*x3, y3y3 = y3*y3, // x_i^2, y_i^2
|
|
x1h1 = x1*h1, y1h1 = y1*h1,
|
|
x2h2 = x2*h2, y2h2 = y2*h2,
|
|
x3h3 = x3*h3, y3h3 = y3*h3, // x_i h_i, y_i h_i
|
|
h1h1 = h1*h1,
|
|
h2h2 = h2*h2,
|
|
h3h3 = h3*h3, // h_i^2
|
|
two = RT(2); // 2
|
|
|
|
_r = y1y1*h2h2*h3h3 - y1h1*y2h2*h3h3 - y1h1*h2h2*y3h3 +
|
|
h1h1*y2y2*h3h3 - h1h1*y2h2*y3h3 + h1h1*h2h2*y3y3;
|
|
|
|
_s = x1x1*h2h2*h3h3 - x1h1*x2h2*h3h3 - x1h1*h2h2*x3h3 +
|
|
h1h1*x2x2*h3h3 - h1h1*x2h2*x3h3 + h1h1*h2h2*x3x3;
|
|
|
|
_t = -two*x1*y1*h2h2*h3h3 + x1h1*y2h2*h3h3 + x1h1*h2h2*y3h3 +
|
|
y1h1*x2h2*h3h3 -two*h1h1*x2*y2*h3h3 + h1h1*x2h2*y3h3 +
|
|
y1h1*h2h2*x3h3 + h1h1*y2h2*x3h3 -two*h1h1*h2h2*x3*y3;
|
|
|
|
_u = -(h1h1*y2y2*x3h3 - h1h1*x2*y2*y3h3 - h1h1*y2h2*x3*y3 +
|
|
x1h1*h2h2*y3y3 + h1h1*x2h2*y3y3 +y1y1*x2h2*h3h3 +
|
|
y1y1*h2h2*x3h3 - x1*y1*y2h2*h3h3 - y1h1*x2*y2*h3h3 -
|
|
x1*y1*h2h2*y3h3 - y1h1*h2h2*x3*y3 + x1h1*y2y2*h3h3);
|
|
|
|
_v = -(h1h1*x2x2*y3h3 - h1h1*x2*y2*x3h3 - h1h1*x2h2*x3*y3 +
|
|
y1h1*h2h2*x3x3 + h1h1*y2h2*x3x3 +x1x1*y2h2*h3h3 +
|
|
x1x1*h2h2*y3h3 - x1*y1*x2h2*h3h3 - x1h1*x2*y2*h3h3 -
|
|
x1*y1*h2h2*x3h3 - x1h1*h2h2*x3*y3 + y1h1*x2x2*h3h3);
|
|
|
|
_w = y1y1*x2h2*x3h3 - x1*y1*y2h2*x3h3 - y1h1*x2*y2*x3h3 +
|
|
y1h1*y2h2*x3x3 - x1*y1*x2h2*y3h3 + y1h1*x2x2*y3h3 -
|
|
y1h1*x2h2*x3*y3 + x1h1*y2y2*x3h3 + x1x1*y2h2*y3h3 -
|
|
x1h1*x2*y2*y3h3 - x1h1*y2h2*x3*y3 + x1h1*x2h2*y3y3;
|
|
|
|
type = ELLIPSE;
|
|
degenerate = trivial = empty = false;
|
|
o = CGAL::NEGATIVE;
|
|
if (CGAL_NTS is_positive (det)) set_opposite ();
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
As before, the Cartesian version is obtained by setting
|
|
$h_1,h_2,h_3$ to 1.
|
|
|
|
@macro<ConicCPA2 public member functions> += @begin
|
|
void set_ellipse (const PT& p1, const PT& p2, const PT& p3)
|
|
{
|
|
FT x1, y1, x2, y2, x3, y3;
|
|
dao.get (p1, x1, y1);
|
|
dao.get (p2, x2, y2);
|
|
dao.get (p3, x3, y3);
|
|
|
|
// precondition: p1, p2, p3 not collinear
|
|
FT det = -x3*y2+x1*y2+x2*y3-x1*y3+x3*y1-x2*y1;
|
|
CGAL_optimisation_precondition (!CGAL_NTS is_zero (det));
|
|
|
|
FT x1x1 = x1*x1, y1y1 = y1*y1,
|
|
x2x2 = x2*x2, y2y2 = y2*y2,
|
|
x3x3 = x3*x3, y3y3 = y3*y3, // x_i^2, y_i^2
|
|
two = FT(2);
|
|
|
|
_r = y1y1 - y1*y2 - y1*y3 +
|
|
y2y2 - y2*y3 + y3y3;
|
|
|
|
_s = x1x1 - x1*x2 - x1*x3 +
|
|
x2x2 - x2*x3 + x3x3;
|
|
|
|
_t = -two*x1*y1 + x1*y2 + x1*y3 +
|
|
y1*x2 -two*x2*y2 + x2*y3 +
|
|
y1*x3 + y2*x3 -two*x3*y3;
|
|
|
|
_u = -(y2y2*x3 - x2*y2*y3 - y2*x3*y3 +
|
|
x1*y3y3 + x2*y3y3 + y1y1*x2 +
|
|
y1y1*x3 - x1*y1*y2 - y1*x2*y2 -
|
|
x1*y1*y3 - y1*x3*y3 + x1*y2y2);
|
|
|
|
_v = -(x2x2*y3 - x2*y2*x3 - x2*x3*y3 +
|
|
y1*x3x3 + y2*x3x3 + x1x1*y2 +
|
|
x1x1*y3 - x1*y1*x2 - x1*x2*y2 -
|
|
x1*y1*x3 - x1*x3*y3 + y1*x2x2);
|
|
|
|
_w = y1y1*x2*x3 - x1*y1*y2*x3 - y1*x2*y2*x3 +
|
|
y1*y2*x3x3 - x1*y1*x2*y3 + y1*x2x2*y3 -
|
|
y1*x2*x3*y3 + x1*y2y2*x3 + x1x1*y2*y3 -
|
|
x1*x2*y2*y3 - x1*y2*x3*y3 + x1*x2*y3y3;
|
|
|
|
type = ELLIPSE;
|
|
degenerate = trivial = empty = false;
|
|
o = CGAL::NEGATIVE;
|
|
if (CGAL_NTS is_positive (det)) set_opposite();
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
\paragraph{Some ellipse through four points in convex position} ~
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
This method builds on the private method to obtain an ellipse from two
|
|
pairs of lines through the four points. For constructing this pair, we
|
|
also have a method available.
|
|
|
|
@macro<ConicHPA2 public member functions> += @begin
|
|
void set_ellipse (const PT& p1, const PT& p2,
|
|
const PT& p3, const PT& p4,
|
|
CGAL::Orientation _o = POSITIVE)
|
|
{
|
|
ConicHPA2<PT,DA> pair1, pair2;
|
|
set_two_linepairs (p1, p2, p3, p4, pair1, pair2);
|
|
set_ellipse (pair1, pair2);
|
|
analyse();
|
|
if (o != _o) set_opposite();
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@macro<ConicCPA2 public member functions> += @begin
|
|
void set_ellipse (const PT& p1, const PT& p2,
|
|
const PT& p3, const PT& p4,
|
|
CGAL::Orientation _o = POSITIVE)
|
|
{
|
|
ConicCPA2<PT,DA> pair1, pair2;
|
|
set_two_linepairs (p1, p2, p3, p4, pair1, pair2);
|
|
set_ellipse (pair1, pair2);
|
|
analyse();
|
|
if (o != _o) set_opposite();
|
|
}
|
|
|
|
@end
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
\paragraph{Unique conic through five points} ~
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
Using the previously defined methods, we implement the method to compute
|
|
the unique nontrivial conic through five given points
|
|
$p_1,p_2,p_3,p_4,p_5$. For this,
|
|
we first compute the two conics
|
|
$\C_1 = \overline{p_1p_2} \cup \overline{p_3p_4}$ and
|
|
$\C_2 = \overline{p_1p_4} \cup \overline{p_2p_3}$, using the
|
|
@prg{set_linepair} method. This gives two conics having the points
|
|
$p_1,p_2,p_3,p_4$ in common. It follows that any linear combination
|
|
of them goes through $p_1,p_2,p_3,p_4$ as well. A particular linear
|
|
combination is given by
|
|
\begin{equation}
|
|
\label{unique_conic}
|
|
\C := \C_2(p_5) \C_1 - \C_1(p_5) \C_2,
|
|
\end{equation}
|
|
and it has the property that $\C(p_5)=0$, i.e. $\C$ goes through
|
|
$p_1,\ldots,p_5$. In case all points are distinct, this is the unique
|
|
nontrivial conic through the points.
|
|
|
|
@macro<ConicHPA2 public member functions> += @begin
|
|
void set (const PT& p1, const PT& p2, const PT& p3, const PT& p4,
|
|
const PT& p5, CGAL::Orientation _o = POSITIVE)
|
|
{
|
|
ConicHPA2<PT,DA> c1; c1.set_linepair (p1, p2, p3, p4);
|
|
ConicHPA2<PT,DA> c2; c2.set_linepair (p1, p4, p2, p3);
|
|
set_linear_combination (c2.evaluate (p5), c1,
|
|
-c1.evaluate (p5), c2);
|
|
analyse();
|
|
// precondition: all points distinct <=> conic nontrivial
|
|
CGAL_optimisation_precondition (!is_trivial());
|
|
if (o != _o) set_opposite();
|
|
}
|
|
|
|
@end
|
|
|
|
@macro<ConicCPA2 public member functions> += @begin
|
|
void set (const PT& p1, const PT& p2, const PT& p3, const PT& p4,
|
|
const PT& p5, CGAL::Orientation _o = POSITIVE)
|
|
{
|
|
ConicCPA2<PT,DA> c1; c1.set_linepair (p1, p2, p3, p4);
|
|
ConicCPA2<PT,DA> c2; c2.set_linepair (p1, p4, p2, p3);
|
|
set_linear_combination (c2.evaluate (p5), c1,
|
|
-c1.evaluate (p5), c2);
|
|
analyse();
|
|
// precondition: all points distinct <=> conic nontrivial
|
|
CGAL_optimisation_precondition (!is_trivial());
|
|
if (o != _o) set_opposite();
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@! ---------------------------------------------------------------------------
|
|
@subsection{IO routines}
|
|
@! ---------------------------------------------------------------------------
|
|
|
|
@macro <ConicHPA2 I/O routines> = @begin
|
|
template< class _PT, class _DA>
|
|
std::ostream& operator << ( std::ostream& os, const ConicHPA2<_PT,_DA>& c)
|
|
{
|
|
return( os << c.r() << ' ' << c.s() << ' ' << c.t() << ' '
|
|
<< c.u() << ' ' << c.v() << ' ' << c.w());
|
|
}
|
|
|
|
template< class _PT, class _DA>
|
|
std::istream& operator >> ( std::istream& is, ConicHPA2<_PT,_DA>& c)
|
|
{
|
|
typedef ConicHPA2<_PT,_DA> Conic;
|
|
typedef typename _DA::RT RT;
|
|
|
|
RT r, s, t, u, v, w;
|
|
is >> r >> s >> t >> u >> v >> w;
|
|
c.set( r, s, t, u, v, w);
|
|
|
|
return( is);
|
|
}
|
|
@end
|
|
|
|
@macro <ConicCPA2 I/O routines> = @begin
|
|
template< class _PT, class _DA>
|
|
std::ostream& operator << ( std::ostream& os, const ConicCPA2<_PT,_DA>& c)
|
|
{
|
|
return( os << c.r() << ' ' << c.s() << ' ' << c.t() << ' '
|
|
<< c.u() << ' ' << c.v() << ' ' << c.w());
|
|
}
|
|
|
|
template< class _PT, class _DA>
|
|
std::istream& operator >> ( std::istream& is, ConicCPA2<_PT,_DA>& c)
|
|
{
|
|
typedef ConicCPA2<_PT,_DA> Conic;
|
|
typedef typename _DA::FT FT;
|
|
|
|
FT r, s, t, u, v, w;
|
|
is >> r >> s >> t >> u >> v >> w;
|
|
c.set( r, s, t, u, v, w);
|
|
|
|
return( is);
|
|
}
|
|
@end
|
|
|
|
@!----------------------------------------------------------------------------
|
|
@section{Files}
|
|
@!----------------------------------------------------------------------------
|
|
|
|
@i share/namespace.awi
|
|
|
|
@!----------------------------------------------------------------------------
|
|
@subsection{{\tt Conic\_misc.h}}
|
|
@!----------------------------------------------------------------------------
|
|
|
|
Here we collect all types and functions that are independent of the
|
|
representation type @prg{R}. These are the declarations of the enumeration
|
|
types @prg{Conic_type} and @prg{Convex_side}, the conic output
|
|
function and the functions in connection with the solution of cubic equations.
|
|
|
|
@file <include/CGAL/Conic_misc.h> = @begin
|
|
@<file header>("include/CGAL/Conic_misc.h","2D Conic")
|
|
|
|
#ifndef CGAL_CONIC_MISC_H
|
|
#define CGAL_CONIC_MISC_H
|
|
|
|
#ifndef CGAL_OPTIMISATION_ASSERTIONS_H
|
|
# include <CGAL/Optimisation/assertions.h>
|
|
#endif
|
|
|
|
@<namespace begin>("CGAL")
|
|
|
|
@<Conic_2 declaration>
|
|
@<Conic_type declaration>
|
|
@<Convex_side declaration>
|
|
|
|
@!<Conic_2 Window_stream output>
|
|
|
|
@<function best_value>
|
|
@<function solve_cubic>
|
|
|
|
@<namespace end>("CGAL")
|
|
|
|
#endif // CGAL_CONIC_MISC_H
|
|
|
|
@<end of file line>
|
|
@end
|
|
|
|
@!----------------------------------------------------------------------------
|
|
@subsection{{\tt Conic\_2.h}}
|
|
@!----------------------------------------------------------------------------
|
|
|
|
This file contains the implementation of the class @prg{Conic_2<R>}.
|
|
Depending on the loaded representation classes, the representation specific
|
|
classes @prg{ConicHPA2<PT,DA>} and/or @prg{ConicCPA2<PT,DA>} are
|
|
included before that.
|
|
|
|
@file <include/CGAL/Conic_2.h> = @begin
|
|
@<file header>("include/CGAL/Conic_2.h","2D Conic")
|
|
|
|
#ifndef CGAL_CONIC_2_H
|
|
#define CGAL_CONIC_2_H
|
|
|
|
#ifndef CGAL_REP_CLASS_DEFINED
|
|
# error no representation class defined
|
|
#endif // CGAL_REP_CLASS_DEFINED
|
|
|
|
#ifdef CGAL_HOMOGENEOUS_H
|
|
# include <CGAL/ConicHPA2.h>
|
|
#endif
|
|
|
|
#ifdef CGAL_CARTESIAN_H
|
|
# include <CGAL/ConicCPA2.h>
|
|
#endif
|
|
|
|
#ifndef CGAL_POINT_2_H
|
|
# include <CGAL/Point_2.h>
|
|
#endif
|
|
|
|
#ifndef CGAL_IO_FORWARD_DECL_WINDOW_STREAM_H
|
|
#include <CGAL/IO/forward_decl_window_stream.h>
|
|
#endif
|
|
|
|
@<namespace begin>("CGAL")
|
|
|
|
@<Optimisation_ellipse_2 declaration>
|
|
|
|
@<namespace end>("CGAL")
|
|
|
|
@<Optimisation_ellipse_2 I/O operator declaration>
|
|
|
|
@<namespace begin>("CGAL")
|
|
|
|
@<Conic_2 interface and implementation>
|
|
|
|
#ifndef CGAL_NO_OSTREAM_INSERT_CONIC_2
|
|
@<Conic_2 I/O routines>
|
|
#endif // CGAL_NO_OSTREAM_INSERT_CONIC_2
|
|
|
|
@<namespace end>("CGAL")
|
|
|
|
#endif // CGAL_CONIC_2_H
|
|
|
|
@<end of file line>
|
|
@end
|
|
|
|
@!----------------------------------------------------------------------------
|
|
@subsection{{\tt ConicHPA2.h}}
|
|
@!----------------------------------------------------------------------------
|
|
|
|
Here is the class @prg{ConicHPA2<PT,DA>}\ldots
|
|
|
|
@file <include/CGAL/ConicHPA2.h> = @begin
|
|
@<file header>("include/CGAL/ConicHPA2.h","2D Conic")
|
|
|
|
#ifndef CGAL_CONICHPA2_H
|
|
#define CGAL_CONICHPA2_H
|
|
|
|
// includes
|
|
#ifndef CGAL_CONIC_MISC_H
|
|
# include <CGAL/Conic_misc.h>
|
|
#endif
|
|
#ifndef CGAL_OPTIMISATION_ASSERTIONS_H
|
|
# include <CGAL/Optimisation/assertions.h>
|
|
#endif
|
|
|
|
@<namespace begin>("CGAL")
|
|
|
|
@<ConicHPA2 declaration>
|
|
@<ConicHPA2 interface and implementation>
|
|
|
|
#ifndef CGAL_NO_OSTREAM_INSERT_CONICHPA2
|
|
@<ConicHPA2 I/O routines>
|
|
#endif // CGAL_NO_OSTREAM_INSERT_CONICHPA2
|
|
|
|
@<namespace end>("CGAL")
|
|
|
|
#endif // CGAL_CONICHPA2_H
|
|
|
|
@<end of file line>
|
|
@end
|
|
|
|
@!----------------------------------------------------------------------------
|
|
@subsection{{\tt ConicCPA2.h}}
|
|
@!----------------------------------------------------------------------------
|
|
|
|
\ldots and the class @prg{ConicCPA2<PT,DA>}.
|
|
|
|
@file <include/CGAL/ConicCPA2.h> = @begin
|
|
@<file header>("include/CGAL/ConicCPA2.h","2D Conic")
|
|
|
|
#ifndef CGAL_CONICCPA2_H
|
|
#define CGAL_CONICCPA2_H
|
|
|
|
// includes
|
|
#ifndef CGAL_CONIC_MISC_H
|
|
# include <CGAL/Conic_misc.h>
|
|
#endif
|
|
#ifndef CGAL_OPTIMISATION_ASSERTIONS_H
|
|
# include <CGAL/Optimisation/assertions.h>
|
|
#endif
|
|
|
|
@<namespace begin>("CGAL")
|
|
|
|
@<ConicCPA2 declaration>
|
|
@<ConicCPA2 interface and implementation>
|
|
|
|
#ifndef CGAL_NO_OSTREAM_INSERT_CONICCPA2
|
|
@<ConicCPA2 I/O routines>
|
|
#endif // CGAL_NO_OSTREAM_INSERT_CONICCPA2
|
|
|
|
@<namespace end>("CGAL")
|
|
|
|
#endif // CGAL_CONICCPA2_H
|
|
|
|
@<end of file line>
|
|
@end
|
|
|
|
@! ----------------------------------------------------------------------------
|
|
@! Conic_2_Window_stream.h
|
|
@! ----------------------------------------------------------------------------
|
|
|
|
\subsection{Conic\_2\_Window\_stream.h}
|
|
@file <include/CGAL/IO/Conic_2_Window_stream.h> = @begin
|
|
@<file header>(
|
|
"include/CGAL/IO/Conic_2_Window_stream.h",
|
|
"graphical output to `leda_window' for Conic_2 algo.")
|
|
|
|
// Each of the following operators is individually
|
|
// protected against multiple inclusion.
|
|
|
|
// Window_stream I/O operators
|
|
// ===========================
|
|
|
|
// Conic_2
|
|
// -------
|
|
@<Conic_2 graphical output operator>
|
|
|
|
@<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",
|
|
"Conic_2",
|
|
"$Revision$","$Date$",
|
|
"Bernd Gärtner, Sven Schönherr <sven@@inf.ethz.ch>",
|
|
"ETH Zürich (Bernd Gärtner <gaertner@@inf.ethz.ch>)",
|
|
"@2")
|
|
@end
|
|
|
|
@! ===== EOF ==================================================================
|