cgal/Box_intersection_d/doc_tex/Box_intersection_d/main.tex

546 lines
26 KiB
TeX

% +------------------------------------------------------------------------+
% | CGAL Reference Manual: Box_intersection_d/main.tex
% +------------------------------------------------------------------------+
% | Fast iso-box intersections
% |
% | 2003, 2004 Lutz Kettner, Andreas Meyer, Afra Zomorodian
% |
\RCSdef{\BoxIntersectionRev}{$Id$}
\RCSdefDate{\BoxIntersectionDate}{$Date$}
% +------------------------------------------------------------------------+
\ccParDims
\ccUserChapter{Intersecting Sequences of dD Iso-oriented Boxes\label{chapterBoxIntersection}}
\ccChapterRelease{\BoxIntersectionRev. \ \BoxIntersectionDate}
\ccChapterAuthor{Lutz Kettner \and Andreas Meyer \and Afra Zomorodian}
\input{Box_intersection_d/PkgDescription.tex}
\begin{ccTexOnly}
\setlength{\unitlength}{1mm}
\begin{picture}(0,0)(0.0,0.0)
\put (90,20){
\includegraphics[width=70mm]{Box_intersection_d/fig/box_inters}
}
\end{picture}\vspace{-4mm}% compensate for some vspace added by picture
\end{ccTexOnly}
\minitoc
% +------------------------------------------------------------------------+
\section{Introduction}
Simple questions on geometric primitives, such as intersection and
distance computations, can themselves become quite expensive if the
primitives are not so simple anymore, for example, three-dimensional
triangles and facets of polyhedral surfaces. Thus algorithms operating
on these primitives tend to be slow in practice. A common (heuristic)
optimization approximates the geometric primitives with their
axis-aligned bounding boxes, runs a suitable modification of the
algorithm on the boxes, and whenever a pair of boxes has an
interesting interaction\footnote{Boxes represent volumes or point-sets. So, \textit{intersection} means intersection of the point-set enclosed by the box and not only intersection of the boundary, of course.}, only then the exact answer is computed on the
complicated geometric primitives contained in the boxes.
\begin{ccHtmlOnly}
<CENTER>
<img src="./fig/box_inters.gif" alt="Two intersecting curves with
approximating boxes."><P>
</CENTER>
\end{ccHtmlOnly}
We provide an efficient algorithm~\cite{cgal:ze-fsbi-02} for finding all
intersecting pairs for large numbers of iso-oriented boxes, i.e.,
typically these will be such bounding boxes of more complicated geometries.
One immediate application of this algorithm is the detection of all
intersections (and self-intersections) for polyhedral surfaces, i.e.,
applying the algorithm on a large set of triangles in space, we give
an example program later in this chapter. Not so obvious applications
are proximity queries and distance computations among such surfaces,
see Section~\ref{sec:box_inters_example_proximity} for an example
and~\cite{cgal:ze-fsbi-02} for more details.
% +------------------------------------------------------------------------+
\section{Definition\label{sec:box-inters-def}}
A $d$-dimensional iso-oriented box is defined as the
Cartesian product of $d$ intervals. We call the
box \emph{half-open} if the $d$ intervals $\{ [lo_i,hi_i) \,|\, 0 \leq
i < d\}$ are half-open intervals, and we call the box \emph{closed} if
the $d$ intervals $\{ [lo_i,hi_i] \,|\, 0 \leq i < d\}$ are closed
intervals. Note that closed boxes support zero-width boxes and they
can intersect at their boundaries, while non-empty half-open boxes
always have a positive volume and they only intersect iff their
interiors overlap. The distinction between closed and half-open boxes
does not require a different representation of boxes, just a different
interpretation when comparing boxes, which is selected with the two
possible values for the \ccc{topology} parameter:
\begin{itemize}
\item \ccc{CGAL::Box_intersection_d::HALF_OPEN} and
\item \ccc{CGAL::Box_intersection_d::CLOSED}.
\end{itemize}
The number type of the interval boundaries must be one of the built-in
types \texttt{int}, \texttt{unsigned int}, \texttt{double} or
\texttt{float}.
In addition, a box has an unique \ccc{id}-number. It is used to order
boxes consistently in each dimension even if boxes have identical
coordinates. In consequence, the algorithm guarantees that a pair of
intersecting boxes is reported only once. Note that boxes with equal
\ccc{id}-number are not reported since they obviously intersect trivially.
The box intersection algorithm comes in two flavors: One algorithm
works on a single sequence of boxes and computes all pairwise
intersections, which is called the \emph{complete\/} case, and used,
for example, in the self-intersection test. The other algorithm works
on two sequences of boxes and computes the pairwise intersections
between boxes from the first sequence with boxes from the second
sequence, which is called the \emph{bipartite\/} case. For each
pairwise intersection found a callback function is called with two
arguments; the first argument is a box from the first sequence and the
second argument a box from the second sequence. In the complete case,
the second argument is a box from an internal copy of the first
sequence.
% +------------------------------------------------------------------------+
\section{Software Design}
The box intersection algorithm is implemented as a family of generic
functions; the functions for the complete case accept one iterator
range, and the functions for the bipartite case accept two iterator
ranges. The callback function for reporting the intersecting pairs is
provided as a template parameter of the \ccc{BinaryFunction} concept.
The two principle function calls utilizing all default arguments look
as follows:
\ccInclude{CGAL/box_intersection_d.h}
\ccThree{void}{box_inter}{}
%\def\ccLongParamLayout{\ccTrue}
\ccGlobalFunction{template< class RandomAccessIterator, class Callback >
void box_intersection_d(
RandomAccessIterator begin, RandomAccessIterator end,
Callback callback);}
\ccGlobalFunction{template< class RandomAccessIterator1,
class RandomAccessIterator2,
class Callback >
void box_intersection_d(
RandomAccessIterator1 begin1, RandomAccessIterator1 end1,
RandomAccessIterator2 begin2, RandomAccessIterator2 end2,
Callback callback);}
Additional parameters to the functions calls are a \emph{cutoff\/}
value to adjust performance trade-offs, and a \emph{topology\/} parameter
selecting between topologically closed boxes (the default) and
topologically half-open boxes.
The algorithm reorders the boxes in the course of the algorithm. Now,
depending on the size of a box it can be faster to copy the boxes, or
to work with pointers to boxes and copy only pointers. We offer
automatic support for both options. To simplify the description, let us
call the \ccc{value_type} of the iterator ranges \emph{box handle}.
The \emph{box handle\/} can either be our box type itself or a
pointer (or const pointer) to the box type; these choices represent
both options from above.
In general, the algorithms treat the box type as opaque type and just
assume that they are models of the \ccc{Assignable} concept, so that
the algorithms can modify the input sequences and reorder the boxes.
The access to the box dimension and box coordinates is mediated with a
traits class of the \ccc{BoxIntersectionTraits_d} concept. A default
traits class is provided that assumes that the box type is a model of
the \ccc{BoxIntersectionBox_d} concept and that the box handle, i.e.,
the iterators value type, is identical to the box type or a pointer
to the box type (see the previous paragraph for the value versus
pointer nature of the box handle).
Two implementations of iso-oriented boxes are provided;
\ccc{CGAL::Box_intersection_d::Box_d} as a plain box, and
\ccc{CGAL::Box_intersection_d::Box_with_handle_d} as a box plus a
handle that can be used to point to the full geometry that is
approximated by the box. Both implementations have template parameters
for the number type used for the interval bounds, for the fixed
dimension of the box, and for a policy class~\cite{cgal:a-mcdgp-01}
selecting among several solutions for providing the \ccc{id}-number.
The function signatures for the bipartite case look as follows. The
signatures for the complete case with the \ccc{box_self_intersection_d}
function look the same except for the single iterator range.
\ccInclude{CGAL/box_intersection_d.h}
\ccThree{void}{box_inter}{}
%\def\ccLongParamLayout{\ccTrue}
\ccGlobalFunction{template< class RandomAccessIterator1,
class RandomAccessIterator2,
class Callback >
void box_intersection_d(
RandomAccessIterator1 begin1, RandomAccessIterator1 end1,
RandomAccessIterator2 begin2, RandomAccessIterator2 end2,
Callback callback,
std::ptrdiff_t cutoff = 10,
Box_intersection_d::Topology topology = Box_intersection_d::CLOSED,
Box_intersection_d::Setting setting = Box_intersection_d::BIPARTITE);}
\ccGlobalFunction{template< class RandomAccessIterator1,
class RandomAccessIterator2,
class Callback, class BoxTraits >
void box_intersection_d(
RandomAccessIterator1 begin1, RandomAccessIterator1 end1,
RandomAccessIterator2 begin2, RandomAccessIterator2 end2,
Callback callback,
BoxTraits box_traits,
std::ptrdiff cutoff = 10,
Box_intersection_d::Topology topology = Box_intersection_d::CLOSED,
Box_intersection_d::Setting setting = Box_intersection_d::BIPARTITE);}
%\begin{ccAdvanced}
% A more detailed interface is provided with the box predicate traits
% class. It implements the various predicates used in the box
% intersection algorithm. It is exchangeable and can be used by
% experts to fine tune the comparisons performed in this algorithm,
% e.g. utilizing machine instruction sets, see the
% \ccc{BoxIntersectionPredicateTraits_d} concept in the reference
% manual section for more details. Note that the generic default model
% provided is fully functional and efficient in all cases, thus
% usually there is no need to look at this predicate traits.
%\end{ccAdvanced}
% +------------------------------------------------------------------------+
\section{Minimal Example for Intersecting Boxes\label{sec:box-intersect-minimal}}
The box implementation provided with
\ccc{CGAL::Box_intersection_d::Box_d<double,2>} has a dedicated
constructor for the \cgal\ bounding box type \ccc{CGAL::Bbox_2}
(similar for dimension 3). We use this in our minimal example to
create easily nine two-dimensional \ccc{boxes} in a grid layout of $3
\times 3$ boxes. Additionally we pick the center box and the box in
the upper-right corner as our second box sequence \ccc{query}.
The default policy of the box type implements the \ccc{id}-number with
an explicit counter in the boxes, which is the default choice since it
always works, but it costs space that could potentially be avoided,
see the example in the next section. We use the \ccc{id}-number in our
callback function to report the result of the intersection algorithm.
The result will be that the first \ccc{query} box intersects all nine
\ccc{boxes} and the second \ccc{query} box intersects the four boxes
in the upper-right quadrant. See Section~\ref{sec:box-inters-params}
for the change of the \ccc{topology} parameter and its effect.
\ccIncludeExampleCode{Box_intersection_d/minimal.cpp}
% +------------------------------------------------------------------------+
\section{Example for Finding Intersecting 3D Triangles}
The conventional application of the axis-aligned box intersection
algorithm will start from complex geometry, here 3D triangles,
approximate them with their bounding box, compute the intersecting
pairs of boxes, and check only for those if the original triangles
intersect as well.
We start in the \ccc{main} function and create ten triangles with
endpoints chosen randomly in a cube $[-1,+1)^3$. We store the
triangles in a vector called \ccc{triangles}.
Next we create a vector for the bounding boxes of the triangles called
\ccc{boxes}. For the boxes we choose the type
\ccc{Box_with_handle_d<double,3,Iterator>} that works nicely together
with the \cgal\ bounding boxes of type \ccc{CGAL::Bbox_3}. In
addition, each box stores the iterator to the corresponding triangle.
The default policy of this box type uses for the \ccc{id}-number the
address of the value of the iterator, i.e., the address of the
triangle. This is a good choice that works correctly iff the boxes
have unique iterators, i.e., there is a one-to-one mapping between
boxes and approximated geometry, which is the case here. It saves us
the extra space that was needed for the explicit \ccc{id}-number in
the previous example.
We run the self intersection algorithm with the \ccc{report_inters}
function as callback. This callback reports the intersecting boxes. It
uses the \ccc{handle} and the global \ccc{triangles} vector to
calculate the triangle numbers. Then it checks the triangles
themselves for intersection and reports if not only the boxes but also
the triangles intersect. We take some precautions before the
intersection test in order to avoid problems, although unlikely, with
degenerate triangles that we might have created with the random
process.
This example can be easily extended to test polyhedral surfaces of the
\ccc{Polyhedron_3} class for (self-) intersections. The main
difference are the numerous cases of incidences between triangles in
the polyhedral surface that should not be reported as intersections,
see the \texttt{examples/Polyhedron/polyhedron\_self\_intersection.cpp}
example program in the \cgal\ distribution.
\ccIncludeExampleCode{Box_intersection_d/triangle_self_intersect.cpp}
% +------------------------------------------------------------------------+
\section{Example for Using Pointers to Boxes}
We modify the previous example, finding intersecting 3D triangles,
and add an additional vector \ccc{ptr} that stores pointers to the bounding
boxes, so that the intersection algorithm will work on a sequence of
pointers and not on a sequence of boxes. The change just affects the
preparation of the additional vector and the call of the box intersection
function. The box intersection function (actually its default traits
class) detects automatically that the value type of the iterators is a
pointer type and not a class type.
\begin{ccExampleCode}
// Create the corresponding vector of pointers to bounding boxes
std::vector<Box *> ptr;
for ( std::vector<Box>::iterator i = boxes.begin(); i != boxes.end(); ++i)
ptr.push_back( &*i);
// Run the self intersection algorithm with all defaults on the
// indirect pointers to bounding boxes. Avoids copying the boxes.
CGAL::box_self_intersection_d( ptr.begin(), ptr.end(), report_inters);
\end{ccExampleCode}
In addition, the callback function \ccc{report_inters} needs to be
changed to work with pointers to boxes. The full example program looks
as follows:
\ccIncludeExampleCode{Box_intersection_d/triangle_self_intersect_pointers.cpp}
A note on performance: The algorithm sorts and partitions the input
sequences. It is clearly costly to copy a large box compared to a
simple pointer. However, the algorithm benefits from memory locality
in the later stages when it copies the boxes, while the pointers would
refer to boxes that become wildly scattered in memory. These two
effects, copying costs and memory locality, counteract each other. For
small box sizes, i.e., small dimension, memory locality wins and one
should work with boxes, while for larger box sizes one should work
with pointers. The exact threshold depends on the memory hierarchy
(caching) of the hardware platform and the size of the boxes, most
notably the type used to represent the box coordinates. A concrete
example; on a laptop with an Intel Mobile Pentium4 running at 1.80GHz
with 512KB cache and 254MB main memory under Linux this version with
pointers was 20\% faster than the version above that copies the boxes
for 10000 boxes, but the picture reversed for 100000 boxes, where the
version above that copies the boxes becomes 300\% faster.
Note that switching to the built-in type \ccc{float} is supported by
the box intersection algorithm, but the interfacing with the \cgal\
bounding box \ccc{CGAL::Bbox_3} would not be that easy. In particular,
just converting from the \ccc{double} to the \ccc{float}
representation incurs rounding that needs to be controlled properly,
otherwise the box might shrink and one might miss intersections.
% +------------------------------------------------------------------------+
\section{Example Using the \textit{topology} and the \textit{cutoff}
Parameters\label{sec:box-inters-params}}
Boxes can be interpreted by the box intersection algorithm as closed
or as half-open boxes, see also Section~\ref{sec:box-inters-def}. Closed
boxes support zero-width boxes and they can intersect at their
boundaries, while half-open boxes always have a positive volume and
they only intersect iff their interiors overlap. The choice between
closed or half-open boxes is selected with the \ccc{topology}
parameter and its two values:
\begin{itemize}
\item \ccc{CGAL::Box_intersection_d::HALF_OPEN} and
\item \ccc{CGAL::Box_intersection_d::CLOSED}.
\end{itemize}
The example program uses a two-dimensional box with \ccc{int}
coordinates and \ccc{id}-numbers that are by default explicitly
stored. We create the same boxes as in the minimal example in
Section~\ref{sec:box-intersect-minimal}. We create a $3 \times 3$ grid
of \ccc{boxes}, and two boxes for the \ccc{query} sequence, namely the
box at the center and the box from the upper-right corner of the grid.
We write a more involved callback function object \ccc{Report} that
stores an output iterator and writes the \ccc{id}-number of the
box in the first argument to the output iterator. We also provide a
small helper function \ccc{report} that simplifies the use of the function
object.
We call the box intersection algorithm twice; once for the default
\ccc{topology}, which is the closed box topology, and once for the
half-open box topology. We sort the resulting output for better
readability and verify its correctness with the \ccc{check1} and
\ccc{check2} data. For the closed box topology, the center box in
\ccc{query} intersects all \ccc{boxes}, and the upper-right box in
\ccc{query} intersects the four boxes of the upper-right quadrant in
\ccc{boxes}. Almost all intersections are with the box boundaries,
thus, for the half-open topology only one intersection remains per
\ccc{query} box, namely its corresponding box in \ccc{boxes}. So, the
output of the algorithm will be:
\begin{verbatim}
0 1 2 3 4 4 5 5 6 7 7 8 8
4 8
\end{verbatim}
For the second box intersection function call we have to specify the
\ccc{cutoff} parameter explicitly. See the
Section~\ref{sec:box-inters-performance} below for a detailed
discussion.
\ccIncludeExampleCode{Box_intersection_d/box_grid.cpp}
% +------------------------------------------------------------------------+
\section{Runtime Performance\label{sec:box-inters-performance}}
The implemented algorithm is described in~\cite{cgal:ze-fsbi-02} as
version two. Its performance depends on a \ccc{cutoff} parameter.
When the size of both iterator ranges drops below the \ccc{cutoff}
parameter the function switches from the streamed segment-tree
algorithm to the two-way-scan algorithm, see~\cite{cgal:ze-fsbi-02}
for the details.
The streamed segment-tree algorithm needs $O(n \log^d (n) + k)$
worst-case running time and $O(n)$ space, where $n$ is the number of
boxes in both input sequences, $d$ the (constant) dimension of the
boxes, and $k$ the output complexity, i.e., the number of pairwise
intersections of the boxes. The two-way-scan algorithm needs $O(n \log
(n) + l)$ worst-case running time and $O(n)$ space, where $l$ is the
number of pairwise overlapping intervals in one dimensions (the
dimension where the algorithm is used instead of the segment tree).
Note that $l$ is not necessarily related to $k$ and using the
two-way-scan algorithm is a heuristic.
Unfortunately, we have no general method to automatically determine an
optimal cutoff parameter, since it depends on the used hardware, the
runtime ratio between callback runtime and segment-tree runtime, and
of course the number of boxes to be checked and their distribution. In
cases where the callback runtime is dominant, it may be best to make
the threshold parameter small. Otherwise a \ccc{cutoff}$=\sqrt{n}$ can
lead to acceptable results. For well distributed boxes the original
paper~\cite{cgal:ze-fsbi-02} gives optimal cutoffs in the thousands.
Anyway, for optimal runtime some experiments to compare different
cutoff parameters are recommended.
To demonstrate that box intersection can be done quite fast, different
box sequences are intersected in the range between 4 and 800000 boxes
total. We use three-dimensional default boxes of closed topology with
\ccc{float} coordinates and without additional data fields. The
algorithm works directly on the boxes, not on pointer to boxes. Each
box intersection is reported to an empty dummy callback.
For each box set, a near-optimal cutoff parameter is determined using
an adaptive approximation. The runtime required for streaming is
compared against usual scanning. Results on a Xeon 2.4GHz with 4GB
main memory can be seen in Figure \ref{fig_benchmark}. For a small
number of boxes, pure scanning is still faster than streaming with
optimal cutoff, which would just delegate the box sets to the scanning
algorithm. As there are more and more boxes, the overhead becomes less
important.
\begin{figure}[htbp]
\begin{ccTexOnly}
\begin{center}
\includegraphics[width=0.7\textwidth]{Box_intersection_d/fig/benchmark}
\end{center}
\end{ccTexOnly}
\begin{ccHtmlOnly}
<center>
\end{ccHtmlOnly}
\begin{ccHtmlOnly}
<img border="0" src="./fig/benchmark.gif" align="center" alt="benchmark plot">
</center>
\end{ccHtmlOnly}
\caption{Runtime comparison between the scanning and the streaming algorithm.
\label{fig_benchmark}}
\end{figure}
% +------------------------------------------------------------------------+
\section{Example Using a Custom Box Implementation}
The example in the previous Section~\ref{sec:box-inters-params} uses
an array to provide the coordinates and then creates another array for
the boxes. In the following example we write our own box class
\ccc{Box} that we can initialize directly with the four coordinates and
create the array of boxes directly. We also omit the explicitly stored
\ccc{id}-number and use the address of the box itself as
\ccc{id}-number. This works only if the boxes do not change their
position, i.e., we work with pointers to the boxes in the intersection
algorithm.
We follow with our own box class \ccc{Box} the
\ccc{BoxIntersectionBox_d} concept, which allows us to reuse the
default traits implementation, i.e., we can use the same default
function call to compute all intersections. See the example in the
next section for a self-written traits class. So, in principle, the
remainder of the example stays the same and we omit the part from the
previous example for brevity that illustrates the half-open box topology.
The requirements for the box implementation are best studied on
page~\pageref{ccRef_BoxIntersectionBox_d} in the Reference Manual. In a
nutshell, we have to define the type \ccc{NT} for the box coordinates
and the type \ccc{ID} for the \ccc{id}-number. Member functions
give access to the coordinates and the \ccc{id}-number. A static
member function returns the dimension.
\ccIncludeExampleCode{Box_intersection_d/custom_box_grid.cpp}
% +------------------------------------------------------------------------+
\section{Example for Point Proximity Search with a Custom Traits Class\label{sec:box_inters_example_proximity}}
Given a set of 3D points, we want to find all pairs of points that are
less than a certain distance apart. We use the box intersection
algorithm to find good candidates, namely those that are less than
this specified distance apart in the $L_\infty$ norm, which is a good
approximation of the Euclidean norm.
We use an unusual representation for the box, namely pointers to the 3D points
themselves. We implement a special box traits class that interprets
the point as a box of the dimensions $[-$\ccc{eps}$,+$\ccc{eps}$]^3$
centered at this point. The value for \ccc{eps} is half the specified distance
from above, i.e., points are reported if their distance is smaller
than \ccc{2*eps}.
The requirements for the box traits class are best studied on
page~\pageref{ccRef_BoxIntersectionTraits_d} in the Reference Manual. In a
nutshell, we have to define the type \ccc{NT} for the box coordinates,
the type \ccc{ID} for the \ccc{id}-number, and the type \ccc{Box_parameter}
similar to the box handle, here \ccc{Point_3*} since we work with the pointers.
All member functions in the traits class are static. Two functions give
access to the max and min coordinates that we compute from the point
coordinates plus or minus the \ccc{eps} value, respectively. For the
\ccc{id}-number function the address of the point itself is
sufficient, since the points stay stable. Another function
returns the dimension.
The \ccc{report} callback function computes than the Euclidean
distance and prints a message for points that are close enough.
Note that we need to reserve sufficient space in the \ccc{points}
vector to avoid reallocations while we create the \ccc{points} vector
and the \ccc{boxes} vector in parallel, since otherwise the
\ccc{points} vector might reallocate and invalidate all pointers
stored in the \ccc{boxes} so far.
\ccIncludeExampleCode{Box_intersection_d/proximity_custom_box_traits.cpp}
% +------------------------------------------------------------------------+
\section{Design and Implementation History}
Lutz Kettner and Andreas Meyer implemented the algorithms starting
from the publication~\cite{cgal:ze-fsbi-02}. We had access to the
original C implementation of Afra Zomorodian, which helped clarifying
some questions, and we are grateful to the help of Afra Zomorodian in
answering our questions during his visit. We thank Steve Robbins for
an excellent review for this package. Steve Robbins provided an
independent and earlier implementation of this algorithm,
however, we learned too late about this implementation.
%% EOF %%