• No results found

The Primal Arrangement Representation

Adapting to Boost Graphs

7.1 The Primal Arrangement Representation

Arrangement instances are adapted as Boost graphs by specializing the boost::graph_traits<

Graph> class template for Arrangement_2 instances. The graph traits states the graph concepts that the arrangement class models and defines the types and operations required by these concepts.

In this specialization the Arrangement_2 vertices correspond to the graph vertices, where two vertices are adjacent if there is at least one halfedge connecting them. More pre-cisely, Arrangement_2::Vertex_handle is the graph vertex-descriptor, while Arrangement_

2::Halfedge_handle is the graph edge-descriptor.1 As halfedges are directed, we consider the graph to be directed as well. Moreover, parallel edges are allowed in our Boost graph, since several interior-disjointx-monotone curves (e.g., circular arcs) may share two common endpoints, inducing an arrangement with two vertices that are connected with several edges.

As you know, it is possible to efficiently traverse the vertices and halfedges of a given Arrangement_2instance. Thus, the arrangement models the graph conceptsVertexListGraph and EdgeListGraph that are used for the traversal of all vertex descriptors and all edge descriptors of a given graph, respectively, and are introduced by the Bgl. Using an iterator adaptor of the circulator over the halfedges incident to a vertex, namely Halfedge_around_vertex_circulator (see Section 2.2.1), it is possible to go over the incoming and outgoing edges of a vertex in linear time.2 Therefore, our arrangement also models the Bgl graph concept BidirectionalGraph (this

1Vertex descriptors and edge descriptors are the terms used by Boost to describe objects that represent vertices and edges, respectively.

2The adaptor transforms the circulator into an iterator (so-called Multi-Pass Input Iterator), conforming to the

E. Fogel et al., CGAL Arrangements and Their Applications, Geometry and Computing 7, 161 DOI 10.1007/978-3-642-17283-0__7,© Springer-Verlag Berlin Heidelberg 2012

concept refines IncidenceGraph, which requires only the traversal of outgoing edges).

Notice that the vertex descriptors we use are Vertex_handle objects and not vertex indices.

However, some of the Bgl algorithms require (for efficiency reasons) the user to supply the indices 0, 1, . . . , n − 1 for the vertices, where n is the number of vertices. We therefore introduce the Arr_

vertex_index_map<Arrangement>class template, which maintains a mapping of vertex handles to indices, as required by the Bgl. An object of the appropriate instance of this class template must be attached to a valid arrangement when the map object is created. We use the notification mechanism (see Section 6.1) to dynamically maintain the mapping of vertices to indices, as new vertices might be inserted into the arrangement, and existing vertices might be removed.

Another advantage of having indices for the vertices is the use of property maps. Property maps are used by Boost to generically attach information to features. They are used by Boost algorithms to receive various parameters that correspond to features, and to output results. For example, when we compute the shortest paths from a given source vertexs to all other vertices, we use three different property maps. The first maps edges to their lengths and is passed as input to the Bgl algorithm. The other two property maps are used to hold the output; the first of them maps each vertex to its distance froms, and the second maps each vertex to the descriptor of the vertex that precedes it in the shortest path froms.

Property-map classes supplied by Boost are based on prevalent data structures. A mapping between vertices and indices allows us to easily use property-map classes supplied by Boost.

For example, the property-map class templates boost::vector_property_map, which is based on std::vector, and boost::iterator_property_map, which can be used to implement a property map based on a native C++ array, require the user to supply such a mapping. The class template boost::associative_property_mapcan be used without such a mapping.

Note, however, that when a vertex is removed from the arrangement, the indices of vertices contained in the Arr_vertex_index_map object change. As a consequence the map object is no longer synchronized with the values contained in the property map. Thus, property maps should not be reused in calls to Bgl functions if the arrangement is modified in between these calls.

Example: The first example of this chapter demonstrates the application of Dijkstra’s short-est path algorithm to compute the Euclidean shortshort-est-path length between a given vertex of an arrangement of linear curves and all other vertices. It uses an instance of the functor template Edge_length<Arrangement>to compute the Euclidean length of the linear curve associated with a given halfedge of the arrangement. The functor implements a Boost property-map that at-taches lengths to edges; when the Bgl algorithm queries the property map for a length of an edge the property map computes and returns it. The functor template is defined in the header file Edge_length.h.

#include <boost /property_map. hpp>

#include <CGAL/ b a s i c . h>

