mirror of https://github.com/CGAL/cgal
3539 lines
140 KiB
Plaintext
3539 lines
140 KiB
Plaintext
%------------------------------------------------------------------------------
|
|
%KILLSTART DISS REP
|
|
%LDEL TRACE.*?\)\;
|
|
%LDEL CGAL_assertion\(.*?\)\;
|
|
\documentclass[a4paper]{article}
|
|
\usepackage{MyLweb,version}
|
|
\input{defs}
|
|
|
|
\excludeversion{ignoreindiss}
|
|
\excludeversion{ignore}
|
|
\includeversion{onlyindiss}
|
|
|
|
|
|
\begin{document}
|
|
\title{Nef Polyhedra in the Plane}
|
|
\author{Michael Seel}
|
|
\date{\today}
|
|
\maketitle
|
|
|
|
\begin{abstract}
|
|
A planar Nef polyhedron is any set that can be obtained from the open
|
|
half\-spaces by a finite number of set complement and set intersection
|
|
operations. The set of Nef polyhedra is closed under the Boolean set
|
|
operations. We describe a data structure that realizes two-dimensional
|
|
Nef polyhedra and offers a large set of binary and unary set
|
|
operations. The underlying set operations are realized by an efficient
|
|
and complete algorithm for the overlay of two Nef polyhedra. The
|
|
algorithm is efficient in the sense that its running time is bounded
|
|
by the size of the inputs plus the size of the output times a
|
|
logarithmic factor. The algorithm is complete in the sense that it can
|
|
handle all inputs and requires no general position assumption.
|
|
\end{abstract}
|
|
|
|
\tableofcontents
|
|
\newpage
|
|
%KILLEND REP
|
|
\section{The Interface Specification}
|
|
|
|
\input manpages/Nef_polyhedron_2.man
|
|
\input manpages/Topological_explorer.man
|
|
\input manpages/Explorer.man
|
|
|
|
%KILLEND DISS
|
|
|
|
\section{Motivation}
|
|
|
|
Nef polyhedra are the most general model for rectilinearly bounded
|
|
subsets of affine space. Their definition is surprisingly simple
|
|
whereas the operations that are supported without leaving the model
|
|
are versatile. Nef's model of polyhedra does not impose topological
|
|
restrictions on the sets that can be modeled like manifold or
|
|
regularized models do. This implies, of course, that the abstract
|
|
representation of the underlying theory has to cope with general
|
|
topological complexity. The main reason for us to offer a data type
|
|
for Nef polyhedra is that many other models that are standard concepts
|
|
in the field are covered by Nef's model:
|
|
\begin{itemize}\parsep0ex plus 0.0ex minus 1.0ex%
|
|
\itemsep0ex\topsep-0.5ex%
|
|
\item A \emph{convex polytope} is defined as the convex hull of a
|
|
nonempty finite set of points. Convex polytopes are thus compact
|
|
closed and manifold sets. \cite{gruenbaum67}
|
|
\item An \emph{elementary polyhedron} is defined as the union of a
|
|
finite number of convex polytopes. \cite{gruenbaum67}
|
|
\item A \emph{polyhedral set} is defined as the intersection of a
|
|
finite number of closed half-spaces. Such sets are closed and convex
|
|
but need not to be compact. \cite{gruenbaum67}
|
|
\item The set of all points belonging to the simplices of a
|
|
\emph{simplicial complex} is normally called a (rectilinear)
|
|
polyhedron. \cite{lefschetz71}
|
|
\end{itemize}
|
|
This list shows that a system modeling Nef polyhedra enables a user to
|
|
calculate in many interesting domains.
|
|
|
|
\section{Previous Work}
|
|
|
|
We cite the main publications from the field and present its
|
|
development. We will first give an outline, then we go into more
|
|
details about the notions that are interesting with respect to our
|
|
research. Other notions are solely linked to the literature.
|
|
|
|
The theory of Nef polyhedra was first published in W. Nef's book
|
|
``Beitr{\"a}ge zur Theorie der Polyeder mit Anwendungen in der
|
|
Computergraphik'' \cite{nef:polybook}. The book presents a
|
|
mathematically sound theory of a general kind of polyhedra in
|
|
arbitrary dimension and provides a great intuition about the
|
|
generality of the elaborated concepts. The algorithmic part does not
|
|
specialize in dimension. It should be clear that by realizing only a
|
|
fixed low-dimensional data type the corresponding algorithms and data
|
|
structures can be streamlined in runtime and space requirements.
|
|
|
|
In the following considerations we concentrate mainly on the presented
|
|
data structure and the realization of a binary set intersection
|
|
operation, which poses the most requirements\footnote{The calculation
|
|
of closure, interior, or complement is much simpler due to the fact
|
|
that the faces of the input polyhedron are part of the faces of the
|
|
output polyhedron in these cases.} on the underlying data structures
|
|
and algorithmic modules.
|
|
|
|
At the workshop on computational geometry in W{\"u}rzburg
|
|
\cite{lncs333-88}, a refined elaboration of the concepts in Nef's book
|
|
was given. The paper introduces the later-named \emph{W{\"u}rzburg
|
|
structure}, which is the set of all low-dimensional\footnote{not
|
|
full-dimensional} faces of a polyhedron. Each such face is stored in
|
|
the form of its local pyramid\footnote{Pyramids represent
|
|
faces.}. Thus, the data structure is essentially a collection of
|
|
pyramids. Each pyramid is realized by a selective arrangement of
|
|
hyperplanes. This representation of pyramids is \emph{not lean} in
|
|
low dimensions and can be improved. Moreover, no incidence relation is
|
|
coded into the collection. The intersection operation of two
|
|
polyhedra is defined on top of this structure and is based on two
|
|
techniques: recursion in dimension and superposition of two local
|
|
pyramids. Neither space nor runtime bounds are given for the
|
|
algorithmic description, which, apart from that, is clearly
|
|
structured. The paper solves some subproblems by introducing a
|
|
symbolic parameter to obtain a symbolic hyperplane at
|
|
infinity. However the transfer from the affine to the symbolic objects
|
|
is part of the algorithmic flow and not encapsulated into a geometric
|
|
kernel as we proceed in \repdiss{the report
|
|
\cite{TR:infimaximalframes}}{Chapter \ref{chap Infimaximal Frames}}.
|
|
|
|
H. Bieri's introduction \cite{bieri:nefintro} was a step to market Nef
|
|
polyhedra to a wider audience. It provides a summary of the book and
|
|
introduces all notions in a more tutorial-style picture. It also
|
|
describes a small test system realizing binary set and simple
|
|
topological operations written in PASCAL based on the so-called
|
|
\emph{extended W{\"u}rzburg structure}. The predicate ``extended''
|
|
stems from the addition of incidence links to the W{\"u}rzburg
|
|
structure\footnote{However, the extension is based only on an untyped
|
|
list.}.
|
|
|
|
The article \cite{bieri:booltopolops} describes the realization of
|
|
simple topological and set operations on top of the W{\"u}rzburg
|
|
structure. It has an introductory part and an algorithmic part. The
|
|
key operation of interest, the intersection of two Nef polyhedra, is
|
|
defined in pseudo code in a dimension-recursive manner (up to
|
|
dimension three but a possible generalization to higher dimensions is
|
|
sketched). The main phase of the operation is based on a spatial sweep
|
|
approach, but the presentation is rather condensed, mainly
|
|
mathematical, and lacks runtime and space bounds. In fact, the
|
|
described procedure up to dimension two is quadratic and we will
|
|
improve this bound to the optimal plane sweep bound (of segment
|
|
intersection).
|
|
|
|
A follow-up to the previous publication is the article
|
|
\cite{bieri:twobasicops} that mainly closes open details of
|
|
algorithmic considerations of the previous articles. Its main impact
|
|
is the introduction of the \emph{reduced W{\"u}rzburg
|
|
structure}. H.~Bieri shows that it suffices to store the pyramids of
|
|
faces that are minimal elements of the incidence relation (a partial
|
|
order defined via closure relation). As a consequence, this reduces
|
|
the space requirements of the pyramid collection but requires
|
|
additional algorithmic processing when collecting all faces. The
|
|
proposition has a strong impact on the representation of Nef polyhedra
|
|
when we consider dimension three or higher. In space, the above result
|
|
implies that the pyramids of all vertices suffice to completely
|
|
describe a compact polyhedron.
|
|
|
|
The paper \cite{bieri:representationconversion} embeds Nef polyhedra
|
|
in the field of solid modeling by offering conversion routines between
|
|
previously defined data structures for Nef polyhedra: the reduced
|
|
W{\"u}rzburg structure, selective cellular complexes based on
|
|
hyperplane arrangements, CSG-trees based on half-spaces, and binary
|
|
space partitions. Again there are no time and space bounds.
|
|
|
|
J.R.~Rossignac and M.A.~O'Connor \cite{ro:SGC} have introduced
|
|
\emph{Selective Geometric Complexes} (SGC). An SGC consists of a
|
|
cellular complex (the topological structure) and the corresponding
|
|
geometric support spaces. Geometrically SGCs use \emph{real algebraic
|
|
varieties} as the geometric elements that support cells. Such
|
|
varieties can be decomposed into finite sets of connected smooth
|
|
manifolds (so-called \emph{extents}). An SGC is therefore a collection
|
|
of mutually disjoint cells such that (1) each cell is a relatively
|
|
open subset of an extent, (2) for each cell of the complex its
|
|
boundary (a set of cells) is also part of the complex, and (3) each
|
|
cell has a Boolean selection flag. The dimension of a cell is
|
|
determined by the dimension of its underlying extent. The point set
|
|
modeled by such a complex is the union of all selected cells.
|
|
|
|
One important concept is the incidence relation on cells. In SGCs it
|
|
is defined in terms of a \emph{boundary} and a \emph{star} relation
|
|
stemming from the corresponding concepts of simplicial
|
|
complexes. Moreover due to the possibly curved geometry of extents,
|
|
the notion of \emph{neighborhood} (orientation) is introduced to
|
|
disambiguate degenerate boundary conditions.
|
|
|
|
The description of SGCs is still abstract and leaves room for
|
|
refinement concerning the realization of the necessary data structure
|
|
concepts\footnote{A promised follow-up paper never appeared.}. The
|
|
paper presents the central ideas and abstract definition of SGCs and
|
|
its notions. It sketches binary operations based on the data type
|
|
separated into phases. (We use this approach later in our
|
|
implementation.) On the other hand, the paper omits many concrete
|
|
considerations of the algorithmic subtasks (boundary evaluations,
|
|
merging complexes, etc.) including runtime and space complexity. The
|
|
problem of unbounded structures is not an issue.
|
|
|
|
How do SGCs relate to Nef polyhedra? The support spaces of Nef
|
|
polyhedra are flats. Therefore, varieties and extents are not separate
|
|
concepts. Many geometric ambiguities do not occur. The theory of Nef
|
|
polyhedra as described by Nef and Bieri varies in the way the exterior
|
|
of Nef polyhedra is modeled. In their later papers, the exterior is a
|
|
\emph{non-proper} face and thereby the ambient space is completely
|
|
partitioned. With SGCs the exterior is not a cell. We want to stress
|
|
the following similarity. Assume we realize SGCs geometrically
|
|
restricted to flats. Then, the simplification algorithm as part of the
|
|
algorithmic description of the binary operations on SGCs produces
|
|
cells that are the connected components of proper Nef
|
|
faces. \repdiss{}{(The simplification algorithm is described and used
|
|
in Section \ref{overlay module} of this thesis.)}
|
|
|
|
K.~Dobrindt, K.~Mehlhorn, and M.~Yvinec \cite{dmy:polyhedra} describe
|
|
an efficient algorithm for the intersection of a convex polyhedron and
|
|
a Nef polyhedron in three-dimensional space. The presentation uses a
|
|
\emph{local graph} data structure modeling the local view that we
|
|
introduce below. The local graph data structure is used as a vehicle
|
|
to project the three dimensional topological neighborhood (the local
|
|
pyramid) that defines Nef facets into the surface of a sphere centered
|
|
at a point of interest. Their idea greatly simplifies the
|
|
representation of local pyramids in three dimensions and allows a
|
|
space-efficient representation thereof (linear as opposed to the
|
|
possible quadratic space of the original proposed arrangements). The
|
|
corresponding algorithm was not implemented.
|
|
|
|
K.~Mehlhorn and S.~Naeher have introduced the notion of planar
|
|
\emph{generalized} polygons and implemented them in LEDA \cite[Section
|
|
10.8]{ledabook}. A generalized polygon is a point set bounded by
|
|
possibly several (weakly) simple polygonal chains. This topological
|
|
restriction implies that generalized polygons are the same as
|
|
regularized compact Nef polyhedra.
|
|
|
|
V.~Ferrucci \cite{ferrucci:nefreps} presented two implementation
|
|
efforts to realize a data type modeling Nef polyhedra. His first
|
|
approach is based on selective simplicial complexes and restricts the
|
|
model to bounded Nef polyhedra. All simplices are rectilinearly
|
|
embedded into the affine subspace spanned by their vertices and
|
|
represent relatively open convex sets. All simplices (including
|
|
subsimplices) of the complex are selectable by a Boolean flag. The
|
|
point set of such a simplicial complex is the union of the embeddings
|
|
of the selected simplices. In this approach, Nef faces are present
|
|
only implicitly as a union of simplices. The simplicial complex is
|
|
thus a conforming simplicial subdivision of the bounded Nef
|
|
polyhedron. In the second part of the paper, Ferrucci proves that
|
|
binary space partitions can be used to realize general Nef
|
|
polyhedra. Neither representation provides runtime or space
|
|
qualification.
|
|
|
|
Several algorithmic descriptions from the above list either assume
|
|
general position of their inputs to avoid degeneracies or even require
|
|
what is called regular intersection. In some cases, the generally
|
|
present robustness problems are tackled by transformations of the
|
|
underlying coordinate system to avoid degenerate inputs and minimize
|
|
robustness problems. The possibility of that approach was presented in
|
|
\cite{nefschmidt90}.
|
|
|
|
Our approach differs from the previous work in several aspects. We
|
|
elaborate on the planar case which is, of course, easier than the
|
|
higher-dimensional case but leaves room for optimization via
|
|
specialization. We solve the problem of degeneracy and robustness.
|
|
We use standard data structures of our field to represent Nef
|
|
polyhedra. We generalize an optimized plane sweep framework that can
|
|
handle all degenerate cases for the binary operations and meet the
|
|
optimal time and space bounds. One of our main contributions is the
|
|
introduction of an extended geometric kernel that encapsulates the
|
|
necessary geometric predicates to run the operations of the data
|
|
type. In \repdiss{the report \cite{TR:infimaximalframes}}{Chapter
|
|
\ref{chap Infimaximal Frames}} we show the implementation of the
|
|
kernel in two flavors, one which is simple to implement and one which
|
|
is tuned by filter methods and is both robust and fast.
|
|
|
|
Quite some effort is put into the user interface of the software. Our
|
|
data type offers the user to get her hands on faces by handles and
|
|
explore the incidence of an object within the geometric structure.
|
|
Our data type is based on a space-efficient implementation of plane
|
|
maps. It incorporates an intuitive exploration interface and allows
|
|
further attribution of the objects. Our data type could be considered
|
|
a flavor of the extended W{\"u}rzburg structure where we shift the
|
|
incidence into the center of our attention. Faces are not represented
|
|
by their pyramids but the pyramids can be inferred from the incidence
|
|
relation and the extent of any face can be explored via incident
|
|
lower-dimensional faces at a cost linear in its size. It is our
|
|
conviction that the original W{\"u}rzburg design is reasonable in
|
|
higher-dimensions but means a loss of strength in the planar case. We
|
|
also add functionality. Exploration of a geometric complex needs point
|
|
location and ray shooting operations to link a user's geometric
|
|
question into the geometric complex and its incidence relation.
|
|
Finally our approach unifies the handling of special cases. By the
|
|
introduction of infimaximal frames (\repdiss{technical report
|
|
\cite{TR:infimaximalframes}}{Chapter \ref{chap Infimaximal Frames}})
|
|
and their integration into the geometric complex we enclose the
|
|
geometric complex into a symbolic box. Exploration and maintainance
|
|
of the structures become much easier and more homogeneous as the faces
|
|
of minimal dimension are always vertices. The latter has interesting
|
|
consequences as Bieri has shown that the local pyramids of minimal
|
|
faces describe the polyhedron completely.
|
|
|
|
In this project we realize a data type |Nef_polyhedron_2|. We present
|
|
a software project clearly separated into different modules
|
|
responsible for the different aspects of its realization. We will
|
|
start with the theory, derive an abstract representation, map it to an
|
|
abstract data type and add the algorithmic components.
|
|
|
|
In Section \ref{nef theory} we present the abstract knowledge about
|
|
Nef polyhedra and introduce the notions that we use. Afterwards, in
|
|
Section \ref{nef data structure} we present the necessary software
|
|
components of our design. We introduce the geometric and topological
|
|
modules and their interaction. Then, in Section \ref{nef
|
|
implementation} we present the concrete software design of our main
|
|
interface data type and describe the interaction of different modules
|
|
to implement the geometric methods of that data type. \repdiss{To
|
|
fill the details of the representation we append the three additional
|
|
implementation projects: the extended geometry in the Chapters
|
|
\ref{simple extended geometry} and \ref{filtered extended geometry},
|
|
the binary operations in Chapter \ref{plane map overlay}, and the
|
|
point location in Chapter \ref{plane map point location}.}{In Section
|
|
\ref{nef implementation} we describe the top-level software design, in
|
|
Section \ref{plane map implementation} we provide some basics about
|
|
how we integrated the CGAL HDS as our plane map data type. In Section
|
|
\ref{overlay module} we describe the detailed techniques that
|
|
implement overlays of segments and plane maps. Afterwards, in Section
|
|
\ref{generic plane sweep} we present a generic plane sweep framework
|
|
used as a working horse in the former implementation. Finally in
|
|
Section \ref{Nef Conclusions} we present runtime results and further
|
|
applications.}
|
|
|
|
\section{The Theory}\label{nef theory}
|
|
|
|
We start with the formal definition of Nef polyhedra.
|
|
|
|
\begin{deff}[Nef Polyhedra \cite{nef:polybook}]\label{nefpolyhedra}
|
|
A set $P \subseteq \Rn$ is a \emph{Nef polyhedron} if $P$ is the
|
|
result of a recursive application of set intersection and set
|
|
complement starting from open half-spaces.
|
|
\end{deff}
|
|
This definition supports the claim that they are the most general
|
|
framework to handle polyhedral sets. As set union, set difference, and
|
|
symmetric set difference can be reduced to intersection and complement
|
|
all these set operations are closed in the model.
|
|
|
|
H.~Bieri later gave alternative definitions for Nef polyhedra and
|
|
proved their equivalence.
|
|
\begin{fact}[Bieri \cite{bieri:nefintro}]\label{bieri alternative defs}
|
|
The original definition is equivalent to any of the following
|
|
conditions
|
|
\begin{enumerate}\parsep0ex plus 0.0ex minus 1.0ex%
|
|
\itemsep0ex\topsep-0.5ex%
|
|
\item $P$ corresponds to the root of a CSG-tree with closed half-spaces
|
|
as leaf primitives and intersection, union and difference as
|
|
internal nodes.
|
|
\item There exist two finite families $F = \{f_1,\ldots,f_n\}$ and $G
|
|
= \{g_1,\ldots,g_m\}$ of relatively open subsets of $\Rn$ such that $P
|
|
= \bigcup_i f_i$ and $\cpl P = \bigcup_j g_j$.
|
|
\item There exists a set of hyperplanes $H$ such that $P$ is the
|
|
union of some cells of the arrangement $\mathcal{A}(H)$.
|
|
\end{enumerate}\end{fact}
|
|
(1) gives a link to constructive solid geometry. (2) links Nef
|
|
polyhedra to cellular complexes and (3) to hyperplane arrangements.
|
|
When studying the original theory the third equivalence is actually
|
|
the key observation. Many propositions about the point set $P$ can be
|
|
reduced to an examination of the minimal building blocks of the
|
|
polyhedron: the cells of the arrangement built by the hyperplanes that
|
|
define the polyhedron.
|
|
|
|
The elegance of the definition is carried forward to the notion of
|
|
faces.
|
|
|
|
\begin{deff}[Local pyramids and Faces]
|
|
Let $K \subseteq \Rn, x \in \Rn$. We call $K$ a \emph{cone with apex}
|
|
$0$ if $K = \R^+ K$ and cone with apex $x $ if $K = x + \R^+ (K-x)$. A
|
|
cone that is also a polyhedron is called a \emph{pyramid}.
|
|
|
|
Now let $P \subseteq \Rn$ be a polyhedron and $x \in \Rn$. There is a
|
|
neighborhood $U_0(x)$ such that the pyramid $Q := x + \R^+ ((P \cap
|
|
U(x)) - x)$ is the same for all neighborhoods $U(x) \subseteq
|
|
U_0(x)$. $Q$ is called the \emph{local pyramid} of $P$ in $x$ and is
|
|
denoted $P^x$.
|
|
|
|
A \emph{face} $s$ of $P$ is then a maximal non-empty subset of $\Rn$
|
|
such that all of its points have the same local pyramid $Q$, i.e., $s
|
|
= \{x\in\Rn : P^x = Q\}$. In this case $Q$ is also denoted $P^s$. The
|
|
dimensions of a face is the dimension of its affine hull $\dim(s) :=
|
|
\dim(\aff s)$.
|
|
\end{deff}
|
|
|
|
\displayeps{localview}{The polyhedron $P$ consists of the colored
|
|
face, the triangle boundary and the vertical segment below the
|
|
triangle. Some local views of $P$ are: (A) the full plane, (B) the
|
|
empty set, (C) a radial cake of sectors, (D) a half-space including its
|
|
boundary.}{6cm}
|
|
|
|
Note that this notion of a face partitions $\Rn$ into faces of
|
|
different dimension. Faces as defined by Nef do not have to be
|
|
connected. There are only two full-dimensional faces possible whose
|
|
local pyramids are the space itself or the empty set. All
|
|
lower-dimensional faces form the \emph{boundary} of the polyhedron. As
|
|
usual we call zero-dimensional faces \emph{vertices} and
|
|
one-dimensional faces \emph{edges}. In the plane, we call the
|
|
full-dimensional faces \emph{2-faces} or just \emph{faces} when the
|
|
meaning is clear from the context.
|
|
|
|
\begin{deff}[Incidence]
|
|
Two faces $s$ and $t$ (in their general sense) are \emph{incident} if
|
|
$s \subseteq \clos t$.
|
|
\end{deff}
|
|
We will treat incidence as a bidirectional relation. We say that
|
|
$s$ is \emph{downward-incident} to $t$ and conversely that $t$ is
|
|
\emph{upward-incident} to $s$.
|
|
|
|
We now list some facts about faces. The proofs for these facts can be
|
|
found in Nef's book \cite{nef:polybook}. We append the chapter and
|
|
theorem numbers separated by a semicolon. All faces of a polyhedron
|
|
are polyhedra [6;1.1]. Faces are relatively open sets [6;2]. The
|
|
linear subspace of all apices of the pyramid associated with a face is
|
|
the affine hull of the face [6;2]. $x \in P^x$ iff $x \in P$ [3;7].
|
|
Let $s$ be a face of the polyhedron $P$ and let $t$ be a face of $s$.
|
|
Then, $t$ is the union of some faces of $P$ [6;12]. A face of $P$ is
|
|
either a subset of $P$ or disjoint from $P$ [6;4]. For two faces $s$
|
|
and $t$ of $P$ either $s \subseteq \clos(t)$ or $s \cap \clos(t) =
|
|
\emptyset$ [6;9,10].
|
|
|
|
Remember that Nef edges and Nef 2-faces are not necessarily
|
|
connected. Note also that some connected components of edges are not
|
|
necessarily bounded by a vertex. What do the local pyramids of any
|
|
point $x$ in the plane look like? We can represent them by the
|
|
intersection of a small enough neighborhood disc centered at $x$ with
|
|
its pyramid $P^x$. The disc is partitioned into sectors by radial
|
|
segments. We assign a mark to the center, to any radial segment, and
|
|
to the sectors in between the radial segments such that the
|
|
corresponding point set is marked if it belongs to the local pyramid
|
|
of $x$. We also call such a disc together with its marks the
|
|
\emph{local view} of $x$ (cf. Figure \figref{localview}).
|
|
|
|
\begin{lemma}\label{local view properties}
|
|
The local view of a point $x$ has the following properties:
|
|
\begin{enumerate}
|
|
\item the mark of any radial segment is different from one of the two
|
|
sectors incident to it.
|
|
\item $x$ is contained in a 2-face of $P$ iff the local view is a disc
|
|
that contains no radial segments and is marked as the center.
|
|
\item $x$ is contained in an edge of $P$ iff the local view contains
|
|
exactly two radial segments that are part of a line through the
|
|
center; the center and the two radial segments are marked equally but
|
|
their common mark is different from at least one sector of the disc.
|
|
\item $x$ is a vertex of $P$ iff the mark of the center is
|
|
different from at least one radial segment or one sector of the disc
|
|
and the local view is not that of item 3.
|
|
\end{enumerate}
|
|
\end{lemma}
|
|
\begin{proof}
|
|
(1) follows from the fact that radial segments are part of the
|
|
boundary of an open or closed half-plane and a point on this boundary
|
|
has the local view of that half-plane. (2) refers to the two trivial
|
|
pyramids: the empty set and the full plane. (3) follows from the fact
|
|
that edges are part of the boundary of open and closed half-spaces.
|
|
(4) if all marks are equal then $x$ would have a totally marked or
|
|
non-marked disk neighborhood. But that's the neighborhood of a 2-face.
|
|
\end{proof}
|
|
To determine the local view of a point $x$ with respect to a
|
|
polyhedron $P$ is called to \emph{qualify} $x$ with respect to $P$.
|
|
|
|
\section{The Data Structure}\label{nef data structure}
|
|
|
|
We want to store Nef polyhedra in an intuitive way and want to use
|
|
generally known data structures. Faces should be objects whose extents
|
|
and topological neighborhoods (incidence) can be explored. We shift
|
|
the focus from pyramids to incidence as compared to the extended
|
|
W{\"u}rzburg structure. In our approach incidence is the key concept,
|
|
pyramids can be derived from it.
|
|
|
|
Starting from the last item of Fact \ref{bieri alternative defs} we
|
|
have to model (parts) of an arrangement of lines in the
|
|
plane. Moreover, we want to model the Nef faces including their
|
|
incidences. 2-faces of a polyhedron are in general two-dimensional
|
|
sets of points bounded by chains of segments that do not have to be
|
|
simple, can be circularly closed, or open. The latter happens when
|
|
2-faces extend to infinity. Then the chain of segments has rays as its
|
|
first and last elements. A simple line bounding a 2-face can be seen
|
|
as two oppositely oriented rays starting in the same point.
|
|
|
|
Therefore, concerning the geometry of edges, we need to model
|
|
straight-line objects, such as segments, rays and lines, which are the
|
|
result of intersection operations of half-spaces. To simplify the
|
|
treatment of unbounded structures we use the concept of extended
|
|
points and infimaximal frames which we have formally introduce in
|
|
\repdiss{the report \cite{TR:infimaximalframes}}{Chapter \ref{chap
|
|
Infimaximal Frames}} and which constitutes the geometric layer of our
|
|
design. Consider an axis-parallel squared box centered at the
|
|
origin. Any ray is pruned by this box in a so-called non-standard
|
|
point (corresponding to the ray-tip). The assignment of ray-tips to
|
|
box segments becomes topologically constant when we grow the framing
|
|
box above a certain size. The frame is called infimaximal because it
|
|
is always large enough to enclose all concrete geometric objects like
|
|
points and segments in its interior that appear in the execution of
|
|
our algorithms. Extended points are defined to be standard points and
|
|
the non-standard points corresponding to ray-tips. They allow us to
|
|
represent segments, rays and lines by a pair of such points and we get
|
|
rid of the infinite extent of the unbounded structures. Adding such a
|
|
frame to bound the plane, all unbounded faces become symbolically
|
|
bounded structures, their boundary becomes a cyclic structure.
|
|
|
|
To realize Nef faces, including their incidence relations, we need a
|
|
cellular subdivision of the plane. We use a plane map data type
|
|
(bidirected, embedded graphs). The incidence relations between the
|
|
objects of a plane map (vertices, edges, and faces) reflect the
|
|
topology of the local pyramids of the planar Nef
|
|
polyhedron. \repdiss{}{The plane map concept is presented in the
|
|
introduction of our notions and is our topological bottom layer.}
|
|
Most newer textbook recommend the use of doubly connected edge lists
|
|
(DCEL) or equivalently half edge data structures (HDS)
|
|
\cite{prep-sham:CG,mmmo:CG} when they discuss the implementation of
|
|
plane maps. There are already standard implementations of |plane
|
|
maps| like the CGAL HDS or embedded bidirected graphs in LEDA (similar
|
|
design). We use an extended version of the CGAL HDS structure as the
|
|
topological bottom layer of our Nef polyhedra. See the paper of
|
|
L. Kettner \cite{kettner-polyhedra} for an excellent review of
|
|
different plane map representations and for the description of the
|
|
adaptability of the CGAL HDS. To be more flexible we insert a
|
|
decorator interface that homogeneously defines the functionality
|
|
offered by the HDS. Geometrically we embed the vertices by means of
|
|
extended points. Segments, rays, and lines are uniformly treated by
|
|
the straight-line embedding of edges incident to such vertices. We
|
|
additionally add one face cycle of edges whose embedding corresponds
|
|
to an infimaximal frame:
|
|
|
|
\begin{construction}\label{from Nef faces to plane map faces}
|
|
Consider a Nef polyhedron $P$ and the connected components of the
|
|
faces of dimension zero to two. Assign plane map objects of
|
|
corresponding dimension to each component and match the Nef incidence
|
|
concept with the plane map incidence concept where possible. All
|
|
objects corresponding to unbounded connected components of Nef edges
|
|
and unbounded components of Nef 2-faces have incomplete incidence
|
|
structures: edges are missing end vertices and faces are missing
|
|
closed face cycles. To cure this, we add an infimaximal frame
|
|
consisting of four uedges and four corner vertices. For all plane map
|
|
edges $e$ that correspond to a Nef edge component extending to
|
|
infinity along a ray $r$ we do the following: if $r$ is pruned by one
|
|
of the corner vertices on the frame structure link $e$ to that vertex;
|
|
otherwise $e$ obtains an additional terminating vertex in the relative
|
|
interior of a uedge $e'$ that is part of the infimaximal frame where
|
|
$r$ meets the frame. $e'$ is split into two uedges by this vertex
|
|
insertion. (For Nef edges that represent lines we do this at both
|
|
ends.) After all such edges are linked to the frame (respecting the
|
|
embedding such that the adjacency lists are order-preserving), all
|
|
plane map faces corresponding to an unbounded component of a Nef face
|
|
are cyclically bounded and their incidence structure can be
|
|
completed. We call all edges and vertices that are part of the frame
|
|
and the face outside of the frame that completes the subdivision
|
|
combinatorially \emph{infimaximal frame objects}.
|
|
\end{construction}
|
|
|
|
To mark set membership, all objects of the plane map are
|
|
\emph{selectable}. All objects (vertices, edges, faces) obtain a
|
|
marker labeling set inclusion or exclusion. The markers allow us to
|
|
obtain the local pyramids associated with the plane map objects. The
|
|
local view of a vertex is defined by its own marker, the markers of
|
|
the edges in its adjacency list and markers of the faces in between
|
|
these edges representing the neighborhood disc as explained above. The
|
|
local view of an edge is defined by its mark and the marks of its two
|
|
incident faces. Finally the mark of a face maps to the trivial local
|
|
view: the whole plane or the empty set depending on its selection
|
|
flag. The selection markers of infimaximal frame objects have no
|
|
geometric meaning and therefore those objects are always kept
|
|
unselected.
|
|
|
|
As plane maps are implemented by bidirected graphs, the incidence
|
|
relation between edges and faces is encoded in an oriented fashion.
|
|
Unoriented edges are implemented as pairs of oppositely directed
|
|
halfedges where each such halfedge is incident to exactly one face.
|
|
\repdiss{}{See the implementation description of plane maps in Section
|
|
\ref{plane map implementation} for more information.}
|
|
|
|
\begin{deff}[Data type]\label{data type definition}
|
|
A Nef polyhedron $P$ is stored as a selective plane map $(V,E,F)$
|
|
according to Construction \ref{from Nef faces to plane map faces}.
|
|
The objects of the plane map (vertices, edges, and faces) correspond
|
|
to the connected components of the Nef faces of corresponding
|
|
dimension and additionally to the infimaximal frame objects.
|
|
|
|
Each vertex $v \in V$ is embedded via an extended point |point(v)|.
|
|
Each object $o \in V \cup E \cup F$ is contained in $P$ iff the
|
|
selection mark |mark(o) == true|.
|
|
\end{deff}
|
|
|
|
The feasibility of this definition can be seen as follows. Interpret
|
|
$P$ as a set-valued function $\phi$ on (open) half-spaces
|
|
$H_1,\ldots,H_r$ that are combined by the operations $\cap$ and
|
|
$\cpl$. Let $h_i$ be the line bounding the half-space $H_i$. Now
|
|
consider the arrangement built by the lines $\{h_i\}_i$ enclosed in a
|
|
large enough frame. Interpret the arrangement $A(h_1,\ldots,h_r)$
|
|
inside the frame as a collection of relatively open convex cells of
|
|
dimension 0 to 2. Consider any cell $c$. Any point of $c$ is either in
|
|
$\phi(H_1,\ldots,H_r)$ or not. Mark all cells correspondingly. It
|
|
should be clear that $A(h_1,\ldots,h_r)$ can be represented by a plane
|
|
map as described above. Now start a simplification process. Consider
|
|
all edges $e$ (besides the ones on the frame box). If $e$ and the two
|
|
faces incident to it have the same mark, remove the edge and unify the
|
|
faces (if not equal). Afterwards we iterate over all vertices and
|
|
check if any vertex incident to two edges that are supported by the
|
|
same line has the same local view as the points in the relative
|
|
interior of the two incident edges. If this is the case, we unify the
|
|
two edges and remove the vertex. Finally, we remove all vertices that
|
|
are isolated and whose selection mark equals the one of the
|
|
surrounding face. The final complex is just the data type
|
|
described. When the simplification iteration terminates all points on
|
|
vertices and edges have a local view that makes them low-dimensional
|
|
faces of the Nef polyhedron.
|
|
|
|
The above algorithm is called \emph{simplification} and is described
|
|
in more detail when considering binary operations. There, we also give
|
|
a runtime bound. Note that the local view properties of the vertices,
|
|
edges, and faces of the plane map after the simplification process are
|
|
a necessary condition of Definition \ref{data type definition}.
|
|
|
|
\begin{lemma}
|
|
The representation of Definition \ref{data type definition} is unique.
|
|
\end{lemma}
|
|
\begin{proof}
|
|
Assume that there are two different plane maps $M(V,E,F)$ and
|
|
$M'(V',E',F')$ representing the same Nef polyhedron $P$. If $P$ is the
|
|
complete plane or the empty set then the plane maps consist of just
|
|
one face inside the frame box and $F$ and $F'$ and their selection
|
|
markers have to be equal. So assume otherwise. Then neither $P$ nor
|
|
$\cpl P$ is empty. The boundary of $P$ and $\cpl P$ consists of
|
|
vertices and edges. Assume that $M$ and $M'$ have a vertex at a point
|
|
$x$ but the local views differ. Then, the represented point sets of
|
|
$M$ and $M'$ differ and thus $P$ cannot be represented by both. If
|
|
there is a point $x$ where $M$ has a vertex $v$ but $M'$ does not then
|
|
obviously the local views are different too. Note that edges are
|
|
terminated by vertices, and thus edges that are in $M$ but not in $M'$
|
|
already imply differences in the local view of their end vertices. The
|
|
same holds when the edges are equal but the selection markers are
|
|
not. Finally, note that the fact that the $1$-skeleta of $M$ and $M'$
|
|
are equal, implies that the faces are as well (they are defined that
|
|
way). Different selection markers on faces imply different local views
|
|
in the vertices that are part of their closure. As a consequence $M$
|
|
and $M'$ have to be equal.
|
|
\end{proof}
|
|
|
|
Selective plane maps are the basic structure used to store Nef
|
|
polyhedra. Remember that the edges and faces of a plane map are the
|
|
connected components of the Nef edges and Nef 2-faces. For performance
|
|
reasons we do not maintain the relationship between plane map objects
|
|
and the corresponding abstract Nef faces, which are defined as
|
|
collections of the plane map objects with the same implicitly stored
|
|
local pyramids. The \emph{size} of a Nef polyhedron is the size of its
|
|
underlying plane map (which is the number of vertices, edges, and
|
|
faces).
|
|
|
|
For the binary operations we follow an approach as presented by
|
|
Rossignac et al. \cite{ro:SGC}. In our case the approach is based on a
|
|
generic plane sweep framework. A binary operation is basically split
|
|
into three phases: subdivision -- selection -- simplification. The
|
|
implementation is presented in the module |PM_overlayer|. A unary
|
|
(topological) operation can be subdivided into two phases: selection
|
|
and simplification.
|
|
|
|
We shortly present the abstract algorithmic ideas and the runtimes.
|
|
|
|
\begin{description}
|
|
|
|
\item[subdivision] means for two plane maps $P_i (i=0,1)$ to create a
|
|
plane map $P$ with a minimal number of objects (vertices, edges,
|
|
faces) such that each object of $P$ is supported by exactly one object
|
|
of $P_i$ for $i=0,1$. The subdivision is realized by a plane sweep of
|
|
the objects of the $1$-skeleta of $P_i$ followed by face creation and
|
|
determination of support. The time is dominated by the time for the
|
|
sweep phase which is $O(n \log n)$ where $n$ is the size of the
|
|
resulting subdivision.
|
|
|
|
\item[selection] with respect to the binary set operations means
|
|
selecting the cells of the subdivision according to the logic of the
|
|
underlying Boolean operation. With respect to unary topological set
|
|
operations, selection happens according to the logic of the
|
|
topological unary operation. Selection is in both cases linear in $n$.
|
|
|
|
\item[simplification] means unification of subsets of cells that have
|
|
the same local view. This phase has a quasi-linear runtime due to the
|
|
usage of a union-find data structure. Runtime is $O(n
|
|
\alpha(kn,n))$\footnote{$\alpha(kn,n)$ is the extremly slow growing
|
|
inverse of the Ackermann function used in the analysis of union-find
|
|
data structures.} for some small constant $k$.
|
|
\end{description}
|
|
|
|
\begin{theorem}
|
|
The result of a binary set operation (intersection, union, difference,
|
|
symmetric difference) of two Nef polyhedra $P_0$ and $P_1$ can be
|
|
calculated in time $O(n \log n)$ where $n$ is the size of the overlay
|
|
of $P_0$ and $P_1$. The result of a unary set operation (complement,
|
|
boundary, interior, closure, regularization) can be constructed in
|
|
time $O( n \alpha(kn,n) )$ where $n$ is the size of the input
|
|
structure.
|
|
\end{theorem}
|
|
|
|
The correctness and time bounds of our binary operations are based on
|
|
three parts:
|
|
\begin{itemize}
|
|
\item The Sections \ref{unary operations} and \ref{binary set
|
|
operations} show the high-level composition of unary and binary set
|
|
operations decomposed into phases.
|
|
\item \repdiss{Chapter \ref{plane map overlay}}{Section \ref{overlay
|
|
module}} provides the algorithmic modules for the subdivision,
|
|
selection, and simplification phase. The correctness and resource
|
|
argumentation is purely based on affine concepts and therefore our
|
|
readers can trust their standard geometric intuition when verifying
|
|
the correctness of those modules.
|
|
\item \repdiss{The report \cite{TR:infimaximalframes}}{Chapter
|
|
\ref{chap Infimaximal Frames}} on the other hand shows that
|
|
infimaximal frames allow us to use these algorithmic modules together
|
|
with our extended objects.
|
|
\end{itemize}
|
|
The latter two observations together imply the correctness of the
|
|
above theorem. The runtime lemmata of \repdiss{Chapter \ref{plane map
|
|
overlay}}{Section \ref{overlay module}} imply the time bounds.
|
|
|
|
Now for our additional functionality like point location and ray
|
|
shooting queries we need more than just the naked plane map structure
|
|
described above. Looking at the literature for ray shooting there is
|
|
the notion of segment walks, which can be done easiest in convex
|
|
subdivisions of the plane of bounded complexity
|
|
\cite{mms:qsrayshooting}. Our goal is to refine the basic plane map
|
|
by such a structure. Note that this structure implies again faces,
|
|
edges, and vertices, but of a much simpler fashion. However we do not
|
|
want to lose the original character of the plane map. We might still
|
|
be interested in the original face cycles. We store the refinement
|
|
separate from the plane map. One solution to the segment walk problem
|
|
is to use a constrained Delaunay triangulation of the edges and
|
|
vertices of the HDS, and use any efficient point location structure
|
|
for the location of the ray shooting start point. \repdiss{Our
|
|
approach is presented in Section \ref{PM_point_locator}.}{Point
|
|
location is ommitted here and can be studied in the full
|
|
implementation report \cite{TR:nefimplementation}.}
|
|
|
|
\section{Top Level Implementation}\label{nef implementation}
|
|
|
|
\displayeps{General_design}{An UML diagram of the Nef polyhedron
|
|
module. The main modules are the geometry |Extended_homogenous<RT>|,
|
|
the plane map decorator |PM_decorator<HDS>|, the two algorithmic
|
|
modules |PM_overlayer<PMDEC,GEOM>| and
|
|
|PM_point_locator<PMDEC,GEOM>|.}{12cm}
|
|
|
|
The whole implementation scheme is depicted in Figure
|
|
\figref{General_design}. The main classes map to the abstract layers
|
|
described above: geometry, plane maps, binary overlay, and point
|
|
location.
|
|
\begin{onlyindiss}
|
|
In the following, we will mainly concentrate on the realization of the
|
|
class |Nef_polyhedron_2| and the overlay modules |PM_overlayer| and
|
|
|Segment_overlay_traits|. The functionality of |PM_decorator| is
|
|
described by the concept in the appendix. The realization of the
|
|
extended kernel is a topic of Chapter \ref{chap Infimaximal Frames}.
|
|
\end{onlyindiss}
|
|
|
|
\subsection{The Polyhedron Class}
|
|
|
|
\begin{ignoreindiss}
|
|
<<nef polyhedron definition>>=
|
|
template <typename T> class Nef_polyhedron_2;
|
|
template <typename T> class Nef_polyhedron_2_rep;
|
|
|
|
template <typename T>
|
|
std::ostream& operator<<(std::ostream&, const Nef_polyhedron_2<T>&);
|
|
template <typename T>
|
|
std::istream& operator>>(std::istream&, Nef_polyhedron_2<T>&);
|
|
@ \end{ignoreindiss}
|
|
Our data type |Nef_polyhedron_2<T>| is implemented as a smart pointer
|
|
data type. Content such as a plane map object and a pointer to an
|
|
optional point location object is stored in the class
|
|
|Nef_polyhedron_2_rep<T>|. The traits template parameter |T| is
|
|
specified by the concept |ExtendedKernelTraits_2| as presented on page
|
|
\pageref{ExtendedKernelTraits_2}.
|
|
|
|
\displayeps{Nef_polyhedron_2}{The smart pointer realization of data type
|
|
|Nef_polyhedron_2|.}{6cm}
|
|
|
|
Within the scope of |Nef_polyhedron_2_rep<T>| all auxiliary classes
|
|
are instantiated. The plane map type is based on the CGAL HDS and uses
|
|
two traits classes |HDS_traits| and |HDS_items|. The former carries
|
|
attributes; the latter carries the (fixed) models for vertices, edges,
|
|
and faces. \repdiss{}{Compare the extensions to their standard design
|
|
in Section \ref{plane map implementation}.}
|
|
<<nef rep types>>=
|
|
struct HDS_traits {
|
|
typedef typename T::Point_2 Point;
|
|
typedef bool Mark;
|
|
};
|
|
typedef CGAL_HALFEDGEDS_DEFAULT<HDS_traits,HDS_items> Plane_map;
|
|
typedef CGAL::PM_const_decorator<Plane_map> Const_decorator;
|
|
typedef CGAL::PM_decorator<Plane_map> Decorator;
|
|
typedef CGAL::PM_naive_point_locator<Decorator,T> Slocator;
|
|
typedef CGAL::PM_point_locator<Decorator,T> Locator;
|
|
typedef CGAL::PM_overlayer<Decorator,T> Overlayer;
|
|
|
|
@ \begin{ignoreindiss} For Microsoft we use a hardwired Halfedge data
|
|
structure specially adapted to its weaknesses.
|
|
<<nef rep types msc>>=
|
|
struct HDS_traits {
|
|
typedef typename T::Point_2 Point;
|
|
typedef bool Mark;
|
|
};
|
|
typedef CGAL::HalfedgeDS_default_MSC<HDS_traits> Plane_map;
|
|
typedef CGAL::PM_const_decorator<Plane_map> Const_decorator;
|
|
typedef CGAL::PM_decorator<Plane_map> Decorator;
|
|
typedef CGAL::PM_naive_point_locator<Decorator,T> Slocator;
|
|
typedef CGAL::PM_point_locator<Decorator,T> Locator;
|
|
typedef CGAL::PM_overlayer<Decorator,T> Overlayer;
|
|
|
|
<<nef polyhedron definition>>=
|
|
template <typename T>
|
|
class Nef_polyhedron_2_rep
|
|
{ typedef Nef_polyhedron_2_rep<T> Self;
|
|
friend class Nef_polyhedron_2<T>;
|
|
#ifndef CGAL_SIMPLE_HDS
|
|
<<nef rep types>>
|
|
#else
|
|
<<nef rep types msc>>
|
|
#endif
|
|
//typedef CGAL::PM_transformer<Decorator,T> Transformer;
|
|
Plane_map pm_; Locator* pl_;
|
|
|
|
void init_locator()
|
|
{ if ( !pl_ ) pl_ = new Locator(pm_); }
|
|
void clear_locator()
|
|
{ if ( pl_ ) { delete pl_; pl_=0; } }
|
|
public:
|
|
Nef_polyhedron_2_rep() : pm_(), pl_(0) {}
|
|
Nef_polyhedron_2_rep(const Self& R) : pm_(), pl_(0) {}
|
|
~Nef_polyhedron_2_rep() { pm_.clear(); clear_locator(); }
|
|
};
|
|
|
|
@ The class |Nef_polyhedron_2<T>| has a static member object |EK| of
|
|
type |T| which allows us to interface a kernel object.
|
|
<<nef polyhedron definition>>=
|
|
/*{\Moptions print_title=yes }*/
|
|
/*{\Manpage {Nef_polyhedron_2}{T}{Nef Polyhedra in the Plane}{N}}*/
|
|
|
|
/*{\Mdefinition
|
|
An instance of data type |\Mname| is a subset of the plane that is
|
|
the result of forming complements and intersections starting from a
|
|
finite set |H| of half-spaces. |\Mtype| is closed under all binary set
|
|
operations |intersection|, |union|, |difference|, |complement| and
|
|
under the topological operations |boundary|, |closure|, and
|
|
|interior|.
|
|
|
|
The template parameter |T| is specified via an extended kernel
|
|
concept. |T| must be a model of the concept |ExtendedKernelTraits_2|.
|
|
}*/
|
|
|
|
template <typename T>
|
|
class Nef_polyhedron_2 : public Handle_for< Nef_polyhedron_2_rep<T> >
|
|
{
|
|
public:
|
|
typedef T Extended_kernel;
|
|
static T EK; // static extended kernel
|
|
|
|
<<nef interface types>>
|
|
protected:
|
|
<<nef protected members>>
|
|
public:
|
|
<<nef interface operations>>
|
|
|
|
}; // end of Nef_polyhedron_2
|
|
|
|
template <typename T>
|
|
T Nef_polyhedron_2<T>::EK;
|
|
|
|
|
|
@ \end{ignoreindiss}
|
|
|
|
In the class |Nef_polyhedron_2<T>| all geometric types are obtained
|
|
from the geometric traits class |T|. |T| contains affine types that
|
|
are part of the interface but also the extended types that are used in
|
|
the infimaximal framework. The |Standard|-prefixed types from within
|
|
|T| become the interface types of |Nef_polyhedron_2<T>|. The
|
|
non-prefixed\footnote{T is used as a traits class in our generic
|
|
geometry based modules like |PM_overlayer<T>|. Therefore, the extended
|
|
types conform to a simpler naming scheme within |T|.} types within |T|
|
|
become the extended types within |Nef_polyhedron_2<T>|.
|
|
<<nef interface types>>=
|
|
/*{\Mtypes 7}*/
|
|
typedef Nef_polyhedron_2<T> Self;
|
|
typedef Handle_for< Nef_polyhedron_2_rep<T> > Base;
|
|
typedef typename T::Point_2 Extended_point;
|
|
typedef typename T::Segment_2 Extended_segment;
|
|
|
|
typedef typename T::Standard_line_2 Line;
|
|
/*{\Mtypemember the oriented lines modeling half-planes}*/
|
|
typedef typename T::Standard_point_2 Point;
|
|
/*{\Mtypemember the affine points of the plane.}*/
|
|
typedef typename T::Standard_direction_2 Direction;
|
|
/*{\Mtypemember directions in our plane.}*/
|
|
typedef typename T::Standard_aff_transformation_2 Aff_transformation;
|
|
/*{\Mtypemember affine transformations of the plane.}*/
|
|
|
|
@ \begin{ignoreindiss}
|
|
<<nef interface types>>=
|
|
typedef bool Mark;
|
|
/*{\Xtypemember marking set membership or exclusion.}*/
|
|
|
|
enum Boundary { EXCLUDED=0, INCLUDED=1 };
|
|
/*{\Menum construction selection.}*/
|
|
|
|
enum Content { EMPTY=0, COMPLETE=1 };
|
|
/*{\Menum construction selection}*/
|
|
|
|
@ \end{ignoreindiss}
|
|
|
|
We import the type |Plane_map| and all decorator types like
|
|
|Decorator|, |Overlayer|, |Locator|, |Slocator| from the
|
|
representation type |Nef_polyhedron_2_rep|. Additionally, we import
|
|
handles and iterators from |Decorator| \repdiss{}{(we do not list
|
|
those typedef statements)}.
|
|
\begin{ignoreindiss}
|
|
<<nef protected members>>=
|
|
<<boolean classes>>
|
|
typedef Nef_polyhedron_2_rep<T> Nef_rep;
|
|
typedef typename Nef_rep::Plane_map Plane_map;
|
|
typedef typename Nef_rep::Decorator Decorator;
|
|
typedef typename Nef_rep::Const_decorator Const_decorator;
|
|
typedef typename Nef_rep::Overlayer Overlayer;
|
|
//typedef typename Nef_rep::T Transformer;
|
|
typedef typename Nef_rep::Slocator Slocator;
|
|
typedef typename Nef_rep::Locator Locator;
|
|
|
|
Plane_map& pm() { return ptr()->pm_; }
|
|
const Plane_map& pm() const { return ptr()->pm_; }
|
|
|
|
friend std::ostream& operator<< <>
|
|
(std::ostream& os, const Nef_polyhedron_2<T>& NP);
|
|
friend std::istream& operator>> <>
|
|
(std::istream& is, Nef_polyhedron_2<T>& NP);
|
|
|
|
typedef typename Decorator::Vertex_handle Vertex_handle;
|
|
typedef typename Decorator::Halfedge_handle Halfedge_handle;
|
|
typedef typename Decorator::Face_handle Face_handle;
|
|
typedef typename Decorator::Vertex_const_handle Vertex_const_handle;
|
|
typedef typename Decorator::Halfedge_const_handle Halfedge_const_handle;
|
|
typedef typename Decorator::Face_const_handle Face_const_handle;
|
|
|
|
typedef typename Decorator::Vertex_iterator Vertex_iterator;
|
|
typedef typename Decorator::Halfedge_iterator Halfedge_iterator;
|
|
typedef typename Decorator::Face_iterator Face_iterator;
|
|
typedef typename Const_decorator::Vertex_const_iterator
|
|
Vertex_const_iterator;
|
|
typedef typename Const_decorator::Halfedge_const_iterator
|
|
Halfedge_const_iterator;
|
|
typedef typename Const_decorator::Face_const_iterator
|
|
Face_const_iterator;
|
|
|
|
struct Except_frame_box_edges {
|
|
Decorator D_; Face_handle f_;
|
|
Except_frame_box_edges(Plane_map& P) : D_(P), f_(D_.faces_begin()) {}
|
|
bool operator()(Halfedge_handle e) const
|
|
{ return D_.face(e)==f_ || D_.face(D_.twin(e))==f_; }
|
|
};
|
|
|
|
|
|
@ \end{ignoreindiss}
|
|
@ \subsection{Creating Polyhedra}
|
|
|
|
We provide the construction methods for basic polyhedra. These are the
|
|
empty set, the whole plane, open and closed half-planes, and
|
|
construction of simple polygonal chains (Jordan Curves) where the
|
|
modeled point set can be the bounded or unbounded part of the plane
|
|
and the set can be open or closed.
|
|
|
|
\displayeps{creation}{Elementary Nef polyhedra: (A) the empty set, (B) the
|
|
whole plane, (C/D) a closed/open half-plane, (E/F) a closed/open bounded
|
|
polygon, (G/H) the plane with a closed/open polygonal hole.}{10cm}
|
|
|
|
The construction of simple Nef polyhedra is reduced to the overlay of
|
|
a list of extended segments, the creation of the 2-faces, followed by
|
|
setting the attribute marks that encode set inclusion. The first task
|
|
is implemented in our overlayer module
|
|
|PM_overlayer<>::create(s,e,DA)| where |S = tuple[s,e)| is the set of
|
|
segments and |DA| is a data accessor that allows us to link plane map
|
|
edges to the segments in |S|.
|
|
|
|
Finally, we only have to take care of the correct marks of the plane
|
|
map objects with respect to the construction information from our
|
|
constructor interface. Note that by using the overlayer module we
|
|
obtain all output properties of the plane map created from that
|
|
module.
|
|
|
|
All Nef polyhedra obtain an infimaximal frame embedded by four
|
|
extended segments. We encapsulate this into the following operation.
|
|
See the extended geometry module for the definition of this frame.
|
|
<<nef protected members>>=
|
|
typedef std::list<Extended_segment> ES_list;
|
|
typedef typename ES_list::const_iterator ES_iterator;
|
|
|
|
void fill_with_frame_segs(ES_list& L) const
|
|
/*{\Xop fills the list with the four segments which span our frame,
|
|
the convex hull of SW,SE,NW,NE.}*/
|
|
{ L.push_back(Extended_segment(EK.SW(),EK.NW()));
|
|
L.push_back(Extended_segment(EK.SW(),EK.SE()));
|
|
L.push_back(Extended_segment(EK.NW(),EK.NE()));
|
|
L.push_back(Extended_segment(EK.SE(),EK.NE()));
|
|
}
|
|
|
|
@ We want to establish a link between a particular extended segment
|
|
and its corresponding edge in the plane map. Our overlay module allows
|
|
us to get a grip on this relation by means of a data accessor that is
|
|
passed to the overlay algorithm. The class |Link_to_iterator| is a
|
|
model for that data accessor concept. An object |D| of this type can
|
|
store an iterator referencing a segment. Passed to the
|
|
|PM_overlayer<>::create(...)| method it stores the corresponding
|
|
edge after the executed overlay as its member
|
|
|D._e|. |Link_to_iterator| also initializes all marks of the newly
|
|
created skeleton objects.
|
|
|
|
\begin{ignoreindiss}
|
|
We show more details. We allow also degenerate segments. We associate
|
|
a halfedge or vertex to an input segment |s = *it| referenced by an
|
|
iterator |it|. If the segment |s| is trivial it will be associated to
|
|
a vertex, else it is associated to a halfedge. The data accessor
|
|
initializes the marks of the newly created skeleton objects to |m|
|
|
(defined in the construction). For the concept of the data accessor
|
|
see the manual page of |PM_overlayer<>::create(...)|.
|
|
<<nef protected members>>=
|
|
struct Link_to_iterator {
|
|
const Decorator& D;
|
|
Halfedge_handle _e;
|
|
Vertex_handle _v;
|
|
ES_iterator _it;
|
|
Mark _m;
|
|
Link_to_iterator(const Decorator& d, ES_iterator it, Mark m) :
|
|
D(d), _e(), _v(), _it(it), _m(m) {}
|
|
|
|
void supporting_segment(Halfedge_handle e, ES_iterator it)
|
|
{ if ( it == _it ) _e = e; D.mark(e) = _m; }
|
|
void trivial_segment(Vertex_handle v, ES_iterator it)
|
|
{ if ( it == _it ) _v = v; D.mark(v) = _m; }
|
|
void starting_segment(Vertex_handle v, ES_iterator)
|
|
{ D.mark(v) = _m; }
|
|
void passing_segment(Vertex_handle v, ES_iterator)
|
|
{ D.mark(v) = _m; }
|
|
void ending_segment(Vertex_handle v, ES_iterator)
|
|
{ D.mark(v) = _m; }
|
|
|
|
};
|
|
|
|
@ \end{ignoreindiss}
|
|
\repdiss{}{We do not show the creation of the empty set or the full
|
|
plane. This is just a trivial case of the following half-plane
|
|
construction.}
|
|
\begin{ignoreindiss}
|
|
We add the box edges to |L|, overlay them and set the second face
|
|
(inside the frame box) to the |plane| mark. We know from the output
|
|
properties of |Overlayer| that the first face object is always the
|
|
one surrounding the frame.
|
|
<<nef interface operations>>=
|
|
/*{\Mcreation 3}*/
|
|
Nef_polyhedron_2(Content plane = EMPTY) : Base(Nef_rep())
|
|
/*{\Mcreate creates an instance |\Mvar| of type |\Mname|
|
|
and initializes it to the empty set if |plane == EMPTY|
|
|
and to the whole plane if |plane == COMPLETE|.}*/
|
|
{
|
|
ES_list L;
|
|
fill_with_frame_segs(L);
|
|
Overlayer D(pm());
|
|
Link_to_iterator I(D, --L.end(), false);
|
|
D.create(L.begin(),L.end(),I);
|
|
D.mark(++D.faces_begin()) = bool(plane);
|
|
}
|
|
|
|
@ \end{ignoreindiss} We come to the construction of a half-plane. A
|
|
user describes an open or closed half-plane by an oriented line
|
|
|l|. To create a plane map representing it we overlay a frame box plus
|
|
an extended segment splitting the box into two faces along |l|. The
|
|
overlayer module |PM_overlayer<>::create(...)| creates the plane map
|
|
(including faces) out of the list of extended segments passed to it
|
|
where no object is selected (concerning membership). The data
|
|
accessor |Link_to_iterator I| obtains the edge |I._e| of |pm()|
|
|
corresponding to |--L.end()| (the iterator pointing to the extended
|
|
segment, which is the line) during the |create| phase. We can use that
|
|
edge to mark its adjacent face and the edge itself according to the
|
|
|line| flag.
|
|
<<nef interface operations>>=
|
|
|
|
Nef_polyhedron_2(const Line& l, Boundary line = INCLUDED) : Base(Nef_rep())
|
|
/*{\Mcreate creates a Nef polyhedron |\Mvar| containing the half-plane
|
|
left of |l| including |l| if |line==INCLUDED|, excluding |l| if
|
|
|line==EXCLUDED|.}*/
|
|
{ TRACEN("Nconstruction from line "<<l);
|
|
ES_list L;
|
|
fill_with_frame_segs(L);
|
|
Extended_point ep1 = EK.construct_opposite_point(l);
|
|
Extended_point ep2 = EK.construct_point(l);
|
|
L.push_back(EK.construct_segment(ep1,ep2));
|
|
Overlayer D(pm());
|
|
Link_to_iterator I(D, --L.end(), false);
|
|
D.create(L.begin(),L.end(),I);
|
|
CGAL_assertion( I._e != Halfedge_handle() );
|
|
Halfedge_handle el = I._e;
|
|
if ( D.point(D.target(el)) != EK.target(L.back()) )
|
|
el = D.twin(el);
|
|
D.mark(D.face(el)) = true;
|
|
D.mark(el) = bool(line);
|
|
}
|
|
|
|
@ The construction of a simple polygon defined by an iterator range of
|
|
standard affine points (the value type of |Forward_iterator| is
|
|
|Point|) follows the same idea, however we also accept degenerate
|
|
polygons.
|
|
|
|
The iterator range of points can present us with the following cases:
|
|
(1) the list is empty, (2) the list has only one point, (3) the list
|
|
contains at least two points spanning line segments where the
|
|
following cases and problems can occur: (a) the segment(s) has (have)
|
|
affine dimension 1 (the hull is a segment). (b) the segments enclose
|
|
a simple polygon. (c) the segments enclose no simple polygon (touching
|
|
or intersecting inside). We cover one point, two points spanning a
|
|
segment and $n$ points spanning a simple polygon (which we do not
|
|
check). The construction of the plane map still succeeds when |P| is
|
|
not simple as the overlayer module just constructs the planar
|
|
subdivision implied by the segments in the iterator range. However,
|
|
the face marks will in general be incorrect.
|
|
<<nef interface operations>>=
|
|
|
|
template <class Forward_iterator>
|
|
Nef_polyhedron_2(Forward_iterator it, Forward_iterator end,
|
|
Boundary b = INCLUDED) : Base(Nef_rep())
|
|
/*{\Mcreate creates a Nef polyhedron |\Mvar| from the simple polygon
|
|
|P| spanned by the list of points in the iterator range |[it,end)| and
|
|
including its boundary if |b = INCLUDED| and excluding the boundary
|
|
otherwise. |Forward_iterator| has to be an iterator with value type
|
|
|Point|. This construction expects that |P| is simple. The degenerate
|
|
cases where |P| contains no point, one point or spans just one segment
|
|
(two points) are correctly handled. In all degenerate cases there's
|
|
only one unbounded face adjacent to the degenerate polygon. If |b ==
|
|
INCLUDED| then |\Mvar| is just the boundary. If |b == EXCLUDED| then
|
|
|\Mvar| is the whole plane without the boundary.}*/
|
|
{
|
|
ES_list L;
|
|
fill_with_frame_segs(L);
|
|
bool empty = false;
|
|
if (it != end)
|
|
<<fill segment list L>>
|
|
else empty = true;
|
|
Overlayer D(pm());
|
|
Link_to_iterator I(D, --L.end(), true);
|
|
D.create(L.begin(),L.end(),I);
|
|
<<mark face and boundary>>
|
|
}
|
|
|
|
@ We fill |L| with the segments cyclically spanned by the points in
|
|
the input iterator range.
|
|
<<fill segment list L>>=
|
|
{
|
|
Extended_point ef, ep = ef = EK.construct_point(*it);
|
|
Forward_iterator itl=it; ++itl;
|
|
if (itl == end) // case only one point
|
|
L.push_back(EK.construct_segment(ep,ep));
|
|
else { // at least one segment
|
|
while( itl != end ) {
|
|
Extended_point en = EK.construct_point(*itl);
|
|
L.push_back(EK.construct_segment(ep,en));
|
|
ep = en; ++itl;
|
|
}
|
|
L.push_back(EK.construct_segment(ep,ef));
|
|
}
|
|
}
|
|
|
|
@ We create the marks via the object stored in the |Link_to_iterator|
|
|
object |I|. The object is determined by the last segment |s| in
|
|
|L|. If that segment is trivial then |I. _v| stores the corresponding
|
|
vertex. If |s| is non-trivial then |I. _e| contains the edge supported
|
|
by the segment. We only have to extract the correct halfedge of the
|
|
edge twins. Then, we can mark the face and the boundary accordingly.
|
|
<<mark face and boundary>>=
|
|
if ( empty ) {
|
|
D.mark(++D.faces_begin()) = !bool(b); return; }
|
|
CGAL_assertion( I._e != Halfedge_handle() || I._v != Vertex_handle() );
|
|
if ( EK.is_degenerate(L.back()) ) {
|
|
CGAL_assertion(I._v != Vertex_handle());
|
|
D.mark(D.face(I._v)) = !bool(b); D.mark(I._v) = b;
|
|
} else {
|
|
Halfedge_handle el = I._e;
|
|
if ( D.point(D.target(el)) != EK.target(L.back()) )
|
|
el = D.twin(el);
|
|
D.set_marks_in_face_cycle(el,bool(b));
|
|
if ( D.number_of_faces() > 2 ) D.mark(D.face(el)) = true;
|
|
else D.mark(D.face(el)) = !bool(b);
|
|
}
|
|
clear_outer_face_cycle_marks();
|
|
|
|
|
|
@ \repdiss{}{We leave out the simple implementation of copy
|
|
construction, cloning and basic operations like |clear()|,
|
|
|is_empty()|, and |is_plane()|.}
|
|
\begin{ignoreindiss}
|
|
Now the standard copy construction, assignment and destruction.
|
|
The first two are delegated to the |Handle| type.
|
|
<<nef interface operations>>=
|
|
Nef_polyhedron_2(const Nef_polyhedron_2<T>& N1) : Base(N1) {}
|
|
Nef_polyhedron_2& operator=(const Nef_polyhedron_2<T>& N1)
|
|
{ Base::operator=(N1); return (*this); }
|
|
~Nef_polyhedron_2() {}
|
|
|
|
#ifndef _MSC_VER
|
|
|
|
template <class Forward_iterator>
|
|
Nef_polyhedron_2(Forward_iterator first, Forward_iterator beyond,
|
|
double p) : Base(Nef_rep())
|
|
/*{\Xcreate creates a random Nef polyhedron from the arrangement of
|
|
the set of lines |S = set[first,beyond)|. The cells of the arrangement
|
|
are selected uniformly at random with probability $p$. \precond $0 < p
|
|
< 1$.}*/
|
|
{ CGAL_assertion(0<p && p<1);
|
|
ES_list L; fill_with_frame_segs(L);
|
|
while ( first != beyond ) {
|
|
Extended_point ep1 = EK.construct_opposite_point(*first);
|
|
Extended_point ep2 = EK.construct_point(*first);
|
|
L.push_back(EK.construct_segment(ep1,ep2)); ++first;
|
|
}
|
|
Overlayer D(pm());
|
|
Link_to_iterator I(D, --L.end(), false);
|
|
D.create(L.begin(),L.end(),I);
|
|
|
|
Vertex_iterator v; Halfedge_iterator e; Face_iterator f;
|
|
for (v = D.vertices_begin(); v != D.vertices_end(); ++v)
|
|
D.mark(v) = ( default_random.get_double() < p ? true : false );
|
|
for (e = D.halfedges_begin(); e != D.halfedges_end(); ++(++e))
|
|
D.mark(e) = ( default_random.get_double() < p ? true : false );
|
|
for (f = D.faces_begin(); f != D.faces_end(); ++f)
|
|
D.mark(f) = ( default_random.get_double() < p ? true : false );
|
|
D.simplify(Except_frame_box_edges(pm()));
|
|
clear_outer_face_cycle_marks();
|
|
}
|
|
|
|
#endif
|
|
|
|
@ The construction from a plane map |H| is a clone action. We create a
|
|
representation object which has the same topology, geometry and
|
|
attributes as |H|.
|
|
<<nef interface operations>>=
|
|
protected:
|
|
Nef_polyhedron_2(const Plane_map& H, bool clone=true) : Base(Nef_rep())
|
|
/*{\Xcreate makes |\Mvar| a new object. If |clone==true| then the
|
|
underlying structure of |H| is copied into |\Mvar|.}*/
|
|
{ if (clone) {
|
|
Decorator D(pm()); // a decorator working on the rep plane map
|
|
D.clone(H); // cloning H into pm()
|
|
}
|
|
}
|
|
void clone_rep() { *this = Nef_polyhedron_2<T>(pm()); }
|
|
|
|
@ And now for the standard operations. We expect that the underlying
|
|
plane map structure is simplified as described in the |PM_overlayer<>|
|
|
module. Then for the simple configurations \emph{empty} and
|
|
\emph{plane} the plane map has to be just the frame of four vertices,
|
|
four uedges and two faces.
|
|
<<nef interface operations>>=
|
|
/*{\Moperations 4 3 }*/
|
|
public:
|
|
|
|
void clear(Content plane = EMPTY)
|
|
{ *this = Nef_polyhedron_2(plane); }
|
|
/*{\Mop makes |\Mvar| the empty set if |plane == EMPTY| and the
|
|
full plane if |plane == COMPLETE|.}*/
|
|
|
|
bool is_empty() const
|
|
/*{\Mop returns true if |\Mvar| is empty, false otherwise.}*/
|
|
{ Const_decorator D(pm());
|
|
Face_const_iterator f = D.faces_begin();
|
|
return (D.number_of_vertices()==4 &&
|
|
D.number_of_edges()==4 &&
|
|
D.number_of_faces()==2 &&
|
|
D.mark(++f) == false);
|
|
}
|
|
|
|
bool is_plane() const
|
|
/*{\Mop returns true if |\Mvar| is the whole plane, false otherwise.}*/
|
|
{ Const_decorator D(pm());
|
|
Face_const_iterator f = D.faces_begin();
|
|
return (D.number_of_vertices()==4 &&
|
|
D.number_of_edges()==4 &&
|
|
D.number_of_faces()==2 &&
|
|
D.mark(++f) == true);
|
|
}
|
|
|
|
@ \end{ignoreindiss}
|
|
\subsection{Unary Operations}\label{unary operations}
|
|
|
|
For the unary operations on Nef polyhedra we implement several
|
|
class-modifying methods. They can be chained together into larger
|
|
units. We thereby save cloning operations if the representation
|
|
object is only referenced by one handle. Note that the first line of
|
|
any modifying operation has to check if the representation object is
|
|
shared by several handles. If the representation object is shared,
|
|
the plane map has to be cloned before modification. We first implement
|
|
the three operations $\cpl$, $\int$, and $\bd$.
|
|
|
|
As our planar Nef polyhedra completely partition the plane, the
|
|
complement operation is easy to implement by an inversion (Boolean
|
|
flip) of the selection markers. Note that this conforms to the result
|
|
\cite[theorem 6;14]{nef:polybook} in which Nef showed that the
|
|
low-dimensional faces of $P$ and $\cpl P$ (in their common boundary)
|
|
are the same and the full-dimensional faces that are part of $P$ or
|
|
$\cpl P$ obviously exchange their role (if non-empty). The local
|
|
pyramid in any point $x$ of the plane is inverted by the Boolean flips
|
|
according to \cite[theorem 3;15]{nef:polybook} $(\cpl P)^x = \cpl
|
|
P^x$. Due to the special role\footnote{No affine object can be placed
|
|
on or outside the infimaximal frame.} of the outer face and the edges
|
|
and vertices that are part of the frame box we keep all objects of the
|
|
frame unmarked (operation |clear_outer_face_cycle_marks|). Note that
|
|
just by flipping we do not spoil the local views properties as
|
|
specified in Lemma \ref{local view properties}. Thus, we do not have
|
|
to simplify here.
|
|
<<nef interface operations>>=
|
|
void extract_complement()
|
|
{ TRACEN("extract complement");
|
|
if ( is_shared() ) clone_rep();
|
|
Overlayer D(pm());
|
|
Vertex_iterator v, vend = D.vertices_end();
|
|
for(v = D.vertices_begin(); v != vend; ++v) D.mark(v) = !D.mark(v);
|
|
Halfedge_iterator e, eend = D.halfedges_end();
|
|
for(e = D.halfedges_begin(); e != eend; ++(++e)) D.mark(e) = !D.mark(e);
|
|
Face_iterator f, fend = D.faces_end();
|
|
for(f = D.faces_begin(); f != fend; ++f) D.mark(f) = !D.mark(f);
|
|
clear_outer_face_cycle_marks();
|
|
}
|
|
|
|
@ The interior $\int P$ of a point set $P$ is the set of all points
|
|
where an open ball of infinitesimal radius (a neighborhood) is
|
|
contained in the point set. This is not the case for all
|
|
low-dimensional faces of the plane map. Accordingly (see also
|
|
\cite[theorem 3;19]{nef:polybook}), we have to keep all selected
|
|
full-dimensional faces of $P$ and all objects of the $1$-skeleton have
|
|
to be deselected. Afterwards the simplification operation minimizes
|
|
the structure and makes it again consistent with Definition \ref{data
|
|
type definition}. For example, marked isolated vertices within
|
|
non-marked faces and marked edges within such faces are first unmarked
|
|
and then deleted in the |simplify()| operation. The simplification
|
|
operation has to exempt edges of the infimaximal frame. An object
|
|
|Except_frame_box_edges(P)| has a function operator method |bool
|
|
operator()(Halfedge_handle e)|that returns true iff the edge |e| of
|
|
the plane map |P| is part of the frame box. Thereby within |simplify|
|
|
a removal of edges is only executed on edges that partition the
|
|
interior of the frame box.
|
|
<<nef interface operations>>=
|
|
void extract_interior()
|
|
{ TRACEN("extract interior");
|
|
if ( is_shared() ) clone_rep();
|
|
Overlayer D(pm());
|
|
Vertex_iterator v, vend = D.vertices_end();
|
|
for(v = D.vertices_begin(); v != vend; ++v) D.mark(v) = false;
|
|
Halfedge_iterator e, eend = D.halfedges_end();
|
|
for(e = D.halfedges_begin(); e != eend; ++(++e)) D.mark(e) = false;
|
|
D.simplify(Except_frame_box_edges(pm()));
|
|
}
|
|
|
|
|
|
@ The boundary of a point set $P$ is defined to be the intersection
|
|
$\clos P \cap \clos\cpl P$. This is the set of all points that have a
|
|
nonempty neighborhood with $\int P$ or $\ext P$. Any point $x$ on the
|
|
$1$-skeleton of the plane map has this property due to the properties of
|
|
its local pyramid $P^x$. The boundary $\bd P$ is thus obtained by
|
|
selecting all low-dimensional objects and then deselecting all
|
|
2-faces. Finally we cope with the frame and simplify the structure.
|
|
<<nef interface operations>>=
|
|
void extract_boundary()
|
|
{ TRACEN("extract boundary");
|
|
if ( is_shared() ) clone_rep();
|
|
Overlayer D(pm());
|
|
Vertex_iterator v, vend = D.vertices_end();
|
|
for(v = D.vertices_begin(); v != vend; ++v) D.mark(v) = true;
|
|
Halfedge_iterator e, eend = D.halfedges_end();
|
|
for(e = D.halfedges_begin(); e != eend; ++(++e)) D.mark(e) = true;
|
|
Face_iterator f, fend = D.faces_end();
|
|
for(f = D.faces_begin(); f != fend; ++f) D.mark(f) = false;
|
|
clear_outer_face_cycle_marks();
|
|
D.simplify(Except_frame_box_edges(pm()));
|
|
}
|
|
|
|
@ Now we use the above operations for \emph{closure} and
|
|
\emph{regularization}. The closure of $P$ can be reduced to the
|
|
operations \emph{interior} and \emph{complement} as $\clos P =
|
|
\cpl\int\cpl P$. The regularization of $P$ is defined as $\clos\int
|
|
P$.
|
|
<<nef interface operations>>=
|
|
void extract_closure()
|
|
/*{\Xop converts |\Mvar| to its closure. }*/
|
|
{ TRACEN("extract closure");
|
|
extract_complement();
|
|
extract_interior();
|
|
extract_complement();
|
|
}
|
|
|
|
void extract_regularization()
|
|
/*{\Xop converts |\Mvar| to its regularization. }*/
|
|
{ TRACEN("extract regularization");
|
|
extract_interior();
|
|
extract_closure();
|
|
}
|
|
|
|
@ The constructive interface methods are just mapped to the
|
|
corresponding extract methods.
|
|
<<nef interface operations>>=
|
|
/*{\Mtext \headerline{Constructive Operations}}*/
|
|
|
|
Nef_polyhedron_2<T> complement() const
|
|
/*{\Mop returns the complement of |\Mvar| in the plane.}*/
|
|
{ Nef_polyhedron_2<T> res = *this;
|
|
res.extract_complement();
|
|
return res;
|
|
}
|
|
|
|
@ All other operations like |interior()|, |closure()|, |boundary()|,
|
|
and |regularization()| are implemented similarly.
|
|
\begin{ignoreindiss}
|
|
<<nef interface operations>>=
|
|
|
|
Nef_polyhedron_2<T> interior() const
|
|
/*{\Mop returns the interior of |\Mvar|.}*/
|
|
{ Nef_polyhedron_2<T> res = *this;
|
|
res.extract_interior();
|
|
return res;
|
|
}
|
|
|
|
Nef_polyhedron_2<T> closure() const
|
|
/*{\Mop returns the closure of |\Mvar|.}*/
|
|
{ Nef_polyhedron_2<T> res = *this;
|
|
res.extract_closure();
|
|
return res;
|
|
}
|
|
|
|
Nef_polyhedron_2<T> boundary() const
|
|
/*{\Mop returns the boundary of |\Mvar|.}*/
|
|
{ Nef_polyhedron_2<T> res = *this;
|
|
res.extract_boundary();
|
|
return res;
|
|
}
|
|
|
|
Nef_polyhedron_2<T> regularization() const
|
|
/*{\Mop returns the regularized polyhedron (closure of interior).}*/
|
|
{ Nef_polyhedron_2<T> res = *this;
|
|
res.extract_regularization();
|
|
return res;
|
|
}
|
|
|
|
@ \end{ignoreindiss}
|
|
|
|
\subsection{Binary Set Operations}\label{binary set operations}
|
|
|
|
We follow Rossignac and O'Connor \cite{ro:SGC} and split the binary
|
|
operations into three phases: subdivision, selection, and
|
|
simplification. \repdiss{}{For the implementation see the module
|
|
|PM_overlayer<>| in Section \ref{overlay module}.} The subdivision
|
|
phase creates the overlay of the two input structures. This overlay
|
|
has the property that each object (vertex, edge, face) has exactly one
|
|
object from each input structure that supports it. \repdiss{}{This
|
|
proposition is proven in Lemma \ref{supporting lemma}.} After the
|
|
subdivison each object of the resulting plane map knows its mark in
|
|
each of the two input structures and can thereby be qualified with
|
|
respect to each input structure. The binary set operation is then
|
|
reduced to a Boolean predicate on these marks. The resulting structure
|
|
can be a planar partition that is not a legal Nef polyhedron due to
|
|
the fact that plane map boundary objects can have local views that
|
|
contradict Lemma \ref{local view properties}. Violations are fixed in
|
|
the simplification phase without changing the represented point set.
|
|
This simplification makes the plane map representation minimal with
|
|
respect to the number of its objects and again consistent with
|
|
Definition \ref{data type definition}.
|
|
|
|
The above scheme refers to the theory as presented by Nef who showed
|
|
the following general lemma.
|
|
\begin{lemma}\label{intersection lemma}
|
|
Let $P_0$, $P_1$ be polyhedra. Then every face of $P = P_0 \cap P_1$
|
|
is the union of intersections of faces of $P_0$ and $P_1$.
|
|
\end{lemma}
|
|
The proof follows \cite[Satz 6;16]{nef:polybook}. As a consequence
|
|
the simplification just unions objects within $P$ to form the
|
|
connected components of Nef faces.
|
|
|
|
To implement the binary set operations we use functors\footnote{a
|
|
short form for function objects.} that carry the underlying Boolean
|
|
logic.
|
|
<<boolean classes>>=
|
|
struct AND { bool operator()(bool b1, bool b2) const { return b1&&b2; } };
|
|
struct OR { bool operator()(bool b1, bool b2) const { return b1||b2; } };
|
|
struct DIFF { bool operator()(bool b1, bool b2) const { return b1&&!b2; } };
|
|
struct XOR { bool operator()(bool b1, bool b2) const
|
|
{ return (b1&&!b2)||(!b1&&b2); } };
|
|
|
|
<<nef interface operations>>=
|
|
|
|
Nef_polyhedron_2<T> intersection(const Nef_polyhedron_2<T>& N1) const
|
|
/*{\Mop returns |\Mvar| $\cap$ |N1|. }*/
|
|
{ Nef_polyhedron_2<T> res(pm(),false); // empty, no frame
|
|
Overlayer D(res.pm());
|
|
D.subdivide(pm(),N1.pm());
|
|
AND _and; D.select(_and);
|
|
res.clear_outer_face_cycle_marks();
|
|
D.simplify(Except_frame_box_edges(res.pm()));
|
|
return res;
|
|
}
|
|
|
|
@ Join, difference, and symmetric difference follow similar schemes
|
|
based on |OR|, |DIFF|, and |XOR|.
|
|
\begin{ignoreindiss}
|
|
<<nef interface operations>>=
|
|
|
|
Nef_polyhedron_2<T> join(const Nef_polyhedron_2<T>& N1) const
|
|
/*{\Mop returns |\Mvar| $\cup$ |N1|. }*/
|
|
{ Nef_polyhedron_2<T> res(pm(),false); // empty, no frame
|
|
Overlayer D(res.pm());
|
|
D.subdivide(pm(),N1.pm());
|
|
OR _or; D.select(_or);
|
|
res.clear_outer_face_cycle_marks();
|
|
D.simplify(Except_frame_box_edges(res.pm()));
|
|
return res;
|
|
}
|
|
|
|
Nef_polyhedron_2<T> difference(const Nef_polyhedron_2<T>& N1) const
|
|
/*{\Mop returns |\Mvar| $-$ |N1|. }*/
|
|
{ Nef_polyhedron_2<T> res(pm(),false); // empty, no frame
|
|
Overlayer D(res.pm());
|
|
D.subdivide(pm(),N1.pm());
|
|
DIFF _diff; D.select(_diff);
|
|
res.clear_outer_face_cycle_marks();
|
|
D.simplify(Except_frame_box_edges(res.pm()));
|
|
return res;
|
|
}
|
|
|
|
Nef_polyhedron_2<T> symmetric_difference(
|
|
const Nef_polyhedron_2<T>& N1) const
|
|
/*{\Mop returns the symmectric difference |\Mvar - T| $\cup$
|
|
|T - \Mvar|. }*/
|
|
{ Nef_polyhedron_2<T> res(pm(),false); // empty, no frame
|
|
Overlayer D(res.pm());
|
|
D.subdivide(pm(),N1.pm());
|
|
XOR _xor; D.select(_xor);
|
|
res.clear_outer_face_cycle_marks();
|
|
D.simplify(Except_frame_box_edges(res.pm()));
|
|
return res;
|
|
}
|
|
|
|
#if 0
|
|
Nef_polyhedron_2<T> transform(const Aff_transformation& t) const
|
|
/*{\Mop returns $t(|\Mvar|)$.}*/
|
|
{ Nef_polyhedron_2<T> res(pm()); // cloned
|
|
Transformer PMT(res.pm());
|
|
PMT.transform(t);
|
|
return res;
|
|
}
|
|
#endif
|
|
|
|
|
|
@ The first face object is the one outside the bounding frame. Thus
|
|
it's only hole face cycle consists of the edges of the frame.
|
|
<<nef protected members>>=
|
|
void clear_outer_face_cycle_marks()
|
|
{ // unset all frame marks
|
|
Decorator D(pm());
|
|
Face_iterator f = D.faces_begin();
|
|
D.mark(f) = false;
|
|
Halfedge_handle e = D.holes_begin(f);
|
|
D.set_marks_in_face_cycle(e, false);
|
|
}
|
|
|
|
@ \end{ignoreindiss} \repdiss{}{We do not show the implementation of
|
|
the operators |*,+,-,^,!| which map to the above operations.}
|
|
@ \begin{ignoreindiss}
|
|
<<nef interface operations>>=
|
|
/*{\Mtext Additionally there are operators |*,+,-,^,!| which
|
|
implement the binary operations \emph{intersection}, \emph{union},
|
|
\emph{difference}, \emph{symmetric difference}, and the unary
|
|
operation \emph{complement} respectively. There are also the
|
|
corresponding modification operations |*=,+=,-=,^=|.}*/
|
|
|
|
Nef_polyhedron_2<T> operator*(const Nef_polyhedron_2<T>& N1) const
|
|
{ return intersection(N1); }
|
|
|
|
Nef_polyhedron_2<T> operator+(const Nef_polyhedron_2<T>& N1) const
|
|
{ return join(N1); }
|
|
|
|
Nef_polyhedron_2<T> operator-(const Nef_polyhedron_2<T>& N1) const
|
|
{ return difference(N1); }
|
|
|
|
Nef_polyhedron_2<T> operator^(const Nef_polyhedron_2<T>& N1) const
|
|
{ return symmetric_difference(N1); }
|
|
|
|
Nef_polyhedron_2<T> operator!() const
|
|
{ return complement(); }
|
|
|
|
Nef_polyhedron_2<T>& operator*=(const Nef_polyhedron_2<T>& N1)
|
|
{ this = intersection(N1); return *this; }
|
|
|
|
Nef_polyhedron_2<T>& operator+=(const Nef_polyhedron_2<T>& N1)
|
|
{ this = join(N1); return *this; }
|
|
|
|
Nef_polyhedron_2<T>& operator-=(const Nef_polyhedron_2<T>& N1)
|
|
{ this = difference(N1); return *this; }
|
|
|
|
Nef_polyhedron_2<T>& operator^=(const Nef_polyhedron_2<T>& N1)
|
|
{ this = symmetric_difference(N1); return *this; }
|
|
|
|
@ \end{ignoreindiss}
|
|
\subsection{Binary Comparison Operations}
|
|
|
|
All set comparison operations are reduced to binary operations
|
|
followed by an empty-set test. For two Nef polyhedra $P_1$, $P_2$
|
|
it holds
|
|
\begin{eqnarray*}
|
|
P_1 = P_2 & \Leftrightarrow &
|
|
\mathit{symmetric\_difference}(P_1,P_2) = \emptyset\\
|
|
P_1 \subseteq P_2 & \Leftrightarrow &
|
|
\mathit{difference}(P_1,P_2) = \emptyset\\
|
|
P_1 \varsubsetneq P_2 & \Leftrightarrow &
|
|
\mathit{difference}(P_1,P_2) = \emptyset \ \ \AND\ \
|
|
\mathit{difference}(P_2,P_1) \neq \emptyset
|
|
\end{eqnarray*}
|
|
In our specification $\subseteq$ is |operator<=|, and $\varsubsetneq$
|
|
is |operator<|. The other operations are symmetric.
|
|
<<nef interface operations>>=
|
|
/*{\Mtext There are also comparison operations like |<,<=,>,>=,==,!=|
|
|
which implement the relations subset, subset or equal, superset, superset
|
|
or equal, equality, inequality, respectively.}*/
|
|
|
|
bool operator==(const Nef_polyhedron_2<T>& N1) const
|
|
{ return symmetric_difference(N1).is_empty(); }
|
|
|
|
bool operator!=(const Nef_polyhedron_2<T>& N1) const
|
|
{ return !operator==(N1); }
|
|
|
|
bool operator<=(const Nef_polyhedron_2<T>& N1) const
|
|
{ return difference(N1).is_empty(); }
|
|
|
|
bool operator<(const Nef_polyhedron_2<T>& N1) const
|
|
{ return difference(N1).is_empty() && !N1.difference(*this).is_empty(); }
|
|
|
|
@ \begin{ignoreindiss}
|
|
<<nef interface operations>>=
|
|
bool operator>=(const Nef_polyhedron_2<T>& N1) const
|
|
{ return N1.difference(*this).is_empty(); }
|
|
|
|
bool operator>(const Nef_polyhedron_2<T>& N1) const
|
|
{ return N1.difference(*this).is_empty() && !difference(N1).is_empty(); }
|
|
|
|
|
|
@ \end{ignoreindiss}
|
|
|
|
\subsection{Point location and Ray shooting}
|
|
|
|
Let |P| be the plane map underlying our Nef polyhedron stored in the
|
|
|*this| object. The result of a point location query with an affine
|
|
point |p| is the object of |P| whose embedding contains |p|. Ray
|
|
shooting queries come in two flavors. One variant starts the ray shot
|
|
in a point |p| and determines the closest object of |P| in direction
|
|
|d| that is in the set (determined by the selection mark). The other
|
|
variant determines the closest $1$-skeleton object in direction |d|.
|
|
The point location and ray shooting functionality is taken from the
|
|
two point location classes |PM_point_locator<>| and
|
|
|PM_naive_point_locator<>|. All operations can choose between the two
|
|
approaches by a mode flag |m|. The default is location as implemented
|
|
by |PM_point_locator<>|. That class uses a further subdivision of |P|
|
|
by a locally minimized weight constrained triangulations (LMWT) to
|
|
allow so-called segment walks. The LMWT is calculated on demand, when
|
|
the first point location or ray shooting operation is called. The
|
|
naive point location method is based on a global examination of all
|
|
objects of |P| to find the one that contains |p|.
|
|
\begin{ignoreindiss}
|
|
<<nef interface operations>>=
|
|
/*{\Mtext \headerline{Exploration - Point location - Ray shooting}
|
|
As Nef polyhedra are the result of forming complements
|
|
and intersections starting from a set |H| of half-spaces that are
|
|
defined by oriented lines in the plane, they can be represented by
|
|
an attributed plane map $M = (V,E,F)$. For topological queries
|
|
within |M| the following types and operations allow exploration
|
|
access to this structure.}*/
|
|
|
|
/*{\Mtypes 3}*/
|
|
typedef Const_decorator Topological_explorer;
|
|
|
|
typedef CGAL::PM_explorer<Const_decorator,T> Explorer;
|
|
/*{\Mtypemember a decorator to examine the underlying plane map.
|
|
See the manual page of |Explorer|.}*/
|
|
|
|
typedef typename Locator::Object_handle Object_handle;
|
|
/*{\Mtypemember a generic handle to an object of the underlying
|
|
plane map. The kind of object |(vertex, halfedge, face)| can
|
|
be determined and the object can be assigned to a corresponding
|
|
handle by the three functions:\\
|
|
|bool assign(Vertex_const_handle& h, Object_handle)|\\
|
|
|bool assign(Halfedge_const_handle& h, Object_handle)|\\
|
|
|bool assign(Face_const_handle& h, Object_handle)|\\
|
|
where each function returns |true| iff the assignment to
|
|
|h| was done.}*/
|
|
|
|
enum Location_mode { DEFAULT, NAIVE, LMWT };
|
|
/*{\Menum selection flag for the point location mode.}*/
|
|
|
|
|
|
/*{\Moperations 3 1 }*/
|
|
|
|
void init_locator() const
|
|
{ const_cast<Self*>(this)->ptr()->init_locator(); }
|
|
const Locator& locator() const
|
|
{ assert(ptr()->pl_); return *(ptr()->pl_); }
|
|
|
|
@ \end{ignoreindiss} The type |Object_handle| is a polymorphic handle
|
|
that can reference vertices, edges, and faces. Conversion is done by
|
|
an |assign| operation similarly to the polymorphic CGAL type |Object|.
|
|
<<nef interface operations>>=
|
|
|
|
bool contains(Object_handle h) const
|
|
/*{\Mop returns true iff the object |h| is contained in the set
|
|
represented by |\Mvar|.}*/
|
|
{ Slocator PL(pm()); return PL.mark(h); }
|
|
|
|
bool contained_in_boundary(Object_handle h) const
|
|
/*{\Mop returns true iff the object |h| is contained in the $1$-skeleton
|
|
of |\Mvar|.}*/
|
|
{ Vertex_const_handle v;
|
|
Halfedge_const_handle e;
|
|
return ( CGAL::assign(v,h) || CGAL::assign(e,h) );
|
|
}
|
|
|
|
|
|
@ The chosen point location method depends on |m|. In the non-naive
|
|
case the locator object is initialized by a call to
|
|
|init_locator()|. The corresponding locator object is stored in the
|
|
representation object for further usage in iterated queries and can be
|
|
accessed by the |locator()| method. The naive locate operation
|
|
requires a segment as input that intersects the $1$-skeleton of the
|
|
plane map.
|
|
<<nef interface operations>>=
|
|
|
|
Object_handle locate(const Point& p, Location_mode m = DEFAULT) const
|
|
/*{\Mop returns a generic handle |h| to an object (face, halfedge, vertex)
|
|
of the underlying plane map that contains the point |p| in its relative
|
|
interior. The point |p| is contained in the set represented by |\Mvar| if
|
|
|\Mvar.contains(h)| is true. The location mode flag |m| allows one to choose
|
|
between different point location strategies.}*/
|
|
{
|
|
if (m == DEFAULT || m == LMWT) {
|
|
init_locator();
|
|
Extended_point ep = EK.construct_point(p);
|
|
return locator().locate(ep);
|
|
} else if (m == NAIVE) {
|
|
Slocator PL(pm(),EK);
|
|
Extended_segment s(EK.construct_point(p),
|
|
PL.point(PL.vertices_begin()));
|
|
return PL.locate(s);
|
|
}
|
|
CGAL_assertion_msg(0,"location mode not implemented.");
|
|
return Object_handle();
|
|
}
|
|
|
|
@ The ray shooting operation determines the closest object of |P| that
|
|
is marked and hit by a ray shot from the point |p| in direction |d|.
|
|
The search is delegated to the corresponding member of the locator
|
|
object. The class |INSET| is the predicate class that stops the ray
|
|
shot when a marked object is hit. See the manual page of the locator
|
|
classes for its concept.
|
|
<<nef interface operations>>=
|
|
struct INSET {
|
|
const Const_decorator& D;
|
|
INSET(const Const_decorator& Di) : D(Di) {}
|
|
bool operator()(Vertex_const_handle v) const { return D.mark(v); }
|
|
bool operator()(Halfedge_const_handle e) const { return D.mark(e); }
|
|
bool operator()(Face_const_handle f) const { return D.mark(f); }
|
|
};
|
|
|
|
Object_handle ray_shoot(const Point& p, const Direction& d,
|
|
Location_mode m = DEFAULT) const
|
|
/*{\Mop returns a handle |h| with |\Mvar.contains(h)| that can be
|
|
converted to a |Vertex_/Halfedge_/Face_const_handle| as described
|
|
above. The object returned is intersected by the ray starting in |p|
|
|
with direction |d| and has minimal distance to |p|. The operation
|
|
returns the null handle |NULL| if the ray shoot along |d| does not hit
|
|
any object |h| of |\Mvar| with |\Mvar.contains(h)|. The location mode
|
|
flag |m| allows one to choose between different point location
|
|
strategies.}*/
|
|
{
|
|
if (m == DEFAULT || m == LMWT) {
|
|
init_locator();
|
|
Extended_point ep = EK.construct_point(p),
|
|
eq = EK.construct_point(p,d);
|
|
return locator().ray_shoot(EK.construct_segment(ep,eq),
|
|
INSET(locator()));
|
|
} else if (m == NAIVE) {
|
|
Slocator PL(pm(),EK);
|
|
Extended_point ep = EK.construct_point(p),
|
|
eq = EK.construct_point(p,d);
|
|
return PL.ray_shoot(EK.construct_segment(ep,eq),INSET(PL));
|
|
}
|
|
CGAL_assertion_msg(0,"location mode not implemented.");
|
|
return Object_handle();
|
|
}
|
|
|
|
@ A similar implementation is used for |ray_shoot_to_boundary|. Note
|
|
that we only use a different predicate |INSKEL|.
|
|
<<nef interface operations>>=
|
|
struct INSKEL {
|
|
bool operator()(Vertex_const_handle) const { return true; }
|
|
bool operator()(Halfedge_const_handle) const { return true; }
|
|
bool operator()(Face_const_handle) const { return false; }
|
|
};
|
|
|
|
Object_handle ray_shoot_to_boundary(const Point& p, const Direction& d,
|
|
Location_mode m = DEFAULT) const
|
|
/*{\Mop returns a handle |h| that can be converted to a
|
|
|Vertex_/Halfedge_const_handle| as described above. The object
|
|
returned is part of the $1$-skeleton of |\Mvar|, intersected by the
|
|
ray starting in |p| with direction |d| and has minimal distance to
|
|
|p|. The operation returns the null handle |NULL| if the ray shoot
|
|
along |d| does not hit any $1$-skeleton object |h| of |\Mvar|. The
|
|
location mode flag |m| allows one to choose between different point
|
|
location strategies.}*/
|
|
{
|
|
if (m == DEFAULT || m == LMWT) {
|
|
init_locator();
|
|
Extended_point ep = EK.construct_point(p),
|
|
eq = EK.construct_point(p,d);
|
|
return locator().ray_shoot(EK.construct_segment(ep,eq),INSKEL());
|
|
} else if (m == NAIVE) {
|
|
Slocator PL(pm(),EK);
|
|
Extended_point ep = EK.construct_point(p),
|
|
eq = EK.construct_point(p,d);
|
|
return PL.ray_shoot(EK.construct_segment(ep,eq),INSKEL());
|
|
}
|
|
CGAL_assertion_msg(0,"location mode not implemented.");
|
|
return Object_handle();
|
|
}
|
|
|
|
@ To examine the plane map underlying the Nef polyhedron the user can
|
|
obtain a decorator object that has read-only access to |pm()|. Thus,
|
|
modifications can only take place via the interface operations of
|
|
|Nef_polydron_2|.
|
|
<<nef interface operations>>=
|
|
|
|
Explorer explorer() const { return Explorer(pm(),EK); }
|
|
/*{\Mop returns a decorator object which allows read-only access of
|
|
the underlying plane map. See the manual page |Explorer| for its
|
|
usage.}*/
|
|
|
|
|
|
/*{\Mtext\headerline{Input and Output}
|
|
A Nef polyhedron |\Mvar| can be visualized in a |Window_stream W|. The
|
|
output operator is defined in the file
|
|
|CGAL/IO/Nef_\-poly\-hedron_2_\-Win\-dow_\-stream.h|.
|
|
}*/
|
|
|
|
/*{\Mimplementation Nef polyhedra are implemented on top of a halfedge
|
|
data structure and use linear space in the number of vertices, edges
|
|
and facets. Operations like |empty| take constant time. The
|
|
operations |clear|, |complement|, |interior|, |closure|, |boundary|,
|
|
|regularization|, input and output take linear time. All binary set
|
|
operations and comparison operations take time $O(n \log n)$ where $n$
|
|
is the size of the output plus the size of the input.
|
|
|
|
The point location and ray shooting operations are implemented in
|
|
two flavors. The |NAIVE| operations run in linear query time without
|
|
any preprocessing, the |DEFAULT| operations (equals |LMWT|) run in
|
|
sub-linear query time, but preprocessing is triggered with the first
|
|
operation. Preprocessing takes time $O(N^2)$, the sub-linear point
|
|
location time is either logarithmic when LEDA's persistent
|
|
dictionaries are present or if not then the point location time is
|
|
worst-case linear, but experiments show often sublinear runtimes. Ray
|
|
shooting equals point location plus a walk in the constrained
|
|
triangulation overlayed on the plane map representation. The cost of
|
|
the walk is proportional to the number of triangles passed in
|
|
direction |d| until an obstacle is met. In a minimum weight
|
|
triangulation of the obstacles (the plane map representing the
|
|
polyhedron) the theory provides a $O(\sqrt{n})$ bound for the number
|
|
of steps. Our locally minimum weight triangulation approximates the
|
|
minimum weight triangulation only heuristically (the calculation of
|
|
the minimum weight triangulation is conjectured to be NP hard). Thus
|
|
we have no runtime guarantee but a strong experimental motivation for
|
|
its approximation.}*/
|
|
|
|
/*{\Mexample Nef polyhedra are parameterized by a so-called extended
|
|
geometric kernel. There are three kernels, one based on a homogeneous
|
|
representation of extended points called |Extended_homogeneous<RT>|
|
|
where |RT| is a ring type providing additionally a |gcd| operation and
|
|
one based on a cartesian representation of extended points called
|
|
|Extended_cartesian<NT>| where |NT| is a field type, and finally
|
|
|Filtered_extended_homogeneous<RT>| (an optimized version of the
|
|
first).
|
|
|
|
The member types of |Nef_polyhedron_2< Extended_homogeneous<NT> >|
|
|
map to corresponding types of the CGAL geometry kernel
|
|
(e.g. |Nef_polyhedron::Line| equals
|
|
|CGAL::Homogeneous<leda_integer>::Line_2| in the example below).
|
|
\begin{Mverb}
|
|
#include <CGAL/basic.h>
|
|
#include <CGAL/leda_integer.h>
|
|
#include <CGAL/Extended_homogeneous.h>
|
|
#include <CGAL/Nef_polyhedron_2.h>
|
|
|
|
using namespace CGAL;
|
|
typedef Extended_homogeneous<leda_integer> Extended_kernel;
|
|
typedef Nef_polyhedron_2<Extended_kernel> Nef_polyhedron;
|
|
typedef Nef_polyhedron::Line Line;
|
|
|
|
int main()
|
|
{
|
|
Nef_polyhedron N1(Line(1,0,0));
|
|
Nef_polyhedron N2(Line(0,1,0), Nef_polyhedron::EXCLUDED);
|
|
Nef_polyhedron N3 = N1 * N2; // line (*)
|
|
return 0;
|
|
}
|
|
\end{Mverb}
|
|
After line (*) |N3| is the intersection of |N1| and |N2|.}*/
|
|
|
|
@ \begin{ignoreindiss}
|
|
\subsection{The file wrapper}
|
|
<<Nef_polyhedron_2.h>>=
|
|
<<CGAL Header1>>
|
|
// file : include/CGAL/Nef_polyhedron_2.h
|
|
<<CGAL Header2>>
|
|
#ifndef CGAL_NEF_POLYHEDRON_2_H
|
|
#define CGAL_NEF_POLYHEDRON_2_H
|
|
|
|
#if defined(_MSC_VER) || defined(__BORLANDC__)
|
|
#define CGAL_SIMPLE_HDS
|
|
#endif
|
|
|
|
#include <CGAL/basic.h>
|
|
#include <CGAL/Handle_for.h>
|
|
#include <CGAL/Random.h>
|
|
#ifndef CGAL_SIMPLE_HDS
|
|
#include <CGAL/Nef_2/HDS_items.h>
|
|
#include <CGAL/HalfedgeDS_default.h>
|
|
#else
|
|
#include <CGAL/Nef_2/HalfedgeDS_default_MSC.h>
|
|
#endif
|
|
#include <CGAL/Nef_2/PM_explorer.h>
|
|
#include <CGAL/Nef_2/PM_decorator.h>
|
|
#include <CGAL/Nef_2/PM_io_parser.h>
|
|
#include <CGAL/Nef_2/PM_overlayer.h>
|
|
//#include <CGAL/Nef_2/PM_transformer.h>
|
|
#include <CGAL/Nef_2/PM_point_locator.h>
|
|
#include <vector>
|
|
#include <list>
|
|
|
|
#undef _DEBUG
|
|
#define _DEBUG 11
|
|
#include <CGAL/Nef_2/debug.h>
|
|
|
|
CGAL_BEGIN_NAMESPACE
|
|
|
|
<<nef polyhedron definition>>
|
|
<<nef polyhedron input and output>>
|
|
|
|
CGAL_END_NAMESPACE
|
|
|
|
#undef CGAL_SIMPLE_HDS
|
|
#endif //CGAL_NEF_POLYHEDRON_2_H
|
|
|
|
|
|
@ \end{ignoreindiss}
|
|
\subsection{Visualization}
|
|
|
|
In this section we provide a drawing routine for Nef polyhedra in a
|
|
LEDA window. We want to draw faces, edges, vertices in this order,
|
|
where faces are maximally connected point sets bounded by one outer
|
|
face cycle and maybe by several inner hole cycles. We draw objects
|
|
that are in our point set in black and objects that are not in our
|
|
point set with a light color. Note that we face the following
|
|
problem. Our window represents a rectangular view to our square frame,
|
|
which is large enough to make the topology on this boundary
|
|
constant. Imagine making our frame big enough and then shrinking it
|
|
slowly down to zero. For each ray with slope not equal to one and not
|
|
containing the origin there is a value $R$ when the ray tip on the
|
|
frame leaves its correct frame segment. If we want to prevent
|
|
topological difficulties when drawing the polyhedron we have to keep
|
|
our frame radius above the minimum $R_m$. Thus visualization
|
|
determines this $R_m$ and sets the internal evaluation parameter to
|
|
this value. Then all points on the frame and segments containing such
|
|
points have fixed coordinates and can be drawn. For the concrete
|
|
technical details of drawing the objects see the class
|
|
|PM_visualizor<>|.
|
|
<<window stream output>>=
|
|
static long frame_default = 100;
|
|
static bool show_triangulation = false;
|
|
|
|
template <typename T>
|
|
CGAL::Window_stream& operator<<(CGAL::Window_stream& ws,
|
|
const Nef_polyhedron_2<T>& P)
|
|
{
|
|
typedef Nef_polyhedron_2<T> Polyhedron;
|
|
typedef typename T::RT RT;
|
|
typedef typename T::Standard_RT Standard_RT;
|
|
typedef typename Polyhedron::Topological_explorer TExplorer;
|
|
typedef typename Polyhedron::Point Point;
|
|
typedef typename Polyhedron::Line Line;
|
|
typedef CGAL::PM_BooleColor<TExplorer> BooleColor;
|
|
typedef CGAL::PM_visualizor<TExplorer,T,BooleColor> Visualizor;
|
|
|
|
TExplorer D = P.explorer();
|
|
const T& E = Nef_polyhedron_2<T>::EK;
|
|
|
|
Standard_RT frame_radius = frame_default;
|
|
E.determine_frame_radius(D.points_begin(),D.points_end(),frame_radius);
|
|
RT::set_R(frame_radius);
|
|
Visualizor PMV(ws,D); PMV.draw_map();
|
|
<<draw the refining constrained triangulation>>
|
|
return ws;
|
|
}
|
|
|
|
@ Drawing of the constrained triangulation is done depending on the
|
|
static variable |show_triangulation|. Of course such animation
|
|
requires the necessary preprocessing with the locator object.
|
|
<<draw the refining constrained triangulation>>=
|
|
if (show_triangulation) {
|
|
P.init_locator();
|
|
Visualizor V(ws,P.locator().triangulation());
|
|
V.draw_skeleton(CGAL::BLUE);
|
|
}
|
|
|
|
@ \begin{ignoreindiss}
|
|
The header just wraps the above operations.
|
|
<<Nef_polyhedron_2_Window_stream.h>>=
|
|
<<CGAL Header1>>
|
|
// file : include/CGAL/IO/Nef_polyhedron_2_Window_stream.h
|
|
<<CGAL Header2>>
|
|
#ifndef NEF_POLYHEDRON_2_WINDOW_STREAM_H
|
|
#define NEF_POLYHEDRON_2_WINDOW_STREAM_H
|
|
|
|
#include <CGAL/Nef_polyhedron_2.h>
|
|
#include <CGAL/Nef_2/PM_visualizor.h>
|
|
|
|
CGAL_BEGIN_NAMESPACE
|
|
|
|
<<window stream output>>
|
|
|
|
CGAL_END_NAMESPACE
|
|
|
|
#endif // NEF_POLYHEDRON_2_WINDOW_STREAM_H
|
|
|
|
|
|
@ \end{ignoreindiss}%
|
|
\subsection{Input and Output}
|
|
Standard input and output is done by the plane map I/O class
|
|
|PM_io_parser|. \repdiss{}{See Section \ref{plane map implementation}
|
|
for more information.}
|
|
\begin{ignoreindiss}
|
|
We tag the output with an idendifier to be able to check correct
|
|
coordinate representation when we read it as input.
|
|
<<nef polyhedron input and output>>=
|
|
template <typename T>
|
|
std::ostream& operator<<
|
|
(std::ostream& os, const Nef_polyhedron_2<T>& NP)
|
|
{
|
|
os << "Nef_polyhedron_2<" << NP.EK.output_identifier() << ">\n";
|
|
typedef typename Nef_polyhedron_2<T>::Decorator Decorator;
|
|
CGAL::PM_io_parser<Decorator> O(os, NP.pm()); O.print();
|
|
return os;
|
|
}
|
|
|
|
template <typename T>
|
|
std::istream& operator>>
|
|
(std::istream& is, Nef_polyhedron_2<T>& NP)
|
|
{
|
|
typedef typename Nef_polyhedron_2<T>::Decorator Decorator;
|
|
CGAL::PM_io_parser<Decorator> I(is, NP.pm());
|
|
if (I.check_sep("Nef_polyhedron_2<") &&
|
|
I.check_sep(NP.EK.output_identifier()) &&
|
|
I.check_sep(">")) I.read();
|
|
else {
|
|
std::cerr << "Nef_polyhedron_2 input corrupted." << std::endl;
|
|
NP = Nef_polyhedron_2<T>();
|
|
}
|
|
typename Nef_polyhedron_2<T>::Topological_explorer D(NP.explorer());
|
|
D.check_integrity_and_topological_planarity();
|
|
return is;
|
|
}
|
|
|
|
@ \end{ignoreindiss}
|
|
|
|
\subsection{Hiding extended geometry}
|
|
|
|
The plane map explorer provides an interface that masks the properties
|
|
of our extended kernel by reintroducing purely affine objects. We want
|
|
to provide a simple interface for users who are not interested in the
|
|
detailed features of extended objects. The methods of the class
|
|
|PM_explorer<>| allow queries to the category of vertices and edges
|
|
such as ``Is a vertex embedded in the affine space or on the frame
|
|
box?'' or ``Is an edge part of the affine structure or part of the
|
|
frame box''. \repdiss{}{Its implementation trivially maps to the
|
|
operations of the extended kernel. See its interface in Section
|
|
\ref{Explorer} on page \pageref{Explorer}.}
|
|
|
|
\begin{ignoreindiss}
|
|
<<PM_explorer.h>>=
|
|
<<CGAL Header1>>
|
|
// file : include/CGAL/Nef_2/PM_explorer.h
|
|
<<CGAL Header2>>
|
|
#ifndef CGAL_PM_EXPLORER_H
|
|
#define CGAL_PM_EXPLORER_H
|
|
|
|
#include <CGAL/basic.h>
|
|
#include <CGAL/Nef_2/PM_const_decorator.h>
|
|
|
|
CGAL_BEGIN_NAMESPACE
|
|
|
|
<<PM explorer>>
|
|
|
|
CGAL_END_NAMESPACE
|
|
|
|
#endif // CGAL_PM_EXPLORER_H
|
|
|
|
<<PM explorer>>=
|
|
/*{\Moptions print_title=yes }*/
|
|
/*{\Moptions outfile=Explorer.man }*/
|
|
/*{\Msubst
|
|
PM_explorer#Explorer
|
|
}*/
|
|
/*{\Manpage {PM_explorer}{}{Plane map exploration}{E}}*/
|
|
|
|
/*{\Mdefinition An instance |\Mvar| of the data type |\Mname| is a
|
|
decorator to explore the structure of the plane map underlying the
|
|
Nef polyhedron. It inherits all topological adjacency exploration
|
|
operations from |PMConstDecorator|. |\Mname| additionally allows
|
|
one to explore the geometric embedding.
|
|
|
|
The position of each vertex is given by a so-called extended point,
|
|
which is either a standard affine point or the tip of a ray touching
|
|
an infinimaximal square frame centered at the origin. A vertex |v| is
|
|
called a \emph{standard} vertex if its embedding is a \emph{standard}
|
|
point and \emph{non-standard} if its embedding is a
|
|
\emph{non-standard} point. By the straightline embedding of their
|
|
source and target vertices, edges correspond to either affine segments,
|
|
rays or lines or are part of the bounding frame.
|
|
|
|
\displayeps{extsegs}{Extended geometry: standard vertices are marked
|
|
by S, non-standard vertices are marked by N. \textbf{A}: The possible
|
|
embeddings of edges: an affine segment s1, an affine ray s2, an affine
|
|
line s3. \textbf{B}: A plane map embedded by extended geometry: note
|
|
that the frame is arbitrarily large, the 6 vertices on the frame are at
|
|
infinity, the two faces represent a geometrically unbounded area,
|
|
however they are topologically closed by the frame edges. No standard
|
|
point can be placed outside the frame.}{10cm}
|
|
}*/
|
|
|
|
/*{\Mgeneralization Topological_explorer}*/
|
|
|
|
template <typename PMCDEC, typename GEOM>
|
|
class PM_explorer : public PMCDEC
|
|
{ typedef PMCDEC Base;
|
|
typedef PM_explorer<PMCDEC,GEOM> Self;
|
|
const GEOM* pK;
|
|
public:
|
|
/*{\Mtypes 4}*/
|
|
typedef PMCDEC Topological_explorer;
|
|
/*{\Mtypemember The base class.}*/
|
|
typedef typename PMCDEC::Plane_map Plane_map;
|
|
/*{\Xtypemember equals |PMCDEC::Plane_map|, the underlying plane map type.}*/
|
|
typedef GEOM Geometry;
|
|
/*{\Xtypemember equals |GEOM|. Add link to GEOM model.\\
|
|
\precond |Geometry::Point_2| equals |Plane_map::Point|. }*/
|
|
typedef typename GEOM::Standard_point_2 Point;
|
|
/*{\Mtypemember the point type of finite vertices.}*/
|
|
typedef typename GEOM::Standard_ray_2 Ray;
|
|
/*{\Mtypemember the ray type of vertices on the frame.}*/
|
|
|
|
<<local typedefs of explorer handles>>
|
|
/*{\Mtext Iterators, handles, and circulators are inherited from
|
|
|Topological_explorer|.}*/
|
|
|
|
/*{\Mcreation 3}*/
|
|
/*{\Mtext |\Mname| is copy constructable and assignable. An object
|
|
can be obtained via the |Nef_polyhedron_2::explorer()| method of
|
|
|Nef_polyhedron_2|.}*/
|
|
|
|
PM_explorer(const Self& E) : Base(E), pK(E.pK) {}
|
|
Self& operator=(const Self& E)
|
|
{ Base::operator=(E); pK=E.pK; return *this; }
|
|
|
|
PM_explorer(const Plane_map& P, const Geometry& k = Geometry()) :
|
|
Base(P), pK(&k) {}
|
|
/*{\Xcreate constructs a plane map explorer working on |P| with
|
|
geometric predicates used from |k|.}*/
|
|
|
|
/*{\Moperations 2 }*/
|
|
|
|
bool is_standard(Vertex_const_handle v) const
|
|
/*{\Mop returns true iff |v|'s position is a standard point.}*/
|
|
{ return pK->is_standard(Base::point(v)); }
|
|
|
|
Point point(Vertex_const_handle v) const
|
|
/*{\Mop returns the standard point that is the embedding of |v|.
|
|
\precond |\Mvar.is_standard(v)|.}*/
|
|
{ return pK->standard_point(Base::point(v)); }
|
|
|
|
Ray ray(Vertex_const_handle v) const
|
|
/*{\Mop returns the ray defining the non-standard point on the frame.
|
|
\precond |!\Mvar.is_standard(v)|.}*/
|
|
{ return pK->standard_ray(Base::point(v)); }
|
|
|
|
bool is_frame_edge(Halfedge_const_handle e) const
|
|
/*{\Mop returns true iff |e| is part of the infinimaximal frame.}*/
|
|
{ return ( face(e) == faces_begin() ||
|
|
face(twin(e)) == faces_begin() ); }
|
|
|
|
}; // PM_explorer<PMCDEC,GEOM>
|
|
|
|
|
|
@
|
|
<<local typedefs of explorer handles>>=
|
|
typedef typename Base::Vertex_const_handle Vertex_const_handle;
|
|
typedef typename Base::Halfedge_const_handle Halfedge_const_handle;
|
|
typedef typename Base::Face_const_handle Face_const_handle;
|
|
typedef typename Base::Vertex_const_iterator Vertex_const_iterator;
|
|
typedef typename Base::Halfedge_const_iterator Halfedge_const_iterator;
|
|
typedef typename Base::Face_const_iterator Face_const_iterator;
|
|
typedef typename Base::Halfedge_around_face_const_circulator
|
|
Halfedge_around_face_const_circulator;
|
|
typedef typename Base::Halfedge_around_vertex_const_circulator
|
|
Halfedge_around_vertex_const_circulator;
|
|
typedef typename Base::Isolated_vertex_const_iterator
|
|
Isolated_vertex_const_iterator;
|
|
typedef typename Base::Hole_const_iterator
|
|
Hole_const_iterator;
|
|
|
|
|
|
@ \section{A Demo Program}
|
|
|
|
The basic idea of our demo is simple. Store some basic polyhedra in a
|
|
history list and let the user interactively extend this list by the
|
|
binary and unary operations. The point location and ray shooting
|
|
capabilities of the structure can be triggered by mouse clicks into the
|
|
drawing window.
|
|
<<Nef_polyhedron_2-demo.C>>=
|
|
#include <CGAL/basic.h>
|
|
#ifdef CGAL_USE_LEDA
|
|
#include "xpms/nef.xpm"
|
|
#if CGAL_LEDA_VERSION < 500
|
|
#include <LEDA/pixmaps/button32/eye.xpm>
|
|
#include <LEDA/pixmaps/button32/draw.xpm>
|
|
#else
|
|
#include <LEDA/graphics/pixmaps/button32/eye.xpm>
|
|
#include <LEDA/graphics/pixmaps/button32/draw.xpm>
|
|
#endif
|
|
#include <CGAL/leda_integer.h>
|
|
#include <CGAL/Extended_homogeneous.h>
|
|
#include <CGAL/Filtered_extended_homogeneous.h>
|
|
#include <CGAL/IO/Filtered_extended_homogeneous_Window_stream.h>
|
|
#include <CGAL/Nef_polyhedron_2.h>
|
|
#include <CGAL/IO/Nef_polyhedron_2_Window_stream.h>
|
|
|
|
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); }
|
|
};
|
|
|
|
#define FILTERED_KERNEL
|
|
#ifndef FILTERED_KERNEL
|
|
typedef CGAL::Extended_homogeneous<leda_integer> EKernel;
|
|
#else
|
|
typedef CGAL::Filtered_extended_homogeneous<leda_integer> EKernel;
|
|
#endif
|
|
|
|
#if defined(_MSC_VER) || defined(__BORLANDC__)
|
|
#define WIN32CONFIG
|
|
#endif
|
|
|
|
<<getting types in global scope>>
|
|
<<a small interactive polyhedron editor>>
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
/* Enable debugging by introducing prime into the product:
|
|
RP 3 EP 5/59 PM 7 NefTOP 11
|
|
Overlayer 13 PointLoc 17 ConstrTriang 19
|
|
SegSweep 23
|
|
*/
|
|
SETDTHREAD(113);
|
|
CGAL::set_pretty_mode ( std::cerr );
|
|
std::cerr << "using " << CGAL::pointlocationversion << std::endl;
|
|
std::cerr << "using " << CGAL::sweepversion << std::endl;
|
|
|
|
<<initializing the drawing window>>
|
|
<<initializing the history>>
|
|
<<initializing the two panels>>
|
|
<<load bitmaps and add buttons to panels>>
|
|
|
|
leda_drawing_mode dm;
|
|
Point p_down(0,0);
|
|
main_panel.display();
|
|
int x0,y0,x1,y1;
|
|
W.frame_box(x0,y0,x1,y1);
|
|
W.display_help_text("help/nef-demo");
|
|
Object_handle h;
|
|
for(;;) {
|
|
double x, y;
|
|
int val;
|
|
switch( W.read_event(val, x, y) ) {
|
|
case button_press_event:
|
|
p_down = Point(x,y);
|
|
if (val == MOUSE_BUTTON(1)) {
|
|
std::cerr << "locating " << p_down << std::endl;
|
|
dm = W.set_mode(leda_xor_mode);
|
|
W<<CGAL::GREEN<<p_down; W.set_mode(dm);
|
|
h = N_display.locate(p_down);
|
|
draw(h);
|
|
}
|
|
if (val == MOUSE_BUTTON(2)) {
|
|
std::cerr << "shooting down from " << p_down << std::endl;
|
|
dm = W.set_mode(leda_xor_mode);
|
|
W<<CGAL::GREEN<<p_down; W.set_mode(dm);
|
|
//h = N_display.ray_shoot(p_down,Direction(0,-1),Nef_polyhedron::NAIVE);
|
|
h = N_display.ray_shoot(p_down,Direction(0,-1));
|
|
draw(h);
|
|
}
|
|
if (val == MOUSE_BUTTON(3)) {
|
|
CGAL::show_triangulation = !CGAL::show_triangulation;
|
|
win_redraw_handler(&W);
|
|
}
|
|
break;
|
|
case button_release_event:
|
|
if (val == MOUSE_BUTTON(1))
|
|
#ifndef WIN32CONFIG
|
|
{ dm = W.set_mode(leda_xor_mode);
|
|
W<<CGAL::GREEN<<p_down; W.set_mode(dm); draw(h); }
|
|
#else
|
|
{ win_redraw_handler(&W); }
|
|
#endif
|
|
if (val == MOUSE_BUTTON(2))
|
|
#ifndef WIN32CONFIG
|
|
{ dm = W.set_mode(leda_xor_mode);
|
|
W<<CGAL::GREEN<<p_down; W.set_mode(dm); draw(h); }
|
|
#else
|
|
{ win_redraw_handler(&W); }
|
|
#endif
|
|
break;
|
|
case key_press_event:
|
|
if (val == KEY_UP) { // ZOOM IN
|
|
CGAL::frame_default*=2;
|
|
Nef_polyhedron::Extended_kernel::RT::set_R(CGAL::frame_default);
|
|
int r = CGAL::frame_default+10;
|
|
W.init(-r,r,-r);
|
|
win_redraw_handler(&W);
|
|
}
|
|
if (val == KEY_DOWN) { // ZOOM OUT
|
|
CGAL::frame_default/=2;
|
|
Nef_polyhedron::Extended_kernel::RT::set_R(CGAL::frame_default);
|
|
int r = CGAL::frame_default+10;
|
|
W.init(-r,r,-r);
|
|
win_redraw_handler(&W);
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#if !defined(__KCC) && !defined(__BORLANDC__)
|
|
return 0; // never reached
|
|
#endif
|
|
}
|
|
|
|
#else // CGAL_USE_LEDA
|
|
|
|
int main() { return 0; }
|
|
|
|
#endif // CGAL_USE_LEDA
|
|
|
|
<<getting types in global scope>>=
|
|
typedef CGAL::Nef_polyhedron_2<EKernel> Nef_polyhedron;
|
|
typedef Nef_polyhedron::Point Point;
|
|
typedef Nef_polyhedron::Line Line;
|
|
typedef Nef_polyhedron::Direction Direction;
|
|
typedef Nef_polyhedron::Object_handle Object_handle;
|
|
typedef Nef_polyhedron::Explorer Explorer;
|
|
typedef Nef_polyhedron::Topological_explorer TExplorer;
|
|
typedef Explorer::Vertex_const_handle Vertex_const_handle;
|
|
typedef Explorer::Halfedge_const_handle Halfedge_const_handle;
|
|
typedef Explorer::Face_const_handle Face_const_handle;
|
|
|
|
@ We create one drawing window |W|, and two panels controlling the
|
|
operations |main_panel|, |op_panel|. We store a history of polyhedra:
|
|
|ML| stores the names displayed, |MH| maps the names to the existing
|
|
objects. |N_display| stores the current object drawn in |W|.
|
|
<<a small interactive polyhedron editor>>=
|
|
#if CGAL_LEDA_VERSION < 500
|
|
#include <LEDA/panel.h>
|
|
#include <LEDA/list.h>
|
|
#include <LEDA/d_array.h>
|
|
#include <LEDA/file_panel.h>
|
|
#include <LEDA/file.h>
|
|
#include <LEDA/stream.h>
|
|
#else
|
|
#include <LEDA/graphics/panel.h>
|
|
#include <LEDA/core/list.h>
|
|
#include <LEDA/core/d_array.h>
|
|
#include <LEDA/graphics/file_panel.h>
|
|
#include <LEDA/system/file.h>
|
|
#include <LEDA/system/stream.h>
|
|
#endif
|
|
|
|
static leda_panel main_panel;
|
|
static leda_panel op_panel;
|
|
static panel_item nef_menu_item;
|
|
static panel_item op_item;
|
|
static leda_string nef1;
|
|
static leda_string nef2;
|
|
static menu create_menu;
|
|
static int num;
|
|
#ifndef FILTERED_KERNEL
|
|
static leda_string dname = "./homogeneous_data";
|
|
#else
|
|
static leda_string dname = "./filtered_homogeneous_data";
|
|
#endif
|
|
static leda_string fname = "none";
|
|
static leda_string filter = "*.nef";
|
|
|
|
CGAL::Window_stream* pW;
|
|
Nef_polyhedron* pN;
|
|
leda_list<leda_string>* pML;
|
|
leda_d_array<leda_string,Nef_polyhedron>* pMH;
|
|
|
|
static leda_string stripped(leda_string s)
|
|
{ int i = s.pos(" = "); return s.head(i); }
|
|
|
|
<<initializing the drawing window>>=
|
|
CGAL::Window_stream W(600,600); pW = &W;
|
|
Nef_polyhedron N_display; pN = &N_display;
|
|
leda_list<leda_string> ML; pML = &ML;
|
|
leda_d_array<leda_string,Nef_polyhedron> MH; pMH = &MH;
|
|
|
|
@ Redrawing the window is done via |win_redraw_handler|.
|
|
Our exit handler is |win_del_handler|. Opening a panel
|
|
is done by |open_panel|.
|
|
<<a small interactive polyhedron editor>>=
|
|
void win_redraw_handler(leda_window*)
|
|
{ pW->clear(); (*pW) << (*pN); }
|
|
|
|
void win_del_handler(leda_window*)
|
|
{ leda_panel P("acknowledge");
|
|
P.text_item("");
|
|
P.text_item("\\bf\\blue Do you really want to quit~?");
|
|
P.fbutton("no",0);
|
|
P.button("yes",1);
|
|
if (P.open(main_panel) == 1) exit(0);
|
|
}
|
|
|
|
static int open_panel(leda_panel& p)
|
|
{ p.display(main_panel,0,0);
|
|
int res = p.read_mouse();
|
|
p.close();
|
|
return res;
|
|
}
|
|
|
|
@ |store_new| stores a new polyhedron |N| with name |t| at the
|
|
beginning of our history. |update_history| triggers the visual
|
|
history update.
|
|
<<a small interactive polyhedron editor>>=
|
|
static void store_new(const Nef_polyhedron& N, leda_string t)
|
|
{ leda_string k = leda_string("N%i",++num) ;
|
|
(*pMH)[k] = (*pN) = N;
|
|
pML->push_front(k+" = "+t);
|
|
win_redraw_handler(pW);
|
|
}
|
|
|
|
static void update_history()
|
|
{
|
|
nef1=pML->head();
|
|
nef2=pML->head();
|
|
main_panel.add_menu(nef_menu_item,(*pML));
|
|
op_panel.add_menu(op_item,(*pML));
|
|
}
|
|
|
|
@ |create| is linked to the creation menu (a bitmap button).
|
|
The enums control the different creation actions which are
|
|
mapped to some input operation on |W| followed by a call
|
|
to some constructor of |Nef_polyhedron_2|.
|
|
<<a small interactive polyhedron editor>>=
|
|
enum { EMPTY=31, FULL, HOPEN, HCLOSED, POPEN, PCLOSED };
|
|
enum { FILE_LOAD=111, FILE_SAVE };
|
|
|
|
void create(int i)
|
|
{
|
|
if (pML->back()=="none") pML->pop_back();
|
|
Line l; Point p;
|
|
std::list<Point> Lp;
|
|
leda_point pd;
|
|
leda_list<leda_point> Lpd;
|
|
string_ostream sos; CGAL::set_pretty_mode(sos);
|
|
pW->clear();
|
|
|
|
switch (i) {
|
|
case HOPEN:
|
|
pW->message("Insert Half-Space by Line");
|
|
(*pW) >> l; sos << '(' << l << ')' << '\0';
|
|
if ( l.is_degenerate() ) {
|
|
(*pW).acknowledge("Please enter non-degenerate line.");
|
|
win_redraw_handler(pW);
|
|
} else
|
|
store_new(Nef_polyhedron(l,Nef_polyhedron::EXCLUDED),sos.str());
|
|
break;
|
|
case HCLOSED:
|
|
pW->message("Insert Half-Space by Line");
|
|
(*pW) >> l; sos << '[' << l << ']' << '\0';
|
|
if ( l.is_degenerate() ) {
|
|
(*pW).acknowledge("Please enter non-degenerate line.");
|
|
win_redraw_handler(pW);
|
|
} else
|
|
store_new(Nef_polyhedron(l,Nef_polyhedron::INCLUDED),sos.str());
|
|
break;
|
|
case POPEN:
|
|
pW->message("Insert Polygon by Point Sequence");
|
|
Lpd = pW->read_polygon();
|
|
forall(pd,Lpd) Lp.push_back(Point(pd.xcoord(),pd.ycoord()));
|
|
sos << '[' << Lp.size() << "-gon"<< ']' << '\0';
|
|
store_new(Nef_polyhedron(Lp.begin(),Lp.end(),
|
|
Nef_polyhedron::EXCLUDED),sos.str());
|
|
break;
|
|
case PCLOSED:
|
|
pW->message("Insert Polygon by Point Sequence");
|
|
Lpd = pW->read_polygon();
|
|
forall(pd,Lpd) Lp.push_back(Point(pd.xcoord(),pd.ycoord()));
|
|
sos << '[' << Lp.size() << "-gon"<< ']' << '\0';
|
|
store_new(Nef_polyhedron(Lp.begin(),Lp.end(),
|
|
Nef_polyhedron::INCLUDED),sos.str());
|
|
break;
|
|
default:
|
|
std::cout << "created nothing\n";
|
|
}
|
|
sos.freeze(0);
|
|
update_history();
|
|
main_panel.redraw_panel();
|
|
}
|
|
|
|
@ File input and output is done via the operation |file_handler|.
|
|
<<a small interactive polyhedron editor>>=
|
|
static void read_file(leda_string fn)
|
|
{
|
|
std::ifstream in(fn);
|
|
Nef_polyhedron N; in >> N;
|
|
fn.replace_all(".nef","");
|
|
store_new(N,fn);update_history();
|
|
}
|
|
|
|
static bool confirm_overwrite(const char* fname)
|
|
{
|
|
#if defined(_MSC_VER) || defined(__BORLANDC__)
|
|
return true;
|
|
#else
|
|
leda_panel P;
|
|
P.buttons_per_line(2);
|
|
P.text_item("");
|
|
P.text_item(leda_string("\\bf\\blue File\\black %s\\blue exists.",fname));
|
|
P.button("overwrite",0);
|
|
P.button("cancel",1);
|
|
return (P.open() == 0);
|
|
#endif
|
|
}
|
|
|
|
static void write_file(leda_string fname)
|
|
{
|
|
if (is_file(fname) && !confirm_overwrite(fname)) return;
|
|
// win_ptr->set_status_string(" Writing " + fname);
|
|
leda_string nef = stripped(nef1);
|
|
if (pMH->defined(nef)) {
|
|
std::ofstream out(fname);
|
|
out << (*pMH)[nef];
|
|
} else error_handler(1,"Nef polyhedron "+nef+" not defined.");
|
|
}
|
|
|
|
static void file_handler(int what)
|
|
{
|
|
file_panel FP(fname,dname);
|
|
switch (what) {
|
|
case FILE_LOAD: FP.set_load_handler(read_file);
|
|
break;
|
|
case FILE_SAVE: FP.set_save_handler(write_file);
|
|
break;
|
|
}
|
|
if (filter != "") FP.set_pattern(filter,filter);
|
|
FP.open();
|
|
}
|
|
|
|
@ We have three more operations. |view| just draws the currently
|
|
chosen polyhedron of our history. |binop| allows a user to combine the
|
|
currently chosen polyhedron with one which is chosen in the second
|
|
panel |op_panel|. The binary operation used is defined by the button
|
|
which is pressed in |main_panel|. |unop| just triggers the unary
|
|
operation chosen by the button selection on the currently selected
|
|
polyhedron of the history.
|
|
<<a small interactive polyhedron editor>>=
|
|
void view(int i)
|
|
{
|
|
leda_string nef = stripped(nef1);
|
|
if (pMH->defined(nef)) {
|
|
(*pN) = (*pMH)[nef]; win_redraw_handler(pW);
|
|
} else error_handler(1,"Nef polyhedron "+nef+" not defined.");
|
|
}
|
|
|
|
void binop(int i)
|
|
{
|
|
leda_string op1;
|
|
switch (i) {
|
|
case 30: op1="intersection"; break;
|
|
case 31: op1="union"; break;
|
|
case 32: op1="difference"; break;
|
|
case 33: op1="symmdiff"; break;
|
|
default: break;
|
|
}
|
|
op_panel.set_button_label(111,op1);
|
|
open_panel(op_panel);
|
|
op_panel.flush();
|
|
leda_string arg1 = stripped(nef1), arg2 = stripped(nef2);
|
|
Nef_polyhedron N1 = (*pMH)[arg1];
|
|
Nef_polyhedron N2 = (*pMH)[arg2];
|
|
std::ofstream log("nef-demo.log");
|
|
if ( !log ) CGAL_assertion_msg(0,"no output log nef-demo.log");
|
|
log << 2 << std::endl << N1 << N2 << std::endl;
|
|
log.close();
|
|
switch (i) {
|
|
case 30: *pN = N1*N2; break;
|
|
case 31: *pN = N1+N2; break;
|
|
case 32: *pN = N1-N2; break;
|
|
case 33: *pN = N1^N2; break;
|
|
default: return;
|
|
}
|
|
leda_string descr = op1+"("+arg1+","+arg2+")";
|
|
store_new(*pN,descr);update_history();
|
|
win_redraw_handler(pW);
|
|
}
|
|
|
|
void unop(int i)
|
|
{
|
|
leda_string op, arg = stripped(nef1);
|
|
Nef_polyhedron N = (*pMH)[arg];
|
|
std::ofstream log("nef-demo.log");
|
|
if ( !log ) CGAL_assertion_msg(0,"no output log nef-demo.log");
|
|
log << 1 << std::endl << N << std::endl;
|
|
TRACEN("writing nef to log");
|
|
log.close();
|
|
switch (i) {
|
|
case 40: op="interior"; *pN = N.interior(); break;
|
|
case 41: op="complement"; *pN = N.complement(); break;
|
|
case 42: op="closure"; *pN = N.closure(); break;
|
|
case 43: op="boundary"; *pN = N.boundary(); break;
|
|
default: return;
|
|
}
|
|
leda_string descr = op+"("+arg+")";
|
|
store_new(*pN,descr);update_history();
|
|
win_redraw_handler(pW);
|
|
}
|
|
|
|
@ The |draw| operation just marks visually the object of the
|
|
plane map referenced by |h|.
|
|
<<a small interactive polyhedron editor>>=
|
|
void draw(Object_handle h)
|
|
{ CGAL::PM_visualizor<TExplorer,EKernel>
|
|
PMV(*pW,pN->explorer(),pN->EK,
|
|
CGAL::PM_DefColor<TExplorer>(CGAL::RED,CGAL::RED,6,6) );
|
|
leda_drawing_mode prev = pW->set_mode(leda_xor_mode);
|
|
Vertex_const_handle vh; Halfedge_const_handle eh; Face_const_handle fh;
|
|
if ( CGAL::assign(vh,h) ) PMV.draw(vh);
|
|
if ( CGAL::assign(eh,h) ) PMV.draw(eh);
|
|
if ( CGAL::assign(fh,h) ) PMV.draw(fh);
|
|
pW->set_mode(prev);
|
|
}
|
|
|
|
|
|
@ We initialize the window to the default of the size of our
|
|
frame and center it at the origin.
|
|
<<initializing the drawing window>>=
|
|
W.init(-CGAL::frame_default,CGAL::frame_default,-CGAL::frame_default);
|
|
W.set_show_coordinates(true);
|
|
W.set_grid_mode(5);
|
|
W.set_node_width(3);
|
|
W.set_redraw(&win_redraw_handler);
|
|
W.display(0,0);
|
|
|
|
@ We initialize the history with several simple nef polyhedra like
|
|
an empty one, one representing the plane, one left of the $y$-axis,
|
|
one above a diagonal and one defined by a simple quadratic polygon.
|
|
<<initializing the history>>=
|
|
std::list<Point> Lp;
|
|
const int r=70;
|
|
Lp.push_back(Point(-r,-r));
|
|
Lp.push_back(Point(r,-r));
|
|
Lp.push_back(Point(r,r));
|
|
Lp.push_back(Point(-r,r));
|
|
|
|
store_new(Nef_polyhedron(),"empty");
|
|
store_new(Nef_polyhedron(Nef_polyhedron::COMPLETE),"plane");
|
|
store_new(Nef_polyhedron(Line(Point(0,0),Point(0,1)),
|
|
Nef_polyhedron::INCLUDED),"neg y-plane (closed)");
|
|
store_new(Nef_polyhedron(Line(Point(-2,-1),Point(2,1)),
|
|
Nef_polyhedron::EXCLUDED),"above diagonal (open)");
|
|
store_new(Nef_polyhedron(Lp.begin(),Lp.end(),
|
|
Nef_polyhedron::INCLUDED),"square (closed)");
|
|
|
|
if ( argc == 2 ) {
|
|
std::ifstream log( argv[1] );
|
|
if ( !log )
|
|
CGAL_assertion_msg(0,leda_string("no input log ")+argv[1]);
|
|
int n;
|
|
log >> n;
|
|
for (int i=0; i<n; ++i) {
|
|
Nef_polyhedron Ni;
|
|
log >> Ni;
|
|
store_new(Ni,leda_string(argv[1])+leda_string("%d",i+1));
|
|
}
|
|
}
|
|
|
|
@ The head of our history list is displayed in the panels.
|
|
<<initializing the two panels>>=
|
|
nef1=ML.head();nef2=ML.head();
|
|
nef_menu_item = main_panel.string_item("Polyhedra",nef1,ML);
|
|
main_panel.set_window_delete_handler(win_del_handler);
|
|
main_panel.buttons_per_line(10);
|
|
main_panel.set_item_width(300);
|
|
main_panel.set_frame_label("Operations on Nef Polyhedra");
|
|
main_panel.set_icon_label("Nef Polyhedra");
|
|
|
|
op_item = op_panel.string_item("Polyhedra",nef2,ML);
|
|
op_panel.fbutton(" ",111);
|
|
op_panel.set_frame_label("Choose second argument");
|
|
op_panel.set_item_width(300);
|
|
|
|
<<load bitmaps and add buttons to panels>>=
|
|
char* p_inter = main_panel.create_pixrect(intersection_xpm);
|
|
char* p_union = main_panel.create_pixrect(union_xpm);
|
|
char* p_diff = main_panel.create_pixrect(difference_xpm);
|
|
char* p_exor = main_panel.create_pixrect(exor_xpm);
|
|
char* p_int = main_panel.create_pixrect(interior_xpm);
|
|
char* p_compl = main_panel.create_pixrect(complement_xpm);
|
|
char* p_clos = main_panel.create_pixrect(closure_xpm);
|
|
char* p_bound = main_panel.create_pixrect(boundary_xpm);
|
|
char* p_eye = main_panel.create_pixrect(eye_xpm);
|
|
char* p_draw = main_panel.create_pixrect(draw_xpm);
|
|
|
|
main_panel.button(p_eye,p_eye,"show", 10, view);
|
|
main_panel.button(p_draw,p_draw,"new polyhedron", 11, create_menu);
|
|
|
|
main_panel.button(p_inter,p_inter,"intersection", 30, binop);
|
|
main_panel.button(p_union,p_union,"union", 31, binop);
|
|
main_panel.button(p_diff,p_diff,"difference", 32, binop);
|
|
main_panel.button(p_exor,p_exor,"symmetric difference", 33, binop);
|
|
|
|
main_panel.button(p_int,p_int,"interior", 40,unop);
|
|
main_panel.button(p_compl,p_compl,"complement", 41,unop);
|
|
main_panel.button(p_clos,p_clos,"closure", 42,unop);
|
|
main_panel.button(p_bound,p_bound,"boundary", 43,unop);
|
|
|
|
create_menu.button("half-plane (open)", HOPEN, create);
|
|
create_menu.button("half-plane (closed)", HCLOSED, create);
|
|
create_menu.button("polygon (open)", POPEN, create);
|
|
create_menu.button("polygon (closed)", PCLOSED, create);
|
|
create_menu.button("load from disk",FILE_LOAD, file_handler);
|
|
create_menu.button("save to disk",FILE_SAVE, file_handler);
|
|
|
|
@ \section{A Test program}
|
|
<<Nef_polyhedron_2-test.C>>=
|
|
#include <CGAL/basic.h>
|
|
#include <CGAL/test_macros.h>
|
|
#include <CGAL/Nef_2/redefine_MSC.h>
|
|
#include <CGAL/Extended_homogeneous.h>
|
|
#include <CGAL/Filtered_extended_homogeneous.h>
|
|
#include <CGAL/Nef_polyhedron_2.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); }
|
|
};
|
|
#else
|
|
#ifdef CGAL_USE_GMP
|
|
#include <CGAL/Gmpz.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); }
|
|
};
|
|
#else
|
|
typedef long Integer;
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
int main()
|
|
{
|
|
SETDTHREAD(911); // 911
|
|
CGAL::set_pretty_mode ( std::cerr );
|
|
std::cerr << "using " << CGAL::pointlocationversion << std::endl;
|
|
std::cerr << "using " << CGAL::sweepversion << std::endl;
|
|
CGAL_TEST_START;
|
|
{
|
|
<<simple extended>>
|
|
<<nef test suite>>
|
|
}
|
|
{
|
|
<<filtered extended>>
|
|
<<nef test suite>>
|
|
Nef_polyhedron::EK.print_statistics();
|
|
}
|
|
CGAL_TEST_END;
|
|
}
|
|
|
|
<<simple extended>>=
|
|
typedef CGAL::Extended_homogeneous<Integer> EKernel;
|
|
typedef CGAL::Nef_polyhedron_2<EKernel> Nef_polyhedron;
|
|
typedef Nef_polyhedron::Point Point;
|
|
typedef Nef_polyhedron::Direction Direction;
|
|
typedef Nef_polyhedron::Line Line;
|
|
|
|
<<filtered extended>>=
|
|
typedef CGAL::Filtered_extended_homogeneous<Integer> EKernel;
|
|
typedef CGAL::Nef_polyhedron_2<EKernel> Nef_polyhedron;
|
|
typedef Nef_polyhedron::Point Point;
|
|
typedef Nef_polyhedron::Direction Direction;
|
|
typedef Nef_polyhedron::Line Line;
|
|
|
|
<<nef test suite>>=
|
|
typedef Nef_polyhedron::Object_handle Object_handle;
|
|
typedef Nef_polyhedron::Explorer Explorer;
|
|
typedef Explorer::Vertex_const_handle Vertex_const_handle;
|
|
typedef Explorer::Halfedge_const_handle Halfedge_const_handle;
|
|
typedef Explorer::Face_const_handle Face_const_handle;
|
|
typedef Explorer::Vertex_const_iterator Vertex_const_iterator;
|
|
typedef Explorer::Halfedge_const_iterator Halfedge_const_iterator;
|
|
typedef Explorer::Face_const_iterator Face_const_iterator;
|
|
|
|
typedef Explorer::Ray Ray;
|
|
|
|
Point p1(0,0), p2(0,1), p3(1,0), p4(-1,-1), p5(0,-1), p6(-1,0), p7(1,1);
|
|
Line l1(p2,p1); // neg y-axis
|
|
Line l2(p1,p3); // pos x-axis
|
|
Nef_polyhedron N1(l1), N2(l2, Nef_polyhedron::EXCLUDED),
|
|
EMPTY(Nef_polyhedron::EMPTY),PLANE(Nef_polyhedron::COMPLETE);
|
|
CGAL_TEST((N1*N1) == N1);
|
|
CGAL_TEST((N1*!N1) == EMPTY);
|
|
CGAL_TEST((N1+!N1) == PLANE);
|
|
CGAL_TEST((N1^N2) == ((N1-N2)+(N2-N1)));
|
|
CGAL_TEST((!(N1*N2)) == (!N1+!N2));
|
|
|
|
Nef_polyhedron N3 = N1.intersection(N2);
|
|
/* N3 is the first quadrant including the positive y-axis
|
|
but excluding the origin and the positive x-axis */
|
|
|
|
CGAL_TEST(N3 < N1 && N3 < N2);
|
|
CGAL_TEST(N3 <= N1 && N3 <= N2);
|
|
CGAL_TEST(N1 > N3 && N2 > N3);
|
|
CGAL_TEST(N1 >= N3 && N2 >= N3);
|
|
|
|
Explorer E = N3.explorer();
|
|
Vertex_const_iterator v = E.vertices_begin();
|
|
CGAL_TEST( !E.is_standard(v) && E.ray(v) == Ray(p1,p4) );
|
|
Halfedge_const_handle e = E.first_out_edge(v);
|
|
CGAL_TEST( E.is_frame_edge(e) );
|
|
++(++v); // third vertex
|
|
CGAL_TEST( E.is_standard(v) && E.point(v) == p1 );
|
|
|
|
Vertex_const_handle v1,v2;
|
|
Halfedge_const_handle e1,e2;
|
|
Face_const_handle f1,f2;
|
|
Object_handle h1,h2,h3;
|
|
h1 = N3.locate(p1);
|
|
h2 = N3.locate(p1,Nef_polyhedron::NAIVE);
|
|
CGAL_TEST( CGAL::assign(v1,h1) && CGAL::assign(v2,h2) && v1 == v2 );
|
|
CGAL_TEST( E.is_standard(v1) && E.point(v1) == p1 );
|
|
h1 = N3.locate(p2);
|
|
h2 = N3.locate(p2,Nef_polyhedron::NAIVE);
|
|
CGAL_TEST( CGAL::assign(e1,h1) && CGAL::assign(e2,h2) );
|
|
CGAL_TEST( (e1==e2 || e1==E.twin(e2)) && E.mark(e1) );
|
|
h1 = N3.locate(p4);
|
|
h2 = N3.locate(p4,Nef_polyhedron::NAIVE);
|
|
CGAL_TEST( CGAL::assign(f1,h1) && CGAL::assign(f2,h2) &&
|
|
f1 == f2 && !E.mark(f1) );
|
|
// shooting along angular bisector:
|
|
h1 = N3.ray_shoot(p4,Direction(1,1));
|
|
h2 = N3.ray_shoot(p4,Direction(1,1),Nef_polyhedron::NAIVE);
|
|
CGAL_TEST( CGAL::assign(f1,h1) && CGAL::assign(f2,h2) &&
|
|
f1 == f2 && E.mark(f1) );
|
|
// shooting along x-axis:
|
|
h1 = N3.ray_shoot(p6,Direction(1,0));
|
|
h2 = N3.ray_shoot(p6,Direction(1,0),Nef_polyhedron::NAIVE);
|
|
CGAL_TEST( h1 == NULL && h2 == NULL );
|
|
// shooting along y-axis:
|
|
h1 = N3.ray_shoot(p5,Direction(0,1));
|
|
h2 = N3.ray_shoot(p5,Direction(0,1),Nef_polyhedron::NAIVE);
|
|
e = e1;
|
|
CGAL_TEST( CGAL::assign(e1,h1) && CGAL::assign(e2,h2) &&
|
|
(e1==e2||e1==E.twin(e2)) && E.mark(e1) );
|
|
h1 = N3.ray_shoot_to_boundary(p5,Direction(0,1));
|
|
h2 = N3.ray_shoot_to_boundary(p5,Direction(0,1),Nef_polyhedron::NAIVE);
|
|
CGAL_TEST( N3.contained_in_boundary(h1) && N3.contained_in_boundary(h2) );
|
|
CGAL_TEST( CGAL::assign(v1,h1) && CGAL::assign(v2,h2) && v1 == v2 );
|
|
h1 = N3.ray_shoot_to_boundary(p7,Direction(0,-1));
|
|
h2 = N3.ray_shoot_to_boundary(p7,Direction(0,-1),Nef_polyhedron::NAIVE);
|
|
CGAL_TEST( N3.contained_in_boundary(h1) && N3.contained_in_boundary(h2) );
|
|
CGAL_TEST( CGAL::assign(e1,h1) && CGAL::assign(e2,h2) &&
|
|
(e1==e2 || e1==E.twin(e2)) );
|
|
|
|
std::list<Point> L;
|
|
L.push_back(p1);
|
|
N3 = Nef_polyhedron(L.begin(), L.end(), Nef_polyhedron::INCLUDED);
|
|
E = N3.explorer();
|
|
h1 = N3.locate(p1);
|
|
h2 = N3.locate(p2);
|
|
CGAL_TEST( CGAL::assign(v1,h1) && E.point(v1)==p1 && E.mark(v1) );
|
|
CGAL_TEST( CGAL::assign(f1,h2) && !E.mark(f1) );
|
|
|
|
L.push_back(p2);
|
|
N3 = Nef_polyhedron(L.begin(), L.end(), Nef_polyhedron::INCLUDED);
|
|
E = N3.explorer();
|
|
h1 = N3.locate(p1);
|
|
h2 = N3.locate(CGAL::midpoint(p1,p2));
|
|
h3 = N3.locate(p6);
|
|
CGAL_TEST( CGAL::assign(v1,h1) && E.point(v1)==p1 && E.mark(v1) );
|
|
CGAL_TEST( CGAL::assign(e1,h2) && E.mark(e1) );
|
|
CGAL_TEST( CGAL::assign(f1,h3) && !E.mark(f1) );
|
|
|
|
L.push_back(p3);
|
|
N3 = Nef_polyhedron(L.begin(), L.end(), Nef_polyhedron::INCLUDED);
|
|
E = N3.explorer();
|
|
h1 = N3.locate(p1);
|
|
h2 = N3.locate(CGAL::midpoint(p1,p2));
|
|
h3 = N3.locate(p6);
|
|
CGAL_TEST( CGAL::assign(v1,h1) && E.point(v1)==p1 && E.mark(v1) );
|
|
CGAL_TEST( CGAL::assign(e1,h2) && E.mark(e1) );
|
|
CGAL_TEST( CGAL::assign(f1,h3) && E.mark(f1) );
|
|
h3 = N3.locate(Point(1,1,3));
|
|
CGAL_TEST( CGAL::assign(f1,h3) && !E.mark(f1) );
|
|
|
|
CGAL_IO_TEST(N1,N2);
|
|
|
|
|
|
|
|
@
|
|
\newsavebox{\EXORB}
|
|
\newsavebox{\INTERB}
|
|
\newsavebox{\UNIONB}
|
|
\sbox{\EXORB}{\mbox{$\wedge$}}
|
|
\sbox{\INTERB}{\mbox{$\cap$}}
|
|
\sbox{\UNIONB}{\mbox{$\cup$}}
|
|
\newcommand{\EXOR}{\usebox{\EXORB}}
|
|
\newcommand{\INTER}{\usebox{\INTERB}}
|
|
\newcommand{\UNION}{\usebox{\UNIONB}}
|
|
\newpage
|
|
\section{A Runtime Test}
|
|
|
|
For an evaluation of Nef polyhedra we do the following. We
|
|
recursively create a random structure of desired complexity. We start
|
|
from $n$ half-spaces in an array. We interpret the entries as the
|
|
leaves of a balanced binary tree. Starting from the leaf level we
|
|
combine two neighbored nodes in each level into a new polyhedron by a
|
|
symmetric difference operation. The root of the tree thereby contains
|
|
a structure of complexity $O(n^2)$ as the symmetric difference does
|
|
not take away any structure. The result is an arrangement of the lines
|
|
in the boundary of the half-spaces, where each vertex, edge, and face
|
|
carries a random bit. We then evaluate the two binary operations
|
|
|intersection| and |union| on the two such structures. To get an
|
|
impression of how good the extended approach is we compare four
|
|
different geometric treatments of the problem. We measure the time
|
|
for the naive implementation with an explicit usage of a polynomial
|
|
ring number type based on LEDA's multi-precision integer arithmetic
|
|
(marked as \emph{naive}). We do the same test on a extended kernel
|
|
that uses dynamic double filter techniques (based on the CGAL double
|
|
interval arithmetic |Interval_nt_advanced|). As our predicate
|
|
expressions are of bounded arithmetic depth, we can program these
|
|
expressions explicitly in unrolled code blocks. The corresponding
|
|
expressions are instantiated for the interval data type and for the
|
|
multi-precision integer number type. The filter stage determines the
|
|
result of our predicates in many cases by pure double arithmetic as
|
|
long as the controled error bound guarantees the correctness of the
|
|
result. We call this the \emph{filtered} approach.
|
|
|
|
Finally we compare the two instantiations of our Nef polyhedron data
|
|
type with the simpler scenarios of generic polygons (LEDA
|
|
|rat_gen_polygon|s). The type has a simpler geometric domain as it
|
|
only considers regularized polygons. This takes away some
|
|
complications in the topology of the result of binary operations.
|
|
generic polygons are programmed around the concept of simple polygonal
|
|
chains which store the boundary cycles of the faces of the planar
|
|
subdivision. They also do not consider boundary issues (the boundary
|
|
is part of the face incident to it). The simpler topology should allow
|
|
faster binary operations. Thus we cannot expect to beat the data
|
|
structure with respect to algorithmic processing. Of course Nef
|
|
polyhedra cover additionally the unbounded nature of half-spaces.
|
|
|
|
To allow any testing we fix a static boundary large enough to make the
|
|
topology on the boundary constant (the knowledge about the size is
|
|
taken from the previously calculated nef polyhedron). Then we convert
|
|
the geometry to the affine bounded scenario and use generic
|
|
polygons. This gives us some kind of competitor that competes in a
|
|
simpler domain and that profits from our knowledge. If we just use the
|
|
binary operations of generic polygons recursively they loose the
|
|
competition due to the accumulated mantissae in the multi-precision
|
|
representation of the polygon vertex embedding. If we normalize the
|
|
embedding to its minimal representation then the expected runtime
|
|
hierarchy pops up again. In the following \emph{rgp} refers to
|
|
|rat_gen_polygon| and |rgpn| refers to |rat_gen_polygon| including
|
|
normalization after each binary operations. The lines contain times
|
|
concerning the binary overlay of structures of a certain
|
|
complexity. The first column determines the size $n$ of a set of
|
|
half-spaces. Such a set implies a planar arrangement of $n(n-1)/2 + 2n
|
|
+ 4$ vertices, $n(n+1) + 2n + 4$ edges and $n(n+1)/2 + 2$ faces (in
|
|
the non-degenerate case including the objects created by the framing
|
|
box).
|
|
|
|
The line marked \EXOR{} presents the complexity and times for the
|
|
tree-recursive synthesis of the structure. Thus we measure $O(n)$
|
|
binary symmetric difference operations on operands of increasing
|
|
complexity. The three column entries \#V, \#E, and \#F present the
|
|
actual number of nodes, uedges and faces of the resulting arrangement
|
|
(deviation from the formulas above due to degeneries are possible).
|
|
The lines marked with \INTER{} and \UNION{} present the results of one
|
|
corresponding binary operation where the result has the complexity
|
|
shown in the columns \#V, \#E, \#F.
|
|
|
|
Note that the generic polygons are actually slower in the synthesis
|
|
phase as the normalization takes place for the whole structure and is
|
|
therefore more expensive than our approach. The $-1$ entries tell you
|
|
that we didn't measure those times anymore as the non-normalized
|
|
approach took just too long. The times are measured in seconds on a
|
|
SUN Ultra-Enterprise-10000 with an 333 MHz UltraSPARC processors.
|
|
\smallskip
|
|
|
|
\begin{tabular}{|c|c||c|c|c||c|c|c|c|}\hline
|
|
\#lines & op & \#V & \#E & \#F & naive & filtered & rgpn & rgp \\ \hline\hline
|
|
\input inputs/nef-rt.table
|
|
\end{tabular}
|
|
<<Nef_polyhedron_2-rt.C>>=
|
|
#include <CGAL/basic.h>
|
|
#include <CGAL/Random.h>
|
|
#include <CGAL/Homogeneous.h>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <iomanip>
|
|
#include <CGAL/leda_integer.h>
|
|
#include <CGAL/Extended_homogeneous.h>
|
|
#include <CGAL/Filtered_extended_homogeneous.h>
|
|
#include <CGAL/Nef_polyhedron_2.h>
|
|
#include <CGAL/basic_constructions_2.h>
|
|
#include <CGAL/Point_2.h>
|
|
#include <CGAL/point_generators_2.h>
|
|
#include <CGAL/copy_n.h>
|
|
#include <CGAL/random_selection.h>
|
|
#include <CGAL/IO/Window_stream.h>
|
|
|
|
#include <CGAL/IO/Nef_polyhedron_2_Window_stream.h>
|
|
#if CGAL_LEDA_VERSION < 500
|
|
#include <LEDA/misc.h>
|
|
#include <LEDA/rat_gen_polygon.h>
|
|
#include <LEDA/rat_window.h>
|
|
#include <LEDA/param_handler.h>
|
|
#else
|
|
#include <LEDA/system/misc.h>
|
|
#include <LEDA/geo/rat_gen_polygon.h>
|
|
#include <LEDA/graphics/rat_window.h>
|
|
#include <LEDA/system/param_handler.h>
|
|
#endif
|
|
|
|
template <>
|
|
struct ring_or_field<leda_integer> {
|
|
typedef ring_with_gcd kind;
|
|
static leda_integer gcd(const leda_integer& i1, const leda_integer& i2)
|
|
{ return ::gcd(i1,i2); }
|
|
};
|
|
|
|
typedef CGAL::Extended_homogeneous<leda_integer> EKernel;
|
|
typedef CGAL::Nef_polyhedron_2<EKernel> Nef_polyhedron;
|
|
typedef Nef_polyhedron::Point Point;
|
|
typedef Nef_polyhedron::Direction Direction;
|
|
typedef Nef_polyhedron::Line Line;
|
|
typedef Nef_polyhedron::Explorer PMExplorer;
|
|
typedef CGAL::Filtered_extended_homogeneous<leda_integer> FEKernel;
|
|
typedef CGAL::Nef_polyhedron_2<FEKernel> Nef_polyhedronF;
|
|
typedef CGAL::Creator_uniform_2<leda_integer,Point> Creator;
|
|
|
|
enum { NAIVE=0, FILTERED, GENPOLY_NORMALIZED, GENPOLY };
|
|
enum { EXOR1=0, EXOR2, INTER, UNION };
|
|
static int n, Vn[4],En[4],Fn[4];
|
|
static float tt_start, tt_exor, tt_inter, tt_union;
|
|
static float t_exor[4], t_inter[4], t_union[4];
|
|
static leda_integer R;
|
|
static leda_string input_file;
|
|
static bool verbose;
|
|
|
|
<<tool operations>>
|
|
<<creating random lines>>
|
|
<<determining the frame size>>
|
|
<<combination of lines into nef polyhedron>>
|
|
<<combination of lines into gen polygon>>
|
|
|
|
int main(int argc, char* argv[]) {
|
|
|
|
<<extract command line parameters>>
|
|
<<verbose introduction>>
|
|
Nef_polyhedron N1,N2,N3,N4;
|
|
Nef_polyhedronF NF1,NF2,NF3,NF4;
|
|
std::vector<Line> lines1,lines2;
|
|
<<creating random lines or reading them from file>>
|
|
<<write lines to a log file>>
|
|
|
|
combine_and_time(lines1,lines2,N1,N2,N3,N4,"NAIVE:");
|
|
save_times(NAIVE);
|
|
combine_and_time(lines1,lines2,NF1,NF2,NF3,NF4,"FILTERED:");
|
|
save_times(FILTERED);
|
|
save_structure(NF1,EXOR1); save_structure(NF2,EXOR2);
|
|
save_structure(NF3,INTER); save_structure(NF4,UNION);
|
|
std::ofstream poly1("nef1.log"), poly2("nef2.log");
|
|
CGAL_assertion(poly1&&poly2);
|
|
poly1 << NF1; poly2 << NF2;
|
|
poly1.close(); poly2.close();
|
|
std::ofstream poly3("nef3.log"), poly4("nef4.log");
|
|
poly3 << NF3; poly4 << NF4;
|
|
poly3.close(); poly4.close();
|
|
|
|
R = min_frame_size(NF1);
|
|
R = std::max(min_frame_size(NF2),R);
|
|
R = std::max(min_frame_size(NF3),R);
|
|
R = std::max(min_frame_size(NF4),R);
|
|
|
|
leda_rat_gen_polygon G1,G2,G3,G4;
|
|
combine_and_time_leda(lines1,lines2,G1,G2,G3,G4,true,"RGP normalized:");
|
|
save_times(GENPOLY_NORMALIZED);
|
|
if ( n <= 20 ) {
|
|
combine_and_time_leda(lines1,lines2,G1,G2,G3,G4,false,"RGP:");
|
|
save_times(GENPOLY);
|
|
} else {
|
|
tt_exor = -2.0; tt_inter = tt_union = -1.0;
|
|
save_times(GENPOLY);
|
|
}
|
|
|
|
<<print runtimes>>
|
|
if (verbose) {
|
|
<<report verbose results>>
|
|
<<visualize results>>
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
<<creating random lines or reading them from file>>=
|
|
if ( input_file == "" ) {
|
|
lines1 = std::vector<Line>(n);
|
|
lines2 = std::vector<Line>(n);
|
|
create_random_lines(n,lines1);
|
|
create_random_lines(n,lines2);
|
|
} else {
|
|
std::ifstream input(input_file);
|
|
CGAL_assertion_msg(input,"no input log.");
|
|
input >> n;
|
|
lines1 = std::vector<Line>(n);
|
|
lines2 = std::vector<Line>(n);
|
|
for (int j=0; j<n; ++j) input >> lines1[j];
|
|
for (int j=0; j<n; ++j) input >> lines2[j];
|
|
}
|
|
|
|
@ We determine a random line by a random point $p$ and a random
|
|
direction $d$. We have to avoid that the direction is trivial.
|
|
<<creating random lines>>=
|
|
void create_random_lines(int r, std::vector<Line>& lines)
|
|
{ // n random lines that intersect a square of radius r
|
|
CGAL::Random_points_in_square_2<Point,Creator> points( r );
|
|
for (unsigned int i=0; i<lines.size(); ++i) {
|
|
int dx(0),dy(0);
|
|
while ( dx==0 && dy==0 ) {
|
|
dx = CGAL::default_random.get_int(-r,r);
|
|
dy = CGAL::default_random.get_int(-r,r);
|
|
}
|
|
Point p = *points++;
|
|
Direction d(dx,dy);
|
|
lines[i] = Line(p,d);
|
|
}
|
|
}
|
|
|
|
@ We create elemtary nef polyhedra in a vector and use tree
|
|
constuction bottom-up with the vector entries as the leafs of the
|
|
tree. We use the symmetric difference operation for the construction
|
|
of internal nodes of the tree. We measure the time to construct the
|
|
structure in the root of the tree, which is in general an arrangement
|
|
of lines where all faces are randomly marked (according to the random
|
|
selection of half-spaces). The size of the resulting polyhedron is
|
|
quadratic with respect to the number of lines.
|
|
<<combination of lines into nef polyhedron>>=
|
|
|
|
template <typename L, typename K>
|
|
void
|
|
combine(const std::vector<L>& lines, CGAL::Nef_polyhedron_2<K>& N)
|
|
{ std::vector< CGAL::Nef_polyhedron_2<K> > V(lines.size());
|
|
int s = lines.size(),n(s);
|
|
for (int i=0; i<s; ++i) {
|
|
V[i] = CGAL::Nef_polyhedron_2<K>(lines[i]);
|
|
}
|
|
tt_start = used_time();
|
|
while (s > 1) {
|
|
for (int i = 0; i<s; i+=2) {
|
|
if ( i+1 == s )
|
|
V[i/2] = V[i];
|
|
else {
|
|
V[i/2] = V[i] ^ V[i+1];
|
|
std::cerr << ".";
|
|
}
|
|
}
|
|
s = s/2 + s%2;
|
|
}
|
|
tt_exor = used_time(tt_start);
|
|
N = V[0];
|
|
std::cerr << " " << n << " lines exor combined in " << tt_exor
|
|
<< std::endl;
|
|
}
|
|
|
|
@ We use the \emph{combine} operation to create one complex nef
|
|
polyhedron from each set of lines. Then we measure the time used for
|
|
the two binary operations \emph{intersection} and \emph{union}.
|
|
<<combination of lines into nef polyhedron>>=
|
|
template <class Nef>
|
|
void combine_and_time(const std::vector<Line>& lines1,
|
|
const std::vector<Line>& lines2,
|
|
Nef& N1, Nef& N2, Nef& N3, Nef& N4,
|
|
const char* title)
|
|
{
|
|
std::cerr << "\n" << title << "\n";
|
|
int n = lines1.size(); float t;
|
|
combine(lines1,N1); t = tt_exor;
|
|
combine(lines2,N2); tt_exor += t;
|
|
tt_start = used_time();
|
|
N3 = N1 * N2;
|
|
tt_inter = used_time(tt_start);
|
|
tt_start = used_time();
|
|
N4 = N1 + N2;
|
|
tt_union = used_time(tt_start);
|
|
std::cerr << n << " line arrangement inter " << tt_inter << std::endl;
|
|
std::cerr << n << " line arrangement union " << tt_union << std::endl;
|
|
}
|
|
|
|
@ Concerning general polygons we use the construction of nef polyhedra
|
|
to extract quadrangles realizing half-spaces. We then use the bottom up
|
|
tree combination as before.
|
|
<<combination of lines into gen polygon>>=
|
|
void combine_leda(const std::vector<Line>& lines,
|
|
leda_rat_gen_polygon& G, bool normalize=true)
|
|
{ int s = lines.size();
|
|
std::vector< Nef_polyhedronF > V(s);
|
|
std::vector< leda_rat_gen_polygon > P(s);
|
|
for (int j=0; j<s; ++j ) {
|
|
V[j] = Nef_polyhedronF(lines[j]);
|
|
}
|
|
for (int i=0; i<s; ++i) {
|
|
typedef Nef_polyhedronF::Explorer Explorer;
|
|
typedef Explorer::Face_const_iterator Face_const_iterator;
|
|
typedef Explorer::Halfedge_around_face_const_circulator
|
|
Halfedge_around_face_const_circulator;
|
|
Explorer E = V[i].explorer();
|
|
leda_list<leda_rat_point> L;
|
|
for (Face_const_iterator f = E.faces_begin(); f != E.faces_end();
|
|
++f) {
|
|
if ( !E.mark(f) ) continue;
|
|
Halfedge_around_face_const_circulator e(E.halfedge(f)), ee(e);
|
|
CGAL_For_all(e,ee) {
|
|
L.append(cgal_to_leda(e->vertex()->point()));
|
|
}
|
|
}
|
|
P[i] = leda_rat_gen_polygon(L);
|
|
if (normalize) P[i].normalize();
|
|
}
|
|
tt_start = used_time();
|
|
while (s > 1) {
|
|
for (int i = 0; i<s; i+=2) {
|
|
if ( i+1 == s )
|
|
P[i/2] = P[i];
|
|
else {
|
|
P[i/2] = P[i].sym_diff(P[i+1]);
|
|
if (normalize) P[i/2].normalize();
|
|
std::cerr << ".";
|
|
}
|
|
}
|
|
s = s/2 + s%2;
|
|
}
|
|
G = P[0];
|
|
tt_exor = used_time(tt_start);
|
|
std::cerr << " " << lines.size() << " gen_polygons exor combined in "
|
|
<< tt_exor << std::endl;
|
|
}
|
|
|
|
@ The combination of generic polygons representing half-spaces into
|
|
larger units follows the same scheme as above. First create the
|
|
symmetric difference structure of the input objects (elementary
|
|
polygons representing half-spaces). Then calculate the intersection
|
|
and union of the two objects.
|
|
<<combination of lines into gen polygon>>=
|
|
void combine_and_time_leda(
|
|
const std::vector<Line>& lines1, const std::vector<Line>& lines2,
|
|
leda_rat_gen_polygon& G1, leda_rat_gen_polygon& G2,
|
|
leda_rat_gen_polygon& G3, leda_rat_gen_polygon& G4,
|
|
bool normalize, const char* title)
|
|
{
|
|
std::cerr << "\n" << title << "\n";
|
|
int n = lines1.size(); float t;
|
|
combine_leda(lines1,G1,normalize); t = tt_exor;
|
|
combine_leda(lines2,G2,normalize); tt_exor += t;
|
|
tt_start = used_time();
|
|
G3 = G1.intersection(G2);
|
|
tt_inter = used_time(tt_start);
|
|
tt_start = used_time();
|
|
G4 = G1.unite(G2);
|
|
tt_union = used_time(tt_start);
|
|
std::cerr << n << " gen_polygon inter " << tt_inter << std::endl;
|
|
std::cerr << n << " gen_polygon union " << tt_union << std::endl;
|
|
}
|
|
|
|
|
|
@ To determine the frame size is easy when we have a topologically
|
|
correct nef polyhedron. For standard points the coordinates are a
|
|
lower bound for the frame radius. For non-standard points the
|
|
intersection of the underlying lines with the angular bisectors define
|
|
a lower bound for the fram radius.
|
|
<<determining the frame size>>=
|
|
leda_integer min_frame_size(const Nef_polyhedronF& N)
|
|
{
|
|
typedef Nef_polyhedronF::Extended_kernel EKernel;
|
|
typedef EKernel::Standard_RT Standard_RT;
|
|
typedef Nef_polyhedronF::Explorer Explorer;
|
|
typedef Explorer::Topological_explorer TExplorer;
|
|
typedef Explorer::Vertex_const_iterator Vertex_const_iterator;
|
|
TExplorer D = N.explorer();
|
|
EKernel& E = Nef_polyhedronF::EK;
|
|
Vertex_const_iterator vit = D.vertices_begin(),
|
|
vend = D.vertices_end();
|
|
leda_integer frame_radius(0);
|
|
for (; vit != vend; ++vit) {
|
|
if ( E.is_standard(D.point(vit)) ) {
|
|
Point p = E.standard_point(D.point(vit));
|
|
Standard_RT m = std::max(p.hx()/p.hw(), p.hy()/p.hw());
|
|
frame_radius = std::max(frame_radius,m+1);
|
|
} else { // non-standard
|
|
Line l = E.standard_line(D.point(vit));
|
|
if ( abs(l.a()) != abs(l.b()) ) {
|
|
Standard_RT m = std::max( abs(l.c()/(l.a()+l.b())),
|
|
abs(l.c()/(l.a()-l.b())) );
|
|
frame_radius = std::max(frame_radius,m+1);
|
|
} else {
|
|
frame_radius = std::max(frame_radius,abs(l.c()/l.a()));
|
|
}
|
|
}
|
|
}
|
|
return frame_radius;
|
|
|
|
}
|
|
|
|
|
|
<<verbose introduction>>=
|
|
SETDTHREAD(41);
|
|
int nv = n*(n-1)/2 + 2*n + 4;
|
|
std::cerr << CGAL::pointlocationversion << std::endl;
|
|
std::cerr << CGAL::sweepversion << std::endl;
|
|
std::cerr << "creating arrangement of " << nv << " vertices\n";
|
|
|
|
<<extract command line parameters>>=
|
|
leda_param_handler H(argc,argv,".rt",false);
|
|
H.add_parameter("number_of_lines:-n:int:10");
|
|
H.add_parameter("file_of_lines:-i:string:");
|
|
H.add_parameter("verbose:-v:bool:false");
|
|
leda_param_handler::init_all();
|
|
H.get_parameter("-n",n);
|
|
H.get_parameter("-i",input_file);
|
|
H.get_parameter("-v",verbose);
|
|
|
|
<<print runtimes>>=
|
|
std::cerr << std::endl;
|
|
std::cout << setprecision(3);
|
|
std::cout << n << " & \\EXOR & "
|
|
<< (Vn[EXOR1]+Vn[EXOR2])/2 << " & " << (En[EXOR1]+En[EXOR2])/2 << " & "
|
|
<< (Fn[EXOR1]+Fn[EXOR2])/2 << " & "
|
|
<< (t_exor[NAIVE]/2) << " & " << (t_exor[FILTERED]/2) << " & "
|
|
<< (t_exor[GENPOLY_NORMALIZED]/2) << " & " << (t_exor[GENPOLY]/2)
|
|
<< "\\\\\n";
|
|
std::cout << n << " & \\INTER & "
|
|
<< Vn[INTER] << " & " << En[INTER] << " & "
|
|
<< Fn[INTER] << " & "
|
|
<< t_inter[NAIVE] << " & " << t_inter[FILTERED] << " & "
|
|
<< t_inter[GENPOLY_NORMALIZED] << " & " << t_inter[GENPOLY]
|
|
<< "\\\\\n";
|
|
std::cout << n << " & \\UNION & "
|
|
<< Vn[UNION] << " & " << En[UNION] << " & "
|
|
<< Fn[UNION] << " & "
|
|
<< t_union[NAIVE] << " & " << t_union[FILTERED] << " & "
|
|
<< t_union[GENPOLY_NORMALIZED] << " & " << t_union[GENPOLY]
|
|
<< "\\\\ \\hline\n";
|
|
|
|
<<report verbose results>>=
|
|
std::cerr << std::endl << "frame size = " << R << std::endl;
|
|
N1.explorer().print_statistics();
|
|
N2.explorer().print_statistics();
|
|
N3.explorer().print_statistics();
|
|
N4.explorer().print_statistics();
|
|
Nef_polyhedronF::EK.print_statistics();
|
|
ISOTEST(N1,NF1)
|
|
ISOTEST(N2,NF2)
|
|
ISOTEST(N3,NF3)
|
|
ISOTEST(N4,NF4)
|
|
|
|
<<write lines to a log file>>=
|
|
std::ofstream log("nef-rt.log");
|
|
CGAL_assertion_msg(log,"no output log nef-rt.log");
|
|
log << n << std::endl;
|
|
for (int i=0; i<n; ++i) log << lines1[i] << " ";
|
|
log << std::endl;
|
|
for (int i=0; i<n; ++i) log << lines2[i] << " ";
|
|
log << std::endl;
|
|
log.close();
|
|
|
|
|
|
<<visualize results>>=
|
|
CGAL::Window_stream W(600,600);
|
|
W.init(-CGAL::frame_default,CGAL::frame_default,-CGAL::frame_default);
|
|
W.set_show_coordinates(true);
|
|
W.set_grid_mode(5);
|
|
W.set_node_width(3);
|
|
W.display(0,0);
|
|
W << N3;
|
|
W.read_mouse();
|
|
W << N4;
|
|
W.read_mouse();
|
|
W.clear();
|
|
{
|
|
leda_rat_polygon p;
|
|
forall_polygons(p,G3)
|
|
W.draw_filled_polygon(p.to_polygon(),leda_black);
|
|
}
|
|
W.read_mouse();
|
|
|
|
<<tool operations>>=
|
|
template <typename P>
|
|
leda_rat_point cgal_to_leda(const P& p)
|
|
{ return leda_rat_point(p.hx().eval_at(R),p.hy().eval_at(R),
|
|
p.hw()); }
|
|
|
|
void save_times(int i)
|
|
{ t_exor[i]=tt_exor; t_inter[i]=tt_inter; t_union[i]=tt_union; }
|
|
|
|
void save_structure(const Nef_polyhedronF& N, int i)
|
|
{
|
|
Vn[i] = N.explorer().number_of_vertices();
|
|
En[i] = N.explorer().number_of_edges();
|
|
Fn[i] = N.explorer().number_of_faces();
|
|
}
|
|
|
|
#define ISOTEST(n1,n2)\
|
|
assert(n1.explorer().number_of_vertices()==\
|
|
n2.explorer().number_of_vertices());\
|
|
assert(n1.explorer().number_of_edges()==n2.explorer().number_of_edges());\
|
|
assert(n1.explorer().number_of_faces()==n2.explorer().number_of_faces());
|
|
|
|
|
|
@ \section{Printing Nef polyhedra}
|
|
<<Nef_polyhedron_2-ps.C>>=
|
|
#include <CGAL/basic.h>
|
|
#include <CGAL/Homogeneous.h>
|
|
#include <CGAL/leda_integer.h>
|
|
#include <CGAL/Extended_homogeneous.h>
|
|
#include <CGAL/Filtered_extended_homogeneous.h>
|
|
#include <CGAL/Nef_polyhedron_2.h>
|
|
#include <CGAL/IO/Nef_polyhedron_2_PS_stream.h>
|
|
#if CGAL_LEDA_VERSION < 500
|
|
#include <LEDA/stream.h>
|
|
#else
|
|
#include <LEDA/system/stream.h>
|
|
#endif
|
|
|
|
template <>
|
|
struct ring_or_field<leda_integer> {
|
|
typedef ring_with_gcd kind;
|
|
};
|
|
|
|
//typedef CGAL::Extended_homogeneous<leda_integer> EKernel;
|
|
typedef CGAL::Filtered_extended_homogeneous<leda_integer> EKernel;
|
|
typedef CGAL::Nef_polyhedron_2<EKernel> Nef_polyhedron;
|
|
|
|
using namespace CGAL;
|
|
|
|
int main(int argc, char* argv[]) {
|
|
// Create test point set. Prepare a vector for 1000 points.
|
|
if ( argc == 1 ) {
|
|
std::cout << argv[0] << " input_file\n";
|
|
return 1;
|
|
}
|
|
file_istream f(argv[1]);
|
|
if (f) {
|
|
Nef_polyhedron N;
|
|
f >> N;
|
|
ps_file PS(10,10,"nef.ps");
|
|
PS << N;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
@ \begin{ignore}
|
|
<<CGAL Header1>>=
|
|
// ============================================================================
|
|
//
|
|
// 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$
|
|
//
|
|
<<CGAL Header2>>=
|
|
// package : Nef_2
|
|
// chapter : Nef Polyhedra
|
|
//
|
|
// source : nef_2d/Nef_polyhedron_2.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: Nef polyhedra in the plane
|
|
// ============================================================================
|
|
|
|
@ \end{ignore}
|
|
\end{ignoreindiss}
|
|
%KILLSTART DISS REP
|
|
\newpage
|
|
\bibliographystyle{alpha}
|
|
\bibliography{geo_mod,comp_geo,diss}
|
|
\newpage
|
|
\section{Appendix}
|
|
\input manpages/ExtendedKernelTraits_2.man
|
|
\end{document}
|
|
%KILLEND DISS REP
|