cgal/Nef_2/noweb/Simple_extended_kernel.lw

1773 lines
64 KiB
Plaintext

%------------------------------------------------------------------------------
%KILLSTART DISS REP
%LDEL TRACE.*?\)\;
%LDEL CGAL_NTS\
\documentclass[a4paper]{article}
\usepackage{MyLweb}
\input{defs}
\excludeversion{ignoreindiss}
\excludeversion{ignore}
\begin{document}
\title{Simple Extended Kernel}
\author{Michael Seel}
\maketitle
\tableofcontents
%KILLEND REP
\section{Introduction}\label{Extended geometry}
It is convenient to extend the vision of standard rational points by
so-called {\em non-standard} points. We have one non-standard point
for each equivalence class of rays. Two rays are called equivalent if
one is contained in the other. The geometric properties of
non-standard points are derived by giving them a geometric
interpretation by means of an infinimaximal $R$. $R$ is a real
variable. The value of $R$ is finite but larger than the value of any
concrete real number. Let $F$ be the square box with corners
|NW(-R,R)|, |NE(R,R)|, |SE(R,-R)|, and |SW(-R,-R)|. Let $p$ be a
non-standard point and let $r$ be a ray defining it. If the frame $F$
contains the source point of $r$ then let $p(R)$ be the intersection
of $r$ with the frame $F$, if $F$ does not contain the source of $r$
then $p(R)$ is undefined. For a standard point let $p(R)$ be equal to
$p$ if $p$ is contained in the frame $F$ and let $p(R)$ be undefined
otherwise. Clearly, for any standard or non-standard point $p(R)$ is
defined for any sufficiently large $R$.
Let $f$ be any function on standard points, say with $k$ arguments. We
call $f$ {\em extensible} if for any $k$ points $p_1$, \ldots, $p_k$
the function value $f(p_1(R),\ldots,p_k(R))$ is constant for all
sufficiently large $R$ and has the same value as the function
evaluated at a fixed large enough value $R_0$. We also consider
geometric constructions. Let $g$ be a construction on standard points
constructing a tuple of $l$ points from a tuple of $k$ points. We call
$g$ {\em extensible} if for any $k$ points $p_1$, \ldots, $p_k$ the
construction is closed in our set of extended points: if it constructs
a point tuple $q_1$, \ldots, $q_l$ of extended points for all
sufficiently large $R$ with the property that by fixing a large enough
$R_0$ the tuple is the result of the standard construction. As we
will see in a moment the predicates |lexicographic order| of points,
|orientation|, and |side_of_circle| are extensible. Also the
calculation of the point in the |intersection| of line segments
defined on two pairs of points is extensible.
For a formal definition of extended points, extended segments and the
corresponding predicates see the technical
report~\cite{TR:infimaximalframes}.
\section{Homogeneous Representation}
%KILLEND DISS
We implement planar extended points by a homogeneous component
representation in a polynomial ring type which provides standard ring
operations like $+,-,*$. The definition of extended points puts
constraints on the kind of polynomials representing the
coordinates. We have seen that our extensible predicates are defined
via polynomials in the coordinate polynomials and as such are
extensible via the limit process on polynomials. Going to infinity the
value of a polynomial is determined by the highest-order nonzero
coefficient.
Using extensible predicates on an input set of extended points in the
execution of an algorithm we can determine a concrete value $R_0$
which ensures their extendibility for all $R \geq R_0$ (for each
evaluation determine one $R_i$ and take the maximum of all). Plugging
$R_0$ into all coordinate polynomials leads back to standard affine
geometry and standard predicates. This gives us the possibility to
argue also about the correctness of our algorithms. If the algorithm
is proven to be correct for standard geometry and it computes a
certain output then it will also calculate some extended geometric
result when plugging in extended points and when all geometric
predicates are extensible.
Note that in this way we can design algorithms that use ray like
structures much simpler by enclosing finite structures into the box
$F$ and pruning the rays by means of the frame in a ray tip. The
calculation with the extended points makes algorithmic decisions
trivial if the predicates we use are extensible in the above sense.
In this section we will describe how extended points are stored: they
are composed from the 2D CGAL kernel point type
|Homogeneous<...>::Point_2| and the polynomial ring number type
|Polynomial<...>|. We also describe how the affine world of standard
points and rays interacts with the unifying concept extended
point. This interaction has two directions: the construction of an
extended point from a standard object (point or ray) and the reversal
extraction depending on the character of the extended
point. Afterwards, we show how simple it is to implement predicates
and the intersection construction on top of the genericity of CGAL's
standard kernel. We will encounter the problem of simplification of
polynomials there. And finally, we give some details about
visualization issues of extended objects.
We often use the short term \emph{epoint} to denote extended points.
Each epoint is either a standard point, one of the corner ray points
or lies in the relative interior of one of the frame segments.
\subsubsection*{Extended Points}
The tip of a ray $l$ can be described in two ways. First in form of
its underlying oriented line equation $a x + b y + c = 0$. But also by
its point-vector form $p = p_0 + \lambda d$. The former is the
standard representation of lines. The latter is more suitable to
explore the character of the corresponding extended point.
Starting from the second representation we have two points $p_0$ and
$p_0 + d$ on the line. Now all points on the line can also be
described by the determinant equation:
\begin{eqnarray*}
\begin{vmatrix}
1 & 1 & 1 \\
x_0 & x_0 + d_x & x \\
y_0 & y_0 + d_y & y
\end{vmatrix}
& = & 0
\end{eqnarray*}
and developing this by the last column leads us to
$-d_y x + d_x y + (x_0 d_y - y_0 d_x) = 0$.
Thus the direction vector is:
\[ d = \binom{d_x}{d_y} = \binom{b}{-a} \]
A point on the line specified by the line equation is:
\[ p_0 = \begin{cases} (0,-c/b) & b \neq 0 \\
(-c/a,0) & a \neq 0 \end{cases} \] Both $a$ and
$b$ cannot be zero. In the following assume |Line_2| to be a model for
the CGAL standard geometric kernel.
<<line conversion methods>>=
static RT dx(const Line_2& l) { return l.b(); }
static RT dy(const Line_2& l) { return -l.a(); }
@ Depending on the slope $m = d_y/d_x (d_x \neq 0)$ of a line $l$ we
can define its vertical distance to the origin. If $d_x \neq 0$
$(\Labs{m} \neq \infty)$ then the ordinate intersection $d_o$
determines that distance $d_o = -c / b$.
<<line conversion methods>>=
static FT ordinate_distance(const Line_2& l)
{ return Kernel::make_FT(-l.c(),l.b()); }
@ We introduce enumeration specifiers that describe extended points.
<<enumerate extended point character>>=
enum Point_type { SWCORNER=1, LEFTFRAME, NWCORNER,
BOTTOMFRAME, STANDARD, TOPFRAME,
SECORNER, RIGHTFRAME, NECORNER };
@ Now if we look at a non-standard point $p$ with underlying line
equation $ax + by + c = 0$ the frame segment which is hit by the ray
tip is determined by the slope and in case $\Labs{m}=1$ by the
distance $d_o$ defined above. Look for example at a non-standard point
hitting the left frame segment. This is generally the case if $d_x <
0$ and $\Labs{m}<1$. The latter is equivalent to the condition
$\Labs{d_x} > \Labs{d_y}$. A special case is $\Labs{m} = 1$. Then, we
only hit the left segment if either $m=-1 \AND d_o < 0$ or $m=1 \AND
d_o > 0$. The latter can be checked by $|sign|(d_y) == - |sign|(d_o)$.
Note that because $\Labs{m} \leq 1$ the line indeed intersects the
$y$-axis. The other cases follow by symmetric reasoning.
<<line conversion methods>>=
static Point_type determine_type(const Line_2& l)
{
RT adx = CGAL_NTS abs(dx(l)), ady = CGAL_NTS abs(dy(l));
int sdx = CGAL_NTS sign(dx(l)), sdy = CGAL_NTS sign(dy(l));
int cmp_dx_dy = CGAL_NTS compare(adx,ady), s(1);
if (sdx < 0 && ( cmp_dx_dy > 0 || cmp_dx_dy == 0 &&
sdy != (s = CGAL_NTS sign(ordinate_distance(l))))) {
if (0 == s) return ( sdy < 0 ? SWCORNER : NWCORNER );
else return LEFTFRAME;
} else if (sdx > 0 && ( cmp_dx_dy > 0 || cmp_dx_dy == 0 &&
sdy != (s = CGAL_NTS sign(ordinate_distance(l))))) {
if (0 == s) return ( sdy < 0 ? SECORNER : NECORNER );
else return RIGHTFRAME;
} else if (sdy < 0 && ( cmp_dx_dy < 0 || cmp_dx_dy == 0 &&
ordinate_distance(l) < FT(0))) {
return BOTTOMFRAME;
} else if (sdy > 0 && ( cmp_dx_dy < 0 || cmp_dx_dy == 0 &&
ordinate_distance(l) > FT(0))) {
return TOPFRAME;
}
CGAL_assertion_msg(false," determine_type: degenerate line.");
return (Point_type)-1; // never come here
}
@ All the operations above are packaged into the class
|Line_to_epoint<R>|, where |R| is a model of the CGAL standard 2d
geometric kernel. From |R| we derive the types |RT|, |FT|, and
|Line_2| as used in the code.
\begin{ignoreindiss}
<<Line_to_epoint.h>>=
<<CGAL L2E Header>>
#ifndef CGAL_LINE_TO_EPOINT_H
#define CGAL_LINE_TO_EPOINT_H
CGAL_BEGIN_NAMESPACE
template <class Kernel_>
struct Line_to_epoint {
typedef Kernel_ Kernel;
typedef typename Kernel::RT RT;
typedef typename Kernel::FT FT;
typedef typename Kernel::Line_2 Line_2;
<<enumerate extended point character>>
<<line conversion methods>>
};
CGAL_END_NAMESPACE
#endif //CGAL_LINE_TO_EPOINT_H
@ \end{ignoreindiss}
Any non-standard point can be expressed as a pair of two polynomials
in a variable $R$ --- our infimaximal symbolic number. Let's look at
our example again. Our point $p$ on the left frame segment supported
by the line $ax + by + c = 0$ can be described by the tuple $(-R, a/b
R -c/b)$. Accordingly, a ray tip on the upper frame segment can be
described by $(-b/a R - c/a, R)$. Note that the denominators are
nonzero in both cases due to their frame position. Thus we can store
epoints in terms of linear polynomials $m R + n$. For standard points
the polynomials are just constant with $m=0$. We give the
representation of all points in homogeneous representation, such that
all coefficients can be represented by a ring type.
\begin{equation}\label{pointsfromline}
\begin{array}{ll}
\text{STANDARD} & p = (x,y,w) \\
\text{CORNER} & p = (\pm R, \pm R, 1) \\
\text{LEFTFRAME} & p = (-bR, a R - c, b) \\
\text{RIGHTFRAME} & p = ( bR, -a R - c, b) \\
\text{BOTTOMFRAME} & p = ( bR - c, -aR, a) \\
\text{TOPFRAME} & p = (-b R -c, aR, a)
\end{array}
\end{equation}
The general representation can be taken to be $p = (m_x R + n_x, m_y R
+ n_y, w)$ where $m_{x,y}, n_{x,y}, w$ are objects of a ring number
type. We provide the functionality of extended points bundled into an
extended geometry kernel. This kernel carries the types, predicates,
and constructions that we need in our algorithms. The kernel concept
is specified in the manual page |ExtendedKernelTraits_2| of the
appendix.
\subsubsection*{A decorator wraps functionality}
We obtain the extended point class by plugging our polynomial
arithmetic type into the standard homogeneous point type from the CGAL
kernel. We create a traits class |Extended_homogeneous<RT>| that
carries all types and methods that are used in our algorithmic
framework.
To ensure the special character of homogeneous points concerning their
coordinates and to offer a comfortable construction of such points we
make |Extended_homogeneous<RT>| a decorator/factory data type
\cite{designpatterns95} for the geometric objects. Construction and
conversion routines can be accessed as methods of the factory.
<<extended homogeneous>>=
/*{\Moptions outfile=ExtendedKernelTraits_2.man}*/
/*{\Moptions print_title=yes }*/
/*{\Msubst Extended_homogeneous ExtendedKernelTraits_2}*/
/*{\Manpage {ExtendedKernelTraits_2}{}{Extended Kernel Traits}{K}}*/
template <class RT_>
class Extended_homogeneous : public
CGAL::Homogeneous< CGAL::Polynomial<RT_> > { public:
/*{\Mdefinition |\Mname| is a kernel concept providing extended
geometry\footnote{It is called extended geometry for simplicity,
though it is not a real geometry in the classical sense.}. Let |\Mvar|
be an instance of the data type |\Mname|. The central notion of
extended geomtry are extended points. An extended point represents
either a standard affine point of the Cartesian plane or a
non-standard point representing the equivalence class of rays where
two rays are equivalent if one is contained in the other.
Let $R$ be an infinimaximal number\footnote{A finite but very large
number.}, $F$ be the square box with corners $NW(-R,R)$, $NE(R,R)$,
$SE(R,-R)$, and $SW(-R,-R)$. Let $p$ be a non-standard point and let
$r$ be a ray defining it. If the frame $F$ contains the source point
of $r$ then let $p(R)$ be the intersection of $r$ with the frame $F$,
if $F$ does not contain the source of $r$ then $p(R)$ is undefined.
For a standard point let $p(R)$ be equal to $p$ if $p$ is contained in
the frame $F$ and let $p(R)$ be undefined otherwise. Clearly, for any
standard or non-standard point $p$, $p(R)$ is defined for any
sufficiently large $R$. Let $f$ be any function on standard points,
say with $k$ arguments. We call $f$ {\em extensible} if for any $k$
points $p_1$, \ldots, $p_k$ the function value
$f(p_1(R),\ldots,p_k(R))$ is constant for all sufficiently large
$R$. We define this value as $f(p_1,\ldots,p_k)$. Predicates like
lexicographic order of points, orientation, and incircle tests are
extensible.
An extended segment is defined by two extended points such that it
is either an affine segment, an affine ray, an affine line, or a
segment that is part of the square box. Extended directions extend
the affine notion of direction to extended objects.
This extended geometry concept serves two purposes. It offers
functionality for changing between standard affine and extended
geometry. At the same time it provides extensible geometric primitives
on the extended geometric objects.}*/
<<extended homogeneous kernel interface types>>
<<extended homogeneous kernel members>>
};
@ We introduce the standard affine types into our kernel by prefixing
them accordingly. The extended types carry the typenames without the
prefix. Note that this decorator serves as a traits class to be used
in algorithms that are based on our infimaximal frame. It is also the
glue between the CGAL standard kernel and the extended geometric
objects.
<<extended homogeneous kernel interface types>>=
typedef CGAL::Homogeneous< CGAL::Polynomial<RT_> > Base;
typedef Extended_homogeneous<RT_> Self;
/*{\Mtypes 8.5}*/
/*{\Mtext \headerline{Affine kernel types}}*/
typedef CGAL::Homogeneous<RT_> Standard_kernel;
/*{\Mtypemember the standard affine kernel.}*/
typedef RT_ Standard_RT;
/*{\Mtypemember the standard ring type.}*/
typedef typename Standard_kernel::FT Standard_FT;
/*{\Xtypemember the field type.}*/
typedef typename Standard_kernel::Point_2 Standard_point_2;
/*{\Mtypemember standard points.}*/
typedef typename Standard_kernel::Segment_2 Standard_segment_2;
/*{\Mtypemember standard segments.}*/
typedef typename Standard_kernel::Line_2 Standard_line_2;
/*{\Mtypemember standard oriented lines.}*/
typedef typename Standard_kernel::Direction_2 Standard_direction_2;
/*{\Mtypemember standard directions.}*/
typedef typename Standard_kernel::Ray_2 Standard_ray_2;
/*{\Mtypemember standard rays.}*/
typedef typename Standard_kernel::Aff_transformation_2
Standard_aff_transformation_2;
/*{\Mtypemember standard affine transformations.}*/
/*{\Mtext \headerline{Extended kernel types}}*/
typedef typename Base::RT RT;
/*{\Mtypemember the ring type of our extended kernel.}*/
typedef typename Base::Point_2 Point_2;
/*{\Mtypemember extended points.}*/
typedef typename Base::Segment_2 Segment_2;
/*{\Mtypemember extended segments.}*/
typedef typename Base::Direction_2 Direction_2;
/*{\Mtypemember extended directions.}*/
typedef typename Base::Line_2 Line_2;
// used only internally
enum Point_type { SWCORNER=1, LEFTFRAME, NWCORNER,
BOTTOMFRAME, STANDARD, TOPFRAME,
SECORNER, RIGHTFRAME, NECORNER };
/*{\Menum a type descriptor for extended points.}*/
@ We now implement the construction deduced above. For a non-standard
point on the upper frame segment supported by a line $l \equiv ax + by
+c = 0$ the polynomial coefficients are $m = -b/a, n = -c/a$.
Accordingly on the left frame segment $m = -a/b, n = -c/b$.
<<non-standard point construction>>=
Point_2 epoint(const Standard_RT& m1, const Standard_RT& n1,
const Standard_RT& m2, const Standard_RT& n2,
const Standard_RT& n3) const
{ return Point_2(RT(n1,m1),RT(n2,m2),RT(n3)); }
Point_2 construct_point(const Standard_line_2& l, Point_type& t) const
{
t = (Point_type)Line_to_epoint<Standard_kernel>::determine_type(l);
Point_2 res;
switch (t) {
case SWCORNER: res = epoint(-1, 0, -1, 0, 1); break;
case NWCORNER: res = epoint(-1, 0, 1, 0, 1); break;
case SECORNER: res = epoint( 1, 0, -1, 0, 1); break;
case NECORNER: res = epoint( 1, 0, 1, 0, 1); break;
case LEFTFRAME:
res = epoint(-l.b(), 0, l.a(), -l.c(), l.b()); break;
case RIGHTFRAME:
res = epoint( l.b(), 0, -l.a(), -l.c(), l.b()); break;
case BOTTOMFRAME:
res = epoint( l.b(), -l.c(), -l.a(), 0, l.a()); break;
case TOPFRAME:
res = epoint(-l.b(), -l.c(), l.a(), 0, l.a()); break;
default: CGAL_assertion_msg(0,"EPoint type not correct!");
}
return res;
}
@ \subsubsection*{Type determination}
To evaluate the results of an algorithm one also needs an operation
that deduces the type from an epoint $p$. From the polynomial
representation we can easily defer this type by checking the
homogeneous components |p.hx()| and |p.hy()|. Of course standard
points have zero degree in both x- and y-components. For any
non-standard $p$ on the frame we know that the relative interior of
the frame box segments is specified by the condition that
$\Labs{|p.hx()|} \gtrless \Labs{|p.hy()|}$. The sign of the larger
component (larger with respect to its absolute value) determines the
box segment. Equality $\Labs{|p.hx()|} = \Labs{|p.hy()|}$ specifies
the corners of the box.
@c
Point_type type(const Point_2& p)
{
CGAL_assertion(p.hx().degree()>=0 && p.hy().degree()>=0 );
CGAL_assertion(p.hw().degree()==0);
if (p.hx().degree() == 0 && p.hy().degree() == 0)
return STANDARD;
// now we are on the square frame box
RT rx = p.hx(), ry = p.hy();
int sx = sign(rx), sy = sign(ry);
if ( sx < 0 ) rx = -rx;
if ( sy < 0 ) ry = -ry;
if ( rx > ry )
if (sx > 0) return RIGHTFRAME; else return LEFTFRAME;
if ( rx < ry )
if (sy > 0) return TOPFRAME; else return BOTTOMFRAME;
// now (rx == ry)
if ( sx == sy ) {
if (sx < 0) return SWCORNER; else return NECORNER;
} else { CGAL_assertion(sx==-sy);
if (sx < 0) return NWCORNER; else return SECORNER;
}
}
@ \subsubsection*{Visualization}
We finally treat the problem of how to visualize extended objects.
Given a set $S$ of extended points let us determine a concrete frame
radius $R_0$ such that all standard points in $S$ are contained inside
our frame but also all non-standard points in $S$ can be drawn on the
correct frame box segments. Note that the latter is not trivially true
for arbitrary small values of $R_0$.
Consider a line $l$ with slope $m$. If $\Labs{m} \neq 1$ the line $l$
intersects both angular bisectors of our coordinate frame. The
intersection point of the larger absolute coordinates determines a
lower bound for $R_0$. If $\Labs{m} = 1$ a natural lower bound for
$R_0$ is half the length of the ordinate segment on the y-axis between
$l$ and the origin. See Figure \figref{infiframevis}.
\displaylps{infiframevis} {The point $P_1$ determines a lower bound
for the frame radius $R_0$ to display the non-standard points at the
tips of line $l$. In case (A) we take the absolute value of its
coordinates, in case (B) we take half of its distance to the origin.}
For our polynomial representation $(m_x R + n_x, m_y R + n_y,w)$ we
know that for points in the interior of the frame box segments it
holds that $\Labs{(m_x R + n_x)} \gtrless \Labs{(m_y R + n_y)}$. In
either case we can set both polynomials equal and resolve for $R$ if
$\Labs{m_x} \neq \Labs{m_y}$. $R = \Labs{(n_x - n_y)}/\Labs{(m_y -
m_x)}$ presumed the line is not parallel to any of the angular
bisectors of the coordinate frame. If $\Labs{m_x} = \Labs{m_y}$ then
the constant parts $n_x/w$ or $n_y/w$ determine the abscissa or
ordinate distances between the underlying line and the origin
(depending on the frame segment that contains the extended point). At
least one of $n_x/w$ or $n_y/w$ is actually zero (by definition of our
extended points). In this case the minimum frame radius $R_0$ is half
the absolute value of the abscissa or ordinate distance of the line to
the origin.
We now code this determination of $R_0$ for an iterator range of
extended points. Note that the common denominator of the homogenous
representation is always a constant and positive. Note that we round
the integral division operations on the ring type up.
<<determining a lower bound for R>>=
template <class Forward_iterator>
void determine_frame_radius(Forward_iterator start, Forward_iterator end,
Standard_RT& R0) const
{ Standard_RT R, mx, nx, my, ny;
while ( start != end ) {
Point_2 p = *start++;
if ( is_standard(p) ) {
R = CGAL_NTS max(CGAL_NTS abs(p.hx()[0])/p.hw()[0],
CGAL_NTS abs(p.hy()[0])/p.hw()[0]);
} else {
RT rx = CGAL_NTS abs(p.hx()), ry = CGAL_NTS abs(p.hy());
mx = ( rx.degree()>0 ? rx[1] : 0 ); nx = rx[0];
my = ( ry.degree()>0 ? ry[1] : 0 ); ny = ry[0];
if ( mx > my ) R = CGAL_NTS abs((ny-nx)/(mx-my));
else if ( mx < my ) R = CGAL_NTS abs((nx-ny)/(my-mx));
else /* mx == my */ R = CGAL_NTS abs(nx-ny)/(2*p.hw()[0]);
}
R0 = CGAL_NTS max(R+1,R0);
}
}
@ \subsubsection*{Extended predicates}
Remember why the predicates |compare_xy|, |orientation|,
|side_of_circle| are extensible. The first is just a cascaded
comparison of coordinates (sign of their difference), the latter are
sign-of-determinant calculations. The orientation predicate on three
points is defined by the homogeneous expression:
\begin{displaymath}
\mathrm{orientation}(p1,p2,p3) = \mathrm{sign}
\begin{vmatrix}
x_1 & x_2 & x_3 \\
y_1 & y_2 & y_3 \\
w_1 & w_2 & w_3
\end{vmatrix}
\end{displaymath}
Thus, evaluation of the sign means looking at the sign of the
coefficient of $R$ if it is nonzero, or at the sign of the constant
term if it is zero. The corresponding functionality is programmed into
the sign function of our polynomial ring number type
|Polynomial<NT>|. Thus adding the following methods to the extended
geometry traits class implements the functionality via the kernel base
class.
@c
int compare_xy(const Point_2& p1, const Point_2& p2) const
{ typename Base::Compare_xy_2 _compare_xy = compare_xy_2_object();
return _compare_xy(p1,p2);
}
int orientation(const Point_2& p1, const Point_2& p2, const Point_2& p3)
{ typename Base::Orientation_2 _orientation = orientation_2_object();
return _orientation(p1,p2,p3);
}
@ \subsubsection*{Extended constructions}
Algorithms in computational geometry can be grouped into three
categories: \emph{subset selection}, \emph{computation}, and
\emph{decision} \cite[1.4]{prep-sham:CG}. Algorithms of the first type
resort to predicates, algorithms of the second type construct
geometric objects. To cover this necessity software libraries like
LEDA or CGAL offer a set of so called constructions in their geometric
kernels. We have already shown that the intersection construction is
extendible to be used with extended segments.
First we want to present three examples how the standard algebraic
calculation of intersection points is blown up by common polynomial
factors.
The coefficients of a line $l$ through two points $p_1 = (x_1,y_1)$,
$p_2 = (x_2,y_2)$ are
\begin{equation} \label{linefrompoints}
a = y_1 - y_2 , b = x_2 - x_1, c = x_1 y_2 - x_2 y_1.
\end{equation}
The intersection point is defined by the common point of the two
underlying lines $l_i \equiv (a_i x + b_i y + c_i = 0), i=1,2$. Their
common point is then obtained by solving the linear system which has a
solution if the lines are not parallel. We obtain
\begin{equation}\label{intersectionoflines}
p_i = (b_1 c_2 - b_2 c_1, a_2 c_1 - a_1 c_2, a_1 b_2 - a_2 b_1 )
\end{equation}
in homogeneous representation. Apart from the formal argument why
these quotients contain common factors and how they can be simplified
to a minimal representation we give three examples.
\begin{description}
\item[two non-standard points on one frame segment] --- Look at the
case where the frame segment is the upper one. Thus $p_i = (m_i R +
n_i, R), i=1,2$. According to equation (\ref{linefrompoints}) we
obtain
\begin{eqnarray*}
a & = & R-R = 0 \\
b & = & (m_2 - m_1) R + (n_2 - n_1) \\
c & = & (m_1 - m_2) R^2 + (n_1-n_2) R = bR
\end{eqnarray*}
The common factor is $b$, the underlying line is $l \equiv by + c = 0
\gdw y - R = 0$. We obtain a simple parameterized version of a
horizontal line supporting the upper frame segment. The three other
cases are symmetric.
\item[two non-standard points spanning a standard line] --- Look at
the case of one point $p_1$ on the lower frame segment, $p_2$ on the
upper frame segment, both on a line $l \equiv ax + by + c = 0$ where
we assume orientation from $p_1$ to $p_2$. We get according to
Construction (\ref{pointsfromline}) $p_1 = (b/a\; R - c/a , -R)$, $p_2 =
(-b/a\; R -c/a , R)$. According to equation (\ref{linefrompoints}) we
obtain
\begin{eqnarray*}
a' & = & -2R \\
b' & = & -2R \; b/a \\
c' & = & (b/a - b/a) \; R^2 - 2R \; c/a = - 2R \; c/a
\end{eqnarray*}
The common factor is $-2R$. The underlying line is $l' \equiv a'x +
b'y + c' = 0 \gdw ax + by + c = 0$ as multiplying by $a$ and dividing
by $-2R$ does not change the line.
\item[one non-standard point and one standard point spanning a
standard ray] --- We look again at a line $l \equiv ax + by + c = 0$
supporting $p_2$ on the upper frame segment and a standard point $p_1$
on this line. We have $p_1 = (b/a\;y_0 - c/a , -y_0)$, $p_2 = (-b/a\;R
-c/a , R)$. According to equation (\ref{linefrompoints}) we obtain
\begin{eqnarray*}
a' & = & (y_0 - R) \\
b' & = & -b/a \; R - c/a + b/a \; y_0 + c/a = b/a \; (y_0 - R) \\
c' & = & -b/a \; y_0 R - c/a \; R + b/a \; y_0 R + c/a \;
y_0 = c/a \; (y_0 - R)
\end{eqnarray*}
The common factor is $(y_0 - R)$.
\end{description}
Note that the polynomial factors are very simple. The greatest common
divisor operation and the polynomial division scheme of the
|Polynomial| data type can be used to do the simplificication.
@c
void simplify(Point_2& p)
{ RT x=p.hx(), y=p.hy(), w=p.hw();
RT common = x.is_zero() ? y : gcd(x,y);
common = gcd(common,w);
p = Point_2(x/common,y/common,w/common);
}
@ Now the intersection uses the kernel operation and simplifies
the resulting point afterwards.
@c
Point_2 intersection(
const Segment_2& s1, const Segment_2& s2)
{ typename Base::Intersect_2 _intersect = intersect_2_object();
typename Base::Construct_line_2 _line = construct_line_2_object();
Point_2 p;
CGAL::Object result = _intersect(_line(s1),_line(s2));
if ( !CGAL::assign(p, result) )
CGAL_assertion_msg(false,"intersection: no intersection.");
simplify(p);
return p;
}
@ \begin{ignoreindiss}
We offer construction from standard affine kernel objects.
<<extended homogeneous kernel members>>=
public:
<<non-standard point construction>>
<<determining a lower bound for R>>
/*{\Moperations 2}*/
/*{\Mtext \headerline{Interfacing the affine kernel types}}*/
Point_2 construct_point(const Standard_point_2& p) const
/*{\Mop creates an extended point and initializes it to the
standard point |p|.}*/
{ return Point_2(p.hx(), p.hy(), p.hw()); }
Point_2 construct_point(const Standard_point_2& p1,
const Standard_point_2& p2,
Point_type& t) const
/*{\Xop creates an extended point and initializes it to the equivalence
class of all the rays underlying the oriented line |l(p1,p2)|.
|t| returns the type of the new extended point.}*/
{ return construct_point(Standard_line_2(p1,p2),t); }
Point_2 construct_point(const Standard_line_2& l) const
/*{\Mop creates an extended point and initializes it to the equivalence
class of all the rays underlying the oriented line |l|. }*/
{ Point_type dummy; return construct_point(l,dummy); }
Point_2 construct_point(const Standard_point_2& p1,
const Standard_point_2& p2) const
/*{\Mop creates an extended point and initializes it to the equivalence
class of all the rays underlying the oriented line |l(p1,p2)|.}*/
{ return construct_point(Standard_line_2(p1,p2)); }
Point_2 construct_point(const Standard_point_2& p,
const Standard_direction_2& d) const
/*{\Mop creates an extended point and initializes it to the equivalence
class of all the rays underlying the ray starting in |p| in direction |d|.}*/
{ return construct_point(Standard_line_2(p,d)); }
Point_2 construct_opposite_point(const Standard_line_2& l) const
/*{\Mop creates an extended point and initializes it to the equivalence
class of all the rays underlying the oriented line opposite to |l|. }*/
{ Point_type dummy; return construct_point(l.opposite(),dummy); }
Point_type type(const Point_2& p) const
/*{\Mop determines the type of |p| and returns it.}*/
{
CGAL_assertion(p.hx().degree()>=0 && p.hy().degree()>=0 );
CGAL_assertion(p.hw().degree()==0);
if (p.hx().degree() == 0 && p.hy().degree() == 0)
return STANDARD;
// now we are on the square frame
RT rx = p.hx();
RT ry = p.hy();
int sx = sign(rx);
int sy = sign(ry);
if (sx < 0) rx = -rx;
if (sy < 0) ry = -ry;
if (rx>ry) {
if (sx > 0) return RIGHTFRAME;
else return LEFTFRAME;
}
if (rx<ry) {
if (sy > 0) return TOPFRAME;
else return BOTTOMFRAME;
}
// now (rx == ry)
if (sx==sy) {
if (sx < 0) return SWCORNER;
else return NECORNER;
} else { CGAL_assertion(sx==-sy);
if (sx < 0) return NWCORNER;
else return SECORNER;
}
}
bool is_standard(const Point_2& p) const
/*{\Mop returns |true| iff |p| is a standard point.}*/
{ return (type(p)==STANDARD); }
Standard_point_2 standard_point(const Point_2& p) const
/*{\Mop returns the standard point represented by |p|.
\precond |\Mvar.is_standard(p)|.}*/
{ CGAL_assertion(type(p)==STANDARD);
CGAL_assertion(p.hw() > RT(0));
return Standard_point_2(p.hx()[0],p.hy()[0],p.hw()[0]);
}
Standard_line_2 standard_line(const Point_2& p) const
/*{\Mop returns the oriented line representing the
bundle of rays defining |p|.
\precond |!\Mvar.is_standard(p)|.}*/
{ CGAL_assertion(type(p)!=STANDARD);
RT hx = p.hx(), hy = p.hy(), hw = p.hw();
Standard_RT dx,dy;
if (hx.degree()>0) dx=hx[1]; else dx=0;
if (hy.degree()>0) dy=hy[1]; else dy=0;
Standard_point_2 p0(hx[0],hy[0],hw[0]);
Standard_point_2 p1(hx[0]+dx,hy[0]+dy,hw[0]);
return Standard_line_2(p0,p1);
}
Standard_ray_2 standard_ray(const Point_2& p) const
/*{\Mop a ray defining |p|. \precond |!\Mvar.is_standard(p)|.}*/
{ CGAL_assertion(type(p)!=STANDARD);
Standard_line_2 l = standard_line(p);
Standard_direction_2 d = l.direction();
Standard_point_2 q = l.point(0);
return Standard_ray_2(q,d);
}
Point_2 NE() const { return construct_point(Standard_line_2(-1, 1,0)); }
/*{\Mop returns the point on the northeast frame corner.}*/
Point_2 SE() const { return construct_point(Standard_line_2( 1, 1,0)); }
/*{\Mop returns the point on the southeast frame corner.}*/
Point_2 NW() const { return construct_point(Standard_line_2(-1,-1,0)); }
/*{\Mop returns the point on the northwest frame corner.}*/
Point_2 SW() const { return construct_point(Standard_line_2( 1,-1,0)); }
/*{\Mop returns the point on the southwest frame corner.}*/
Line_2 upper() const { return construct_line(NW(),NE()); }
/*{\Xop returns the line underlying the upper frame segment.}*/
Line_2 lower() const { return construct_line(SW(),SE()); }
/*{\Xop returns the line underlying the lower frame segment.}*/
Line_2 left() const { return construct_line(SW(),NW()); }
/*{\Xop returns the line underlying the left frame segment.}*/
Line_2 right() const { return construct_line(SE(),NE()); }
/*{\Xop returns the line underlying the right frame segment.}*/
/*{\Mtext \headerline{Geometric kernel calls}}*/
Point_2 source(const Segment_2& s) const
/*{\Mop returns the source point of |s|.}*/
{ typename Base::Construct_source_point_2 _source =
construct_source_point_2_object();
return _source(s); }
Point_2 target(const Segment_2& s) const
/*{\Mop returns the target point of |s|.}*/
{ typename Base::Construct_target_point_2 _target =
construct_target_point_2_object();
return _target(s); }
Segment_2 construct_segment(const Point_2& p, const Point_2& q) const
/*{\Mop constructs a segment |pq|.}*/
{ typename Base::Construct_segment_2 _segment =
construct_segment_2_object();
return _segment(p,q); }
void simplify(Point_2& p) const
/*{\Xop only used internally.}*/
{ TRACEN("simplify("<<p<<")");
RT x=p.hx(), y=p.hy(), w=p.hw();
RT common = x.is_zero() ? y : RT::gcd(x,y);
common = RT::gcd(common,w);
p = Point_2(x/common,y/common,w/common);
TRACEN("canceled="<<p);
}
Line_2 construct_line(const Standard_line_2& l) const
/*{\Xop only used internally.}*/
{ return Line_2(l.a(),l.b(),l.c()); }
Line_2 construct_line(const Point_2& p1, const Point_2& p2) const
/*{\Xop only used internally.}*/
{ Line_2 l(p1,p2);
TRACEN("eline("<<p1<<p2<<")="<<l);
RT a=l.a(), b=l.b(), c=l.c();
RT common = a.is_zero() ? b : RT::gcd(a,b);
common = RT::gcd(common,c);
l = Line_2(a/common,b/common,c/common);
TRACEN("canceled="<<l);
return l;
}
int orientation(const Segment_2& s, const Point_2& p) const
/*{\Mop returns the orientation of |p| with respect to the line
through |s|.}*/
{ typename Base::Orientation_2 _orientation =
orientation_2_object();
return static_cast<int> ( _orientation(source(s),target(s),p) );
}
int orientation(const Point_2& p1, const Point_2& p2, const Point_2& p3)
const
/*{\Mop returns the orientation of |p3| with respect to the line
through |p1p2|.}*/
{ typename Base::Orientation_2 _orientation =
orientation_2_object();
return static_cast<int> ( _orientation(p1,p2,p3) );
}
bool left_turn(const Point_2& p1, const Point_2& p2, const Point_2& p3)
const
/*{\Mop return true iff the |p3| is left of the line through |p1p2|.}*/
{ return orientation(p1,p2,p3) > 0; }
bool is_degenerate(const Segment_2& s) const
/*{\Mop return true iff |s| is degenerate.}*/
{ typename Base::Is_degenerate_2 _is_degenerate =
is_degenerate_2_object();
return _is_degenerate(s); }
int compare_xy(const Point_2& p1, const Point_2& p2) const
/*{\Mop returns the lexicographic order of |p1| and |p2|.}*/
{ typename Base::Compare_xy_2 _compare_xy =
compare_xy_2_object();
return static_cast<int>( _compare_xy(p1,p2) );
}
int compare_x(const Point_2& p1, const Point_2& p2) const
/*{\Mop returns the order on the $x$-coordinates of |p1| and |p2|.}*/
{ typename Base::Compare_x_2 _compare_x =
compare_x_2_object();
return static_cast<int>( _compare_x(p1,p2) );
}
int compare_y(const Point_2& p1, const Point_2& p2) const
/*{\Mop returns the order on the $y$-coordinates of |p1| and |p2|.}*/
{ typename Base::Compare_y_2 _compare_y =
compare_y_2_object();
return static_cast<int>( _compare_y(p1,p2) );
}
Point_2 intersection(
const Segment_2& s1, const Segment_2& s2) const
/*{\Mop returns the point of intersection of the lines supported by
|s1| and |s2|. \precond the intersection point exists.}*/
{ typename Base::Intersect_2 _intersect =
intersect_2_object();
typename Base::Construct_line_2 _line =
construct_line_2_object();
Point_2 p;
CGAL::Object result =
_intersect(_line(s1),_line(s2));
if ( !CGAL::assign(p, result) )
CGAL_assertion_msg(false,"intersection: no intersection.");
simplify(p);
return p;
}
Direction_2 construct_direction(
const Point_2& p1, const Point_2& p2) const
/*{\Mop returns the direction of the vector |p2| - |p1|.}*/
{ typename Base::Construct_direction_of_line_2 _direction =
construct_direction_of_line_2_object();
return _direction(construct_line(p1,p2)); }
bool strictly_ordered_ccw(const Direction_2& d1,
const Direction_2& d2, const Direction_2& d3) const
/*{\Mop returns |true| iff |d2| is in the interior of the
counterclockwise angular sector between |d1| and |d3|.}*/
{
if ( d1 < d2 ) return ( d2 < d3 )||( d3 <= d1 );
if ( d1 > d2 ) return ( d2 < d3 )&&( d3 <= d1 );
return false;
}
bool strictly_ordered_along_line(
const Point_2& p1, const Point_2& p2, const Point_2& p3) const
/*{\Mop returns |true| iff |p2| is in the relative interior of the
segment |p1p3|.}*/
{ typename Base::Are_strictly_ordered_along_line_2 _ordered =
are_strictly_ordered_along_line_2_object();
return _ordered(p1,p2,p3);
}
bool contains(const Segment_2& s, const Point_2& p) const
/*{\Mop returns true iff |s| contains |p|.}*/
{ typename Base::Has_on_2 _contains = has_on_2_object();
return _contains(s,p);
}
bool first_pair_closer_than_second(
const Point_2& p1, const Point_2& p2,
const Point_2& p3, const Point_2& p4) const
/*{\Mop returns true iff $\Labs{p1-p2} < \Labs{p3-p4}$.}*/
{ return ( squared_distance(p1,p2) < squared_distance(p3,p4) ); }
@ We can transform points, but have to be careful about their
representation. The method |transform| just applies the standard
matrix multiplication of planar affine transformations to our extended
points. Afterwards we scale the represenstation back to our square
box by the method |scale_first_by_second|.
Note that the correctness of the following piece of code is due to two
facts:
\begin{itemize}
\item the larger absolute values of the two tranformed components
determines the coordinate that can be scaled to the square frame.
\item any coordinate transformation $R \leftarrow mR+n$ is a legal
transformation of our point representation $(m_x R + n_x, m_y R +
n_y)$. One can easily show that both lie on the same line equation.
\end{itemize}
<<extended homogeneous kernel members>>=
void scale_first_by_second(RT& r1, RT& r2, RT& w) const
{ CGAL_assertion(w.degree()==0&&w!=RT(0)&& r2[1]!=Standard_RT(0));
Standard_RT w_res = w[0]*r2[1];
int sm2 = CGAL_NTS sign(r2[1]);
RT r2_res = RT(Standard_RT(0),sm2 * w_res);
RT r1_res = RT(r2[1]*r1[0]-r1[1]*r2[0], w[0]*r1[1]*sm2);
r1 = r1_res; r2 = r2_res; w = w_res;
}
Point_2 transform(const Point_2& p,
const Standard_aff_transformation_2& t) const
{
RT tpx = t.homogeneous(0,0)*p.hx() + t.homogeneous(0,1)*p.hy() +
t.homogeneous(0,2)*p.hw();
RT tpy = t.homogeneous(1,0)*p.hx() + t.homogeneous(1,1)*p.hy() +
t.homogeneous(1,2)*p.hw();
RT tpw = t.homogeneous(2,2)*p.hw();
if ( is_standard(p) ) {
Point_2 res(tpx,tpy,tpw); simplify(res);
return res;
}
RT tpxa = CGAL_NTS abs(tpx);
RT tpya = CGAL_NTS abs(tpy);
if ( tpxa > tpya ) {
scale_first_by_second(tpy,tpx,tpw);
} else { // tpxa <= tpya
scale_first_by_second(tpx,tpy,tpw);
}
Point_2 res(tpx,tpy,tpw); simplify(res);
return res;
}
const char* output_identifier() const { return "Extended_homogeneous"; }
/*{\Mop returns a unique identifier for kernel object input/output.}*/
@ The file wrapper is here.
<<Extended_homogeneous.h>>=
<<CGAL EH Header>>
#ifndef CGAL_EXTENDED_HOMOGENEOUS_H
#define CGAL_EXTENDED_HOMOGENEOUS_H
#include <CGAL/basic.h>
#include <CGAL/Homogeneous.h>
#include <CGAL/Point_2.h>
#include <CGAL/Line_2_Line_2_intersection.h>
#include <CGAL/squared_distance_2.h>
#ifndef _MSC_VER
#include <CGAL/Nef_2/Polynomial.h>
#else
#include <CGAL/Nef_2/Polynomial_MSC.h>
#define Polynomial Polynomial_MSC
#endif
#undef _DEBUG
#define _DEBUG 5
#include <CGAL/Nef_2/debug.h>
#include <CGAL/Nef_2/Line_to_epoint.h>
CGAL_BEGIN_NAMESPACE
template <class T> class Extended_homogeneous;
<<extended homogeneous>>
#undef Polynomial
CGAL_END_NAMESPACE
#endif // CGAL_EXTENDED_HOMOGENEOUS_H
@ \end{ignoreindiss}%
We provide a similar kernel based on a \emph{cartesian representation}
of points. In this case we use |Polynomial<NT>| fed with a field type
and use standard polynomial division for simplification in the
intersection construction. The latter replaces the |gcd| operation of
the ring type in the homogeneous case.
\begin{ignoreindiss}
\section{Cartesian Representation}
We obtain our epoint class by plugging our polynomial arithmetic type
into the standard Cartesian point type from CGAL.
<<extended cartesian>>=
/*{\Xanpage {Extended_cartesian}{}{An extended geometric kernel model}{K}}*/
template <class pFT>
class Extended_cartesian : public
CGAL::Cartesian< CGAL::Polynomial<pFT> > { public:
typedef CGAL::Cartesian< CGAL::Polynomial<pFT> > Base;
typedef Extended_cartesian<pFT> Self;
/*{\Xdefinition |\Mname| is a kernel model realizing the concept
extended geometry. }*/
/*{\Xtypes 6.5}*/
/*{\Xtext \headerline{Affine kernel and types}}*/
typedef CGAL::Cartesian<pFT> Standard_kernel;
/*{\Xtypemember the standard affine kernel.}*/
typedef typename Standard_kernel::RT Standard_RT;
/*{\Xtypemember the standard ring type.}*/
typedef typename Standard_kernel::FT Standard_FT;
/*{\Xtypemember the field type.}*/
typedef typename Standard_kernel::Point_2 Standard_point_2;
/*{\Xtypemember standard points.}*/
typedef typename Standard_kernel::Segment_2 Standard_segment_2;
/*{\Xtypemember standard segments.}*/
typedef typename Standard_kernel::Line_2 Standard_line_2;
/*{\Xtypemember standard oriented lines.}*/
typedef typename Standard_kernel::Direction_2 Standard_direction_2;
/*{\Xtypemember standard directions.}*/
typedef typename Standard_kernel::Ray_2 Standard_ray_2;
/*{\Xtypemember standard rays.}*/
typedef typename Standard_kernel::Aff_transformation_2
Standard_aff_transformation_2;
/*{\Xtypemember standard affine transformations.}*/
/*{\Xtext \headerline{Extended kernel types}}*/
typedef typename Base::RT RT;
/*{\Xtypemember the ring type of our extended kernel.}*/
typedef typename Base::FT FT;
/*{\Xtypemember the ring type of our extended kernel.}*/
typedef typename Base::Point_2 Point_2;
/*{\Xtypemember extended points.}*/
typedef typename Base::Segment_2 Segment_2;
/*{\Xtypemember extended segments.}*/
typedef typename Base::Line_2 Line_2;
/*{\Xtypemember extended lines.}*/
typedef typename Base::Direction_2 Direction_2;
/*{\Xtypemember extended directions.}*/
enum Point_type { SWCORNER=1, LEFTFRAME, NWCORNER,
BOTTOMFRAME, STANDARD, TOPFRAME,
SECORNER, RIGHTFRAME, NECORNER };
/*{\Xenum a type descriptor for extended points.}*/
<<extended cartesian kernel members>>
const char* output_identifier() const { return "Extended_cartesian"; }
};
@ We offer construction from a standard affine kernel objects.
<<extended cartesian kernel members>>=
Point_2 epoint(const Standard_FT& m1, const Standard_FT& n1,
const Standard_FT& m2, const Standard_FT& n2) const
{ return Point_2(FT(n1,m1),FT(n2,m2)); }
public:
/*{\Xoperations 2}*/
/*{\Xtext \headerline{Interfacing the affine kernel types}}*/
Point_2 construct_point(const Standard_point_2& p) const
/*{\Xop creates an extended point |Point_2| and initializes it to the
standard point |p|.}*/
{ return Point_2(p.x(), p.y()); }
Point_2 construct_point(const Standard_line_2& l, Point_type& t) const
/*{\Xop creates an extended point initialized to the equivalence
class of all the rays underlying the oriented line |l|.
|t| returns the type of the new extended point.}*/
{
t = (Point_type)Line_to_epoint<Standard_kernel>::determine_type(l);
Point_2 res;
switch (t) {
case SWCORNER: res = epoint(-1, 0, -1, 0); break;
case NWCORNER: res = epoint(-1, 0, 1, 0); break;
case SECORNER: res = epoint( 1, 0, -1, 0); break;
case NECORNER: res = epoint( 1, 0, 1, 0); break;
case LEFTFRAME:
res = epoint(-1, 0, l.a()/l.b(), -l.c()/l.b()); break;
case RIGHTFRAME:
res = epoint( 1, 0, -l.a()/l.b(), -l.c()/l.b()); break;
case BOTTOMFRAME:
res = epoint( l.b()/l.a(), -l.c()/l.a(), -1, 0); break;
case TOPFRAME:
res = epoint(-l.b()/l.a(), -l.c()/l.a(), 1, 0); break;
default: CGAL_assertion_msg(0,"EPoint type not correct!");
}
return res;
}
Point_2 construct_point(const Standard_point_2& p1,
const Standard_point_2& p2,
Point_type& t) const
/*{\Xop creates an extended point and initializes it to the equivalence
class of all the rays underlying the oriented line |l(p1,p2)|.
|t| returns the type of the new extended point.}*/
{ return construct_point(Standard_line_2(p1,p2),t); }
Point_2 construct_point(const Standard_line_2& l) const
/*{\Xop creates an extended point and initializes it to the equivalence
class of all the rays underlying the oriented line |l|. }*/
{ Point_type dummy; return construct_point(l,dummy); }
Point_2 construct_point(const Standard_point_2& p1,
const Standard_point_2& p2) const
/*{\Xop creates an extended point and initializes it to the equivalence
class of all the rays underlying the oriented line |l(p1,p2)|.}*/
{ return construct_point(Standard_line_2(p1,p2)); }
Point_2 construct_point(const Standard_point_2& p,
const Standard_direction_2& d) const
/*{\Xop creates an extended point and initializes it to the equivalence
class of all the rays underlying the ray starting in |p| in direction |d|.}*/
{ return construct_point(Standard_line_2(p,d)); }
Point_2 construct_opposite_point(const Standard_line_2& l) const
/*{\Xop creates an extended point and initializes it to the equivalence
class of all the rays underlying the oriented line opposite to |l|. }*/
{ Point_type dummy; return construct_point(l.opposite(),dummy); }
Point_type type(const Point_2& p) const
/*{\Xop determines the type of |p| and returns it.}*/
{
CGAL_assertion(p.x().degree()>=0 && p.y().degree()>=0 );
if ( p.x().degree() == 0 && p.y().degree() == 0)
return STANDARD;
// now we are on the square frame
FT rx = p.x();
FT ry = p.y();
int sx = sign(rx);
int sy = sign(ry);
if (sx < 0) rx = -rx;
if (sy < 0) ry = -ry;
if (rx>ry) {
if (sx > 0) return RIGHTFRAME;
else return LEFTFRAME;
}
if (rx<ry) {
if (sy > 0) return TOPFRAME;
else return BOTTOMFRAME;
}
// now (rx == ry)
if (sx==sy) {
if (sx < 0) return SWCORNER;
else return NECORNER;
} else { CGAL_assertion(sx==-sy);
if (sx < 0) return NWCORNER;
else return SECORNER;
}
}
bool is_standard(const Point_2& p) const
/*{\Xop returns |true| iff |p| is a standard point.}*/
{ return (type(p)==STANDARD); }
Standard_point_2 standard_point(const Point_2& p) const
/*{\Xop returns the standard point represented by |p|.
\precond |\Mvar.is_standard(p)|.}*/
{ CGAL_assertion( type(p)==STANDARD );
return Standard_point_2(p.x()[0],p.y()[0]);
}
Standard_line_2 standard_line(const Point_2& p) const
/*{\Xop returns the oriented line representing the
bundle of rays defining |p|.
\precond |!\Mvar.is_standard(p)|.}*/
{ CGAL_assertion( type(p)!=STANDARD );
FT x = p.x(), y = p.y();
Standard_FT dx = x.degree()>0 ? x[1] : Standard_FT(0);
Standard_FT dy = y.degree()>0 ? y[1] : Standard_FT(0);
Standard_point_2 p0(x[0],y[0]);
Standard_point_2 p1(x[0]+dx,y[0]+dy);
return Standard_line_2(p0,p1);
}
Standard_ray_2 standard_ray(const Point_2& p) const
/*{\Xop a ray defining |p|. \precond |!\Mvar.is_standard(p)|.}*/
{ Standard_line_2 l = standard_line(p);
Standard_direction_2 d = l.direction();
Standard_point_2 q = l.point(0);
return Standard_ray_2(q,d);
}
Point_2 NE() const { return construct_point(Standard_line_2(-1, 1,0)); }
/*{\Xop returns the point on the north east frame corner.}*/
Point_2 SE() const { return construct_point(Standard_line_2( 1, 1,0)); }
/*{\Xop returns the point on the south east frame corner.}*/
Point_2 NW() const { return construct_point(Standard_line_2(-1,-1,0)); }
/*{\Xop returns the point on the north west frame corner.}*/
Point_2 SW() const { return construct_point(Standard_line_2( 1,-1,0)); }
/*{\Xop returns the point on the south west frame corner.}*/
Line_2 upper() const { return construct_line(NW(),NE()); }
/*{\Xop returns the line underlying the upper frame segment.}*/
Line_2 lower() const { return construct_line(SW(),SE()); }
/*{\Xop returns the line underlying the lower frame segment.}*/
Line_2 left() const { return construct_line(SW(),NW()); }
/*{\Xop returns the line underlying the left frame segment.}*/
Line_2 right() const { return construct_line(SE(),NE()); }
/*{\Xop returns the line underlying the right frame segment.}*/
/*{\Xtext \headerline{Geometric kernel calls}}*/
Point_2 source(const Segment_2& s) const
/*{\Xop returns the source point of |s|.}*/
{ typename Base::Construct_source_point_2 _source =
construct_source_point_2_object();
return _source(s); }
Point_2 target(const Segment_2& s) const
/*{\Xop returns the target point of |s|.}*/
{ typename Base::Construct_target_point_2 _target =
construct_target_point_2_object();
return _target(s); }
Segment_2 construct_segment(const Point_2& p, const Point_2& q) const
/*{\Xop constructs a segment |pq|.}*/
{ typename Base::Construct_segment_2 _segment =
construct_segment_2_object();
return _segment(p,q); }
Line_2 construct_line(const Standard_line_2& l) const
/*{\Xop returns an extended line.}*/
{ return Line_2(l.a(),l.b(),l.c()); }
Line_2 construct_line(const Point_2& p1, const Point_2& p2) const
/*{\Xop returns a line through the two extended points |p1| and |p2|.}*/
{ Line_2 l(p1,p2);
TRACEN("eline("<<p1<<p2<<")="<<l);
RT a=l.a(), b=l.b(), c=l.c();
l = Line_2(a,b,c);
return l;
}
int orientation(const Segment_2& s, const Point_2& p) const
/*{\Xop returns the orientation of |p| with respect to the line
through |s|.}*/
{ typename Base::Orientation_2 _orientation =
orientation_2_object();
return static_cast<int> ( _orientation(source(s),target(s),p) );
}
int orientation(const Point_2& p1, const Point_2& p2, const Point_2& p3)
const
/*{\Xop returns the orientation of |p2| with respect to the line
through |p1p2|.}*/
{ typename Base::Orientation_2 _orientation =
orientation_2_object();
return static_cast<int> ( _orientation(p1,p2,p3) );
}
bool left_turn(const Point_2& p1, const Point_2& p2, const Point_2& p3)
const
/*{\Xop return true iff the |p3| is left of the line through |p1p2|.}*/
{ return orientation(p1,p2,p3) > 0; }
bool is_degenerate(const Segment_2& s) const
/*{\Xop return true iff |s| is degenerate.}*/
{ typename Base::Is_degenerate_2 _is_degenerate =
is_degenerate_2_object();
return _is_degenerate(s); }
int compare_xy(const Point_2& p1, const Point_2& p2) const
/*{\Xop returns the lexicographic order of |p1| and |p2|.}*/
{ typename Base::Compare_xy_2 _compare_xy =
compare_xy_2_object();
return static_cast<int>( _compare_xy(p1,p2) );
}
int compare_x(const Point_2& p1, const Point_2& p2) const
/*{\Xop returns the order on the $x$-coordinates of |p1| and |p2|.}*/
{ typename Base::Compare_x_2 _compare_x =
compare_x_2_object();
return static_cast<int>( _compare_x(p1,p2) );
}
int compare_y(const Point_2& p1, const Point_2& p2) const
/*{\Xop returns the order on the $y$-coordinates of |p1| and |p2|.}*/
{ typename Base::Compare_y_2 _compare_y =
compare_y_2_object();
return static_cast<int>( _compare_y(p1,p2) );
}
Point_2 intersection(
const Segment_2& s1, const Segment_2& s2) const
/*{\Xop returns the point of intersection of the lines supported by |s1|
and |s2|.}*/
{ typename Base::Intersect_2 _intersect =
intersect_2_object();
typename Base::Construct_line_2 _line =
construct_line_2_object();
Point_2 p;
CGAL::Object result =
_intersect(_line(s1),_line(s2));
if ( !CGAL::assign(p, result) )
CGAL_assertion_msg(false,"intersection: no intersection.");
return p;
}
Direction_2 construct_direction(
const Point_2& p1, const Point_2& p2) const
/*{\Xop returns the direction of the vector |p2| - |p1|.}*/
{ typename Base::Construct_direction_of_line_2 _direction =
construct_direction_of_line_2_object();
return _direction(construct_line(p1,p2)); }
bool strictly_ordered_ccw(const Direction_2& d1,
const Direction_2& d2, const Direction_2& d3) const
/*{\Xop returns |true| iff |d2| is in the interior of the
counterclockwise angular sector between |d1| and |d3|.}*/
{
if ( d1 < d2 ) return ( d2 < d3 )||( d3 <= d1 );
if ( d1 > d2 ) return ( d2 < d3 )&&( d3 <= d1 );
return false;
}
bool contains(const Segment_2& s, const Point_2& p) const
/*{\Xop returns true iff |s| contains |p|.}*/
{ typename Base::Has_on_2 _contains = has_on_2_object();
return _contains(s,p);
}
bool strictly_ordered_along_line(
const Point_2& p1, const Point_2& p2, const Point_2& p3) const
/*{\Xop returns |true| iff |p2| is in the relative interior of the
segment |p1p3|.}*/
{ typename Base::Are_strictly_ordered_along_line_2 _ordered =
are_strictly_ordered_along_line_2_object();
return _ordered(p1,p2,p3);
}
bool first_pair_closer_than_second(
const Point_2& p1, const Point_2& p2,
const Point_2& p3, const Point_2& p4) const
{ return ( squared_distance(p1,p2) < squared_distance(p3,p4) ); }
template <class Forward_iterator>
void determine_frame_radius(Forward_iterator start, Forward_iterator end,
Standard_RT& R0) const
{ Standard_RT R;
while ( start != end ) {
Point_2 p = *start++;
if ( is_standard(p) ) {
R = CGAL_NTS max(CGAL_NTS abs(p.x()[0]), CGAL_NTS abs(p.y()[0]));
} else {
RT rx = CGAL_NTS abs(p.x()), ry = CGAL_NTS abs(p.y());
if ( rx[1] > ry[1] ) R = CGAL_NTS abs(ry[0]-rx[0])/(rx[1]-ry[1]);
else if ( rx[1] < ry[1] ) R = CGAL_NTS abs(rx[0]-ry[0])/(ry[1]-rx[1]);
else /* rx[1] == ry[1] */ R = CGAL_NTS abs(rx[0]-ry[0])/2;
}
R0 = CGAL_NTS max(R+1,R0);
}
}
@ The file wrapper is here.
<<Extended_cartesian.h>>=
<<CGAL EC Header>>
#ifndef CGAL_EXTENDED_CARTESIAN_H
#define CGAL_EXTENDED_CARTESIAN_H
#include <CGAL/Cartesian.h>
#include <CGAL/Point_2.h>
#include <CGAL/Line_2_Line_2_intersection.h>
#ifndef _MSC_VER
#include <CGAL/Nef_2/Polynomial.h>
#else
#include <CGAL/Nef_2/Polynomial_MSC.h>
#define Polynomial Polynomial_MSC
#endif
#undef _DEBUG
#define _DEBUG 51
#include <CGAL/Nef_2/debug.h>
#include <CGAL/Nef_2/Line_to_epoint.h>
CGAL_BEGIN_NAMESPACE
template <class T> class Extended_cartesian;
<<extended cartesian>>
#undef Polynomial
CGAL_END_NAMESPACE
#endif // CGAL_EXTENDED_CARTESIAN_H
@ \section{A Test of Extended Points}
<<EPoint-test.C>>=
#define POLYNOMIAL_EXPLICIT_OUTPUT
#include <CGAL/Cartesian.h>
#include <CGAL/Extended_homogeneous.h>
#include <CGAL/Extended_cartesian.h>
#include <CGAL/Filtered_extended_homogeneous.h>
#include <CGAL/test_macros.h>
#ifdef CGAL_USE_LEDA
#include <CGAL/leda_integer.h>
typedef leda_integer Integer;
template <>
struct ring_or_field<leda_integer> {
typedef ring_with_gcd kind;
typedef leda_integer RT;
static RT gcd(const RT& r1, const RT& r2)
{ return ::gcd(r1,r2); }
};
#include <CGAL/leda_real.h>
typedef leda_real Real;
template <>
struct ring_or_field<leda_real> {
typedef field_with_div kind;
};
#else
#ifdef CGAL_USE_GMP
#include <CGAL/Gmpz.h>
#include <CGAL/Quotient.h>
typedef CGAL::Gmpz Integer;
template <>
struct ring_or_field<CGAL::Gmpz> {
typedef ring_with_gcd kind;
typedef CGAL::Gmpz RT;
static RT gcd(const RT& r1, const RT& r2)
{ return CGAL::gcd(r1,r2); }
};
typedef CGAL::Quotient<Integer> Real;
template <>
struct ring_or_field<Real> {
typedef field_with_div kind;
};
#else
typedef long Integer;
typedef double Real;
#endif
#endif
#include <CGAL/intersection_2.h>
using namespace CGAL;
int main()
{
SETDTHREAD(41);
CGAL_TEST_START;
{
typedef CGAL::Extended_homogeneous<Integer> EDec;
<<EDec test body>>
<<IO test>>
}
{
typedef CGAL::Extended_cartesian<Real> EDec;
<<EDec test body>>
//IO does not work for LEDA reals
}
{
typedef CGAL::Filtered_extended_homogeneous<Integer> EDec;
<<EDec test body>>
<<IO test>>
D.print_statistics();
}
CGAL_TEST_END;
}
<<EDec test body>>=
typedef EDec::Point_2 EP;
typedef EDec::Segment_2 ES;
typedef EDec::Direction_2 ED;
typedef EDec::Standard_kernel::Point_2 Point;
typedef EDec::Standard_kernel::Line_2 Line;
typedef EDec::Standard_RT RT;
EDec D;
CGAL::set_pretty_mode ( std::cerr );
Point ps1(0,0), ps2(1,1), ps3(1,0), ps4(0,1), ps5(1,1,2);
EDec::Point_type t1,t2,t3;
EP eps1 = D.construct_point(ps1);
EP eps2 = D.construct_point(ps2);
EP eps3 = D.construct_point(ps3);
EP eps4 = D.construct_point(ps4);
EP eps5 = D.construct_point(ps5);
EP epn1 = D.construct_point(ps4,ps1,t1); // vertical down ray
EP epn2 = D.construct_point(ps1,ps4,t2); // vertical up ray
EP epn3 = D.construct_point(ps1,ps3,t3); // horizontal right ray
EP epn4 = D.construct_point(Line(2,3,4));
ES el1 = D.construct_segment(D.construct_point(Line(ps1,ps4)),
D.construct_point(Line(ps4,ps1)));
ES el2 = D.construct_segment(D.NW(),D.NE());
CGAL_TEST(D.type(D.SW())==EDec::SWCORNER);
CGAL_TEST(D.type(D.NW())==EDec::NWCORNER);
CGAL_TEST(D.type(D.SE())==EDec::SECORNER);
CGAL_TEST(D.type(D.NE())==EDec::NECORNER);
CGAL_TEST(D.type(epn1)==EDec::BOTTOMFRAME);
CGAL_TEST(D.type(epn2)==EDec::TOPFRAME);
CGAL_TEST(D.type(epn3)==EDec::RIGHTFRAME);
CGAL_TEST(D.type(eps1)==EDec::STANDARD);
CGAL_TEST(D.standard_line(epn1) == Line(ps4,ps1));
CGAL_TEST(D.standard_point(eps1) == ps1);
ES es1 = D.construct_segment(epn1,epn2);
ES es2 = D.construct_segment(eps1,eps5);
CGAL_TEST(D.source(es1) == epn1);
CGAL_TEST(D.target(es1) == epn2);
CGAL_TEST(D.orientation(es1,D.construct_point(Point(-1,-2))) > 0 );
CGAL_TEST(D.is_degenerate(D.construct_segment(epn1,epn1)));
CGAL_TEST(D.compare_xy(eps1,eps5)<0);
CGAL_TEST(D.compare_xy(eps1,epn2)<0);
CGAL_TEST(D.compare_xy(epn1,eps1)<0);
CGAL_TEST(D.intersection(es1,es2) == eps1);
CGAL_TEST(D.compare_xy(eps1,eps2)<0);
CGAL_TEST(D.compare_xy(eps4,eps1)>0);
CGAL_TEST(D.compare_xy(eps1,eps1)==0);
CGAL_TEST(D.compare_xy(D.NW(),eps2)<0);
CGAL_TEST(D.compare_xy(eps1,D.NE())<0);
CGAL_TEST(D.compare_xy(D.SW(),D.SE())<0);
CGAL_TEST(D.compare_xy(epn1,eps1)<0);
CGAL_TEST(D.compare_xy(eps1,epn2)<0);
CGAL_TEST(D.orientation(eps1,eps2,eps3)<0);
CGAL_TEST(D.orientation(eps1,eps3,eps2)>0);
CGAL_TEST(D.orientation(eps1,eps2,D.construct_point(Point(2,2)))==0);
CGAL_TEST(D.orientation(eps1,eps2,D.construct_point(ps1,ps2))==0);
CGAL_TEST(D.orientation(eps1,eps2,epn3)<0);
CGAL_TEST(D.orientation(eps1,eps2,epn2)>0);
CGAL_TEST(D.orientation(D.NW(),D.NE(),eps1)<0);
CGAL_TEST(D.orientation(D.NE(),D.NW(),eps1)>0);
CGAL_TEST(D.orientation(D.SW(),D.NE(),eps1)==0);
CGAL_TEST(D.orientation(epn1,epn2,eps1)==0);
CGAL_TEST(D.orientation(epn1,epn2,eps4)==0);
CGAL_TEST(D.orientation(epn1,epn2,eps3)<0);
CGAL_TEST(D.orientation(epn2,epn1,eps3)>0);
CGAL_TEST(D.first_pair_closer_than_second(eps1,eps5,eps1,eps2));
CGAL_TEST(!D.first_pair_closer_than_second(eps1,eps3,eps1,eps4));
CGAL_TEST(D.first_pair_closer_than_second(eps1,eps3,eps2,
D.construct_point(Point(2,2,1))));
CGAL_TEST(D.first_pair_closer_than_second(eps1,eps3,eps2,D.NW()));
CGAL_TEST(D.first_pair_closer_than_second(eps1,D.SE(),D.SW(),D.NE()));
CGAL_TEST(!D.first_pair_closer_than_second(eps1,D.SE(),eps5,D.NE()));
CGAL_TEST(D.first_pair_closer_than_second(eps5,D.NE(),eps1,D.SE()));
CGAL_TEST(!D.first_pair_closer_than_second(eps1,D.SE(),eps1,D.NE()));
CGAL_TEST(D.first_pair_closer_than_second(D.SE(),D.NE(),D.NE(),D.SW()));
CGAL_TEST(!D.first_pair_closer_than_second(D.SE(),D.NE(),D.NW(),D.SW()));
CGAL_TEST(D.construct_direction(D.NW(),D.NE())==ED(RT(1),RT(0)));
CGAL_TEST(D.construct_direction(D.NE(),D.NW())==ED(RT(-1),RT(0)));
CGAL_TEST(D.construct_direction(D.SW(),D.NE())==ED(RT(1),RT(1)));
CGAL_TEST(D.construct_direction(D.NW(),D.SE())==ED(RT(1),RT(-1)));
CGAL_TEST(D.construct_direction(D.NW(),D.SW())==ED(RT(0),RT(-1)));
CGAL_TEST(D.construct_direction(D.SW(),D.NW())==ED(RT(0),RT(1)));
CGAL_TEST(D.construct_direction(eps5,D.NE())==ED(RT(1),RT(1)));
CGAL_TEST(D.construct_direction(eps5,D.SW())==ED(RT(-1),RT(-1)));
ES upper = D.construct_segment(D.NW(),D.NE());
ES left = D.construct_segment(D.SW(),D.NW());
EP ep_res = D.intersection(el1,upper);
CGAL_TEST(ep_res==epn2);
ep_res = D.intersection(left, upper);
CGAL_TEST(ep_res == D.NW());
<<IO test>>=
CGAL_IO_TEST(eps3,eps1);
CGAL_IO_TEST(epn2,epn1);
@ \section{A Demo of the EPoint concept}
<<EPoint-demo.C>>=
#define RPOLYNOMIAL_EXPLICIT_OUTPUT
#include <CGAL/Extended_homogeneous.h>
#include <CGAL/leda_integer.h>
#include <CGAL/test_macros.h>
template <>
struct ring_or_field<leda_integer> {
typedef ring_with_gcd kind;
};
typedef CGAL::Extended_homogeneous<leda_integer> EDec;
typedef EDec::Standard_point_2 Standard_point;
typedef EDec::Standard_line_2 Standard_line;
typedef EDec::Point_2 Point;
typedef EDec::Standard_aff_transformation_2 Atra;
EDec D;
int main() {
CGAL::set_pretty_mode ( std::cout );
CGAL::set_pretty_mode ( std::cerr );
Standard_point p1(0,0),p2(2,1),p3(1,4);
Standard_line l(p1,p2);
Point ep1 = D.construct_point(l);
Point ep2 = D.construct_point(p2,p3);
Point ep3 = D.construct_point(p2);
TRACEV(ep1); TRACEV(ep2); TRACEV(ep3);
Atra T(CGAL::ROTATION,1,0);
Point ep10 = D.transform(ep1,T);
Point ep20 = D.transform(ep2,T);
Point ep30 = D.transform(ep3,T);
TRACEV(ep10); TRACEV(ep20); TRACEV(ep30);
}
@ \begin{ignore}
<<CGAL EH Header>>=
// ============================================================================
//
// Copyright (c) 1997-2000 The CGAL Consortium
//
// This software and related documentation is part of an INTERNAL release
// of the Computational Geometry Algorithms Library (CGAL). It is not
// intended for general use.
//
// ----------------------------------------------------------------------------
//
// release : $CGAL_Revision$
// release_date : $CGAL_Date$
//
// file : include/CGAL/Extended_homogeneous.h
// package : Nef_2
// chapter : Nef Polyhedra
//
// source : nef_2d/Simple_extended_kernel.lw
// revision : $Id$
// revision_date : $Date$
//
// author(s) : Michael Seel <seel@mpi-sb.mpg.de>
// maintainer : Michael Seel <seel@mpi-sb.mpg.de>
// coordinator : Michael Seel <seel@mpi-sb.mpg.de>
//
// implementation: Extended homogeneous kernel
// ============================================================================
@
<<CGAL EC Header>>=
// ============================================================================
//
// Copyright (c) 1997-2000 The CGAL Consortium
//
// This software and related documentation is part of an INTERNAL release
// of the Computational Geometry Algorithms Library (CGAL). It is not
// intended for general use.
//
// ----------------------------------------------------------------------------
//
// release : $CGAL_Revision$
// release_date : $CGAL_Date$
//
// file : include/CGAL/Extended_cartesian.h
// package : Nef_2
// chapter : Nef Polyhedra
//
// source : nef_2d/Simple_extended_kernel.lw
// revision : $Id$
// revision_date : $Date$
//
// author(s) : Michael Seel <seel@mpi-sb.mpg.de>
// maintainer : Michael Seel <seel@mpi-sb.mpg.de>
// coordinator : Michael Seel <seel@mpi-sb.mpg.de>
//
// implementation: Extended cartesian kernel
// ============================================================================
<<CGAL L2E Header>>=
// ============================================================================
//
// Copyright (c) 1997-2000 The CGAL Consortium
//
// This software and related documentation is part of an INTERNAL release
// of the Computational Geometry Algorithms Library (CGAL). It is not
// intended for general use.
//
// ----------------------------------------------------------------------------
//
// release : $CGAL_Revision$
// release_date : $CGAL_Date$
//
// file : include/CGAL/Nef_2/Line_to_epoint.h
// package : Nef_2
// chapter : Nef Polyhedra
//
// source : nef_2d/Simple_extended_kernel.lw
// revision : $Id$
// revision_date : $Date$
//
// author(s) : Michael Seel <seel@mpi-sb.mpg.de>
// maintainer : Michael Seel <seel@mpi-sb.mpg.de>
// coordinator : Michael Seel <seel@mpi-sb.mpg.de>
//
// implementation: Simple converter
// ============================================================================
@ \end{ignore}
\end{ignoreindiss}
%KILLSTART DISS REP
\bibliographystyle{alpha}
\bibliography{comp_geo,general,diss}
\newpage
\section{Appendix}
\input manpages/ExtendedKernelTraits_2.man
\end{document}
%KILLEND DISS REP