Arrangements of Unbounded Curves
4.1 Representing Arrangements of Unbounded Curves
4.1.1 Basic Manipulation and Traversal Methods
The types Vertex, Halfedge, and Face nested in the Arrangement_2 class template support the methods described below in addition to the ones listed in Section 2.2.1. Letv, e, and f be handles to a vertex of type Vertex, a halfedge of type Halfedge, and a face of type Face, respectively.
• The calls v->parameter_space_in_x() and v->parameter_space_in_y() determine the location of the geometric embedding of the vertexv. The call v->parameter_space_in_x() returns CGAL::ARR_INTERIOR if the x-coordinate associated with v is finite, CGAL::ARR_
LEFT_BOUNDARY if v represents the end of a curve the x-coordinate of which approaches
−∞, and CGAL::ARR_RIGHT_BOUNDARY if v represents the end of a curve the x-coordinate of which approaches∞. Similarly, the call v->parameter_space_in_y() returns CGAL::ARR_
INTERIORif the y-coordinate associated with v is finite and CGAL::ARR_BOTTOM_BOUNDARY or CGAL::ARR_TOP_BOUNDARY if v represents the end of a curve the y-coordinate of which approaches−∞ or ∞, respectively.1
The nested Vertex type also provides the Boolean predicate is_at_open_boundary(). If the call v->is_at_open_boundary() predicates evaluates to false, you can access the point associated withv. Otherwise, v is not associated with a Point_2 object, as it represents the unbounded end of a curve that approaches infinity.
• The nested Halfedge type provides the Boolean predicate is_fictitious(). If the call e->is_fictitious()predicate evaluates to false, you can access the x-monotone curve associated withe. Otherwise, e is not associated with an X_monotone_curve object.
• The nested Face type provides the Boolean predicate is_fictitious(). The call f->is_
fictitious()predicate evaluates to true only whenf lies outside the bounding rectangle.
The method outer_ccb() (see Section 2.2.1) must not be invoked for the fictitious face.2 Note that a valid unbounded face (of an arrangement that supports unbounded curves) has a valid outer Ccb, although the Ccb comprises only fictitious halfedges if the arrangement is induced only by bounded curves.
Let pl = (xl, yl) and pr = (xr, yr) be the left and right endpoints of a boundedx-monotone curve c, respectively. We say that a point p = (xp, yp) lies in thex-range of c if xl≤ xp≤ xr. All the points in the figure to the right are in thex-range of the curve in the figure. Let pl= (xl, yl) be the left endpoint of an x-monotone curve c unbounded from the right. A point p = (xp, yp) lies in thex-range of c if xl≤ xp. Similarly, A pointp = (xp, yp) lies in the x-range of an x-monotone curve unbounded from the left if xp ≤ xr. Naturally, every
pointp ∈ R2is in thex-range of an x-monotone curve unbounded from the left and from the right.
The template function is_in_x_range() listed below, and defined in the file is_in_x_range.h, checks whether a given point p is in the x-range of the curve associated with a given halfedge e.
The function template also exemplifies how some of the above functions can be used.
template <typename Arrangement>
bool is_in_x_range(typename Arrangement : : Halfedge_const_handle he , const typename Arrangement : : Point_2& p ,
const typename Arrangement : : Traits_2& t r a i t s ) {
1The term “parameter space” stems from a major extension the 2D Arrangements package was going through at the time this book was written to support arrangements embedded on certain two-dimensional parametric surfaces in three (or higher) dimensions; see Section 11.1.
2Typically, the code is guarded against such malicious calls with adequate preconditions. However, these preconditions are suppressed when the code is compiled with maximum optimization.
typedef typename Arrangement : : Traits_2 Traits_2 ; typename Traits_2 : : Compare_x_2 cmp = t r a i t s . compare_x_2_object( ) ; // Compare p with the source v e r t e x ( which may l i e at x = +/− oo ).
CGAL: : Arr_parameter_space src_px = he−>source()−>parameter_space_in_x( ) ; CGAL: : Comparison_result res_s =
( src_px == CGAL: :ARR_LEFT_BOUNDARY) ? CGAL: :SMALLER : ( ( src_px == CGAL: :ARR_RIGHT_BOUNDARY) ? CGAL: :LARGER :
cmp( he−>source()−>point () , p ) ) ; i f ( res_s == CGAL: :EQUAL) return true ;
// Compare p with the t a r g e t v e r t e x ( which may l i e at x = +/− oo ).
CGAL: : Arr_parameter_space trg_px = he−>target()−>parameter_space_in_x( ) ; CGAL: : Comparison_result res_t =
( trg_px == CGAL: :ARR_LEFT_BOUNDARY) ? CGAL: :SMALLER : ( ( trg_px == CGAL: :ARR_RIGHT_BOUNDARY) ? CGAL: :LARGER :
cmp( he−>target()−>point () , p ) ) ;
// p l i e s in the x−range of the halfedge i f f i t s source and target l i e // at opposite x−positions .
return ( res_s != res_t ) ; }
It is important to observe that the call arr.number_of_vertices() does not count the ver-tices at infinity in the arrangement arr. To find out this number you need to issue the call arr.number_of_vertices_at_infinity(). Similarly, arr.number_of_edges() does not count the fictitious edges (whose number is always arr.number_of_vertices_at_infinity() + 4) and arr.number_of_faces()does not count the fictitious faces. The vertex, halfedge, edge, and face iterators defined by the Arrangement_2 class template only go over true features of the arrange-ment; namely, vertices at infinity and fictitious halfedges and fictitious faces are skipped. On the other hand, the Ccb_halfedge_circulator of the outer boundary of an unbounded face or the Halfegde_around_vertex_circulator of a vertex at infinity do traverse fictitious halfedges.
While an arrangement induced by bounded curves has a single unbounded face, an arrangement induced by unbounded curves may have several unbounded faces. The calls arr.unbounded_
faces_begin() and arr.unbounded_faces_end() return iterators of the type Arrangement_
2::Unbounded_face_iterator (or its non-mutable, const, counterpart type) that define the range of the arrangement unbounded faces.
There is no way to directly obtain the fictitious vertices that represent the four corners of the imaginary bounding rectangle. However, you can obtain the fictitious face through the call arr.fictitious_face(), and then iterate over the boundary of its single hole, which represents the imaginary bounding rectangle. Recall that an arrangement of bounded curves does not have a fictitious face. In this case the call above returns a null handle.
Example: The example below exhibits the difference between an arrangement induced by bounded curves, which has a single unbounded face, and an arrangement induced by unbounded curves, which may have several unbounded faces. It also demonstrates the usage of the insertion func-tion for pairwise interior-disjoint unbounded curves. In this case we use an instance of the traits class-template Arr_linear_traits_2<Kernel> to substitute the Traits parameter of the Arrangement_2<Traits,Dcel> class template when instantiated. This traits class-template is capable of representing line segments as well as unbounded linear curves, namely, lines and rays.
Observe that objects of the X_monotone_curve_2 type nested in this traits class-template are constructible from objects of types Line_2, Ray_2, and Segment_2 also nested in the traits class-template. These three types and the Point_2 type are defined by the traits class-template Arr_
linear_traits_2<Kernel>to be the corresponding types of the kernel used to instantiate the traits class-template; see Section 5.2.3.
The type definitions used by the example below, as well as by other examples that use the Arr_linear_traits_2, are listed next. These types are defined in the header file Arr_linear.h.
#include <CGAL/Exact_predicates_exact_constructions_kernel . h>
#include <CGAL/Arr_linear_traits_2 . h>
#include <CGAL/Arrangement_2 . h>
typedef CGAL: : Exact_predicates_exact_constructions_kernel Kernel ;
typedef Kernel : :FT Number_type ;
typedef CGAL: : Arr_linear_traits_2<Kernel> Tr a its ;
typedef Tr a its : : Point_2 Point ;
typedef Tr a its : : Segment_2 Segment ;
typedef Tr a its : : Ray_2 Ray ;
typedef Tr a its : : Line_2 Line ;
typedef Tr a its : : X_monotone_curve_2 X_monotone_curve;
typedef CGAL: : Arrangement_2<Traits> Arrangement;
typedef Arrangement : : Vertex_handle Vertex_handle ;
typedef Arrangement : : Halfedge_handle Halfedge_handle ;
typedef Arrangement : : Face_handle Face_handle ;
v c4
c5
c1
c2
c3
c6
The first three curves, c1, c2, and c3, are inserted using the specialized insertion functions forx-monotone curves whose location in the arrangement is known. Notice that inserting an unbounded curve in the interior of an unbounded face, or from an existing vertex that represents an bounded end of the curve, may cause an unbounded face to split. (This is never the case when inserting a bounded curve—compare with Section 2.2.2.) Three additional rays are then inserted incrementally using the insertion function forx-monotone curves, the interior of which is disjoint from all arrangement features; see the illustration in the figure to the right. Finally, the program prints the size
of the arrangement using the template function print_unbounded_arrangement_size() defined in header file arr_print.h. (Its listing is omitted here.) The program also traverses the outer boundaries of its six unbounded faces and prints the curves along these boundaries. The header file arr_linear.h, which contains the definitions used by the example program, is listed immediately after the listing of the main function.
// F i l e : ex_unbounded_non_intersecting . cpp
#include " a rr_linear . h"
#include " arr_print . h"
int main ( ) {
Arrangement a r r ;
// I n s e r t a l i n e in the ( c u r r e n t l y s i n g l e ) unbounded face o f the arrangement , // then s p l i t i t i n t o two at ( 0 , 0 ) . Assign v to be the s p l i t point .
X_monotone_curve c1 = Line ( Point (−1, 0) , Point (1 , 0));
Halfedge_handle e1 = a r r . inser t_in_fa ce_inter io r ( c1 , a r r . unbounded_face ( ) ) ; X_monotone_curve c1 _left = Ray( Point ( 0 , 0 ) , Point (−1, 0));
X_monotone_curve c1_right = Ray( Point ( 0 , 0 ) , Point ( 1 , 0 ) ) ; e1 = a r r . split_edge ( e1 , c1_left , c1_right ) ;
Vertex_handle v = e1−>target ( ) ;
CGAL_assertion( ! v−>is_at_open_boundary ( ) ) ;
// Add two more rays using the s p e c i a l i z e d i n s e r t i o n f u n c t i o n s .
a r r . insert_from_right_vertex(Ray( Point ( 0 , 0 ) , Point (−1, 1)) , v ) ; // c2 a r r . insert_from_left_vertex (Ray( Point ( 0 , 0 ) , Point ( 1 , 1 ) ) , v ) ; // c3 // I n s e r t three more i n t e r i o r−disjoint rays , c4 , c5 , and c6 .
insert_non_intersecting_curve ( arr , Ray( Point ( 0 , −1), Point(−2, −2)));
insert_non_intersecting_curve ( arr , Ray( Point ( 0 , −1), Point (2 , −2)));
insert_non_intersecting_curve ( arr , Ray( Point ( 0 , 0 ) , Point ( 0 , 1 ) ) ) ; print_unbounded_arrangement_size( a r r ) ;
// Print the outer CCBs o f the unbounded f a c e s . int k = 1 ;
Arrangement : : Unbounded_face_const_iterator i t ;
for ( i t = a r r . unbounded_faces_begin ( ) ; i t != a r r . unbounded_faces_end ( ) ; ++i t ) {
std : : cout << "Face␣no . ␣" << k++ << " ( " << i t−>number_of_outer_ccbs()
<< " , " << i t−>number_of_inner_ccbs () << ")" << " : ␣" ; Arrangement : : Ccb_halfedge_const_circulator f i r s t , cur r ; curr = f i r s t = i t−>outer_ccb ( ) ;
i f ( ! curr−>source()−>is_at_open_boundary())
std : : cout << " ( " << curr−>source()−>point () << ")" ; do {
Arrangement : : Halfedge_const_handle he = cur r ;
i f ( ! he−>i s _ fi c ti ti o u s ()) std : : cout << "␣␣␣ [ " << he−>curve () << " ] ␣␣␣" ; else std : : cout << "␣␣␣ [ ␣ . . . ␣ ] ␣␣␣" ;
i f ( ! he−>target()−>is_at_open_boundary())
std : : cout << " ( " << he−>target()−>point () << ")" ; } while (++cur r != f i r s t ) ;
std : : cout << std : : endl ; }
return 0 ; }