template <typename Arrangement> class Edge_length { public :

// Boost property−type definitions .

typedef boost : : readable_property_map_tag category ;

typedef double value_type ;

typedef value_type r e f e r e n c e ;

typedef typename Arrangement : : Halfedge_handle key_type ;

double operator ( ) (typename Arrangement : : Halfedge_handle e ) const {

const double diff_x = CGAL: : to_double( e−>target()−>point ( ) . x()) − CGAL: : to_double ( e−>source()−>point ( ) . x ( ) ) ;

const double diff_y = CGAL: : to_double( e−>target()−>point ( ) . y()) −

requirements of the Bgl graph concepts.

CGAL: : to_double ( e−>source()−>point ( ) . y ( ) ) ; return std : : s q r t ( diff_x∗diff_x + diff_y∗diff_y ) ; }

friend double get ( const Edge_length& edge_length , key_type key ) { return edge_length( key ) ; }

} ;

p1

v0

p2

p3

p4

p5

p6

p7 The example below constructs an arrangement of seven

line segments, as shown in the figure to the right. Then, it uses the Bgl generic implementation of Dijkstra’s shortest path algorithm to compute the distances to all vertices from the lexicographically smallest vertexv0in the arrangement.

Note the usage of the Arr_vertex_index_map class tem-plate in the call to boost::dijkstra_shortest_paths() and in the definition of the distance property-map. We in-stantiate a property map that attaches a number stored as a double-precision floating-point (double) to each vertex.

The number represents the distance of the vertex fromv0. // F i l e : ex_bgl_primal_adaptor . cpp

#include <vector>

#include <boost /vector_property_map. hpp>

#include <boost /graph/ dijkstra_shortest_paths . hpp>

#include <CGAL/ b a s i c . h>

#include <CGAL/graph_traits_Arrangement_2 . h>

#include <CGAL/Arr_vertex_index_map . h>

#include "arr_exact_construction_segments . h"

#include "Edge_length . h"

typedef CGAL: : Arr_vertex_index_map<Arrangement> Vertex_index_map;

typedef Edge_length<Arrangement> My_edge_length;

int main ( ) {

// Construct an arrangement o f seven i n t e r s e c t i n g l i n e segments .

// We keep a handle f o r the v e r t e x v0 t h a t corresponds to the point ( 1 , 1 ) . Point p1 ( 1 , 1 ) , p2 ( 1 , 4 ) , p3 ( 2 , 2 ) , p4 ( 3 , 7 ) , p5 ( 4 , 4 ) , p6 ( 7 , 1 ) , p7 ( 9 , 3 ) ; Arrangement a r r ;

Segment s (p1 , p6 ) ;

Arrangement : : Halfedge_handle e = insert_non_intersecting_curve ( arr , s ) ; Arrangement : : Vertex_handle v0 = e−>source ( ) ;

i n s e r t ( arr , Segment(p1 , p4 ) ) ; i n s e r t ( arr , Segment(p2 , p6 ) ) ; i n s e r t ( arr , Segment(p3 , p7 ) ) ; i n s e r t ( arr , Segment(p3 , p5 ) ) ; i n s e r t ( arr , Segment(p6 , p7 ) ) ; i n s e r t ( arr , Segment(p4 , p7 ) ) ; // Create a mapping o f the arrangement v e r t i c e s to i n d i c e s . Vertex_index_map index_map( a r r ) ;

// Create a property map based on s t d : : vector to keep the r e s u l t d i s t a n c e s . boost : : vector_property_map<double , Vertex_index_map>

dist_map( a r r . number_of_vertices ( ) , index_map ) ; // Perform D i j k s t r a ’ s algorithm from the v e r t e x v0 . My_edge_length edge_length ;

boost : : dijkstra_shortest_paths ( arr , v0 , boost : : vertex_index_map(index_map ) . weight_map( edge_length ) . distance_map( dist_map ) ) ; // Print the distance o f each v e r t e x from v0 .

std : : cout << "The␣graph␣ d i s t a n c e s ␣ o f ␣ the␣arrangement␣ v e r t i c e s ␣from␣ ( "

<< v0−>point () << ")␣ : " << std : : endl ; Arrangement : : Vertex_iterator v i t ;

for ( v i t = a r r . ver tices_beg in ( ) ; v i t != a r r . vertices_end ( ) ; ++v i t ) std : : cout << " ( " << vit−>point () << ")␣at␣distance ␣"

<< dist_map [ v i t ] << std : : endl ; return 0 ;

}