Arrangement-Traits Classes
5.4 Traits Classes for Algebraic Curves
5.4.5 A Traits Class for Planar Algebraic Curves of Arbitrary Degree
5.4.5 A Traits Class for Planar Algebraic Curves of Arbitrary Degree
The 2D Arrangements package of Version 3.7 included for the first time a traits class that supports planar algebraic curves of arbitrary degree. The traits class, namely Arr_algebraic_segment_
traits_2, is based on the Algebraic_kernel_d_1 class template, which models the algebraic-kernel conceptAlgebraicKernel_d_1; see Section5.4.3. The traits class handles (i) algebraic curves and (ii) continuous (weakly) x-monotone segments of algebraic curves, which, however, are not necessarily maximal. (A formal definition is given below.) Observe that non-x-monotone segments are not supported. Still, it is the traits class that supports the most general type of curves among the traits classes included in the package.
Recall that an algebraic curveC in the plane is defined as the (real) zero set of a bivariate polynomialf(x, y). The curve is uniquely defined by f, although several polynomials may define the same curve. We callf a defining polynomial of C.
A formal definition of (weakly)x-monotone segments of algebraic curves follows. A point p on a curve Cf ⊂ R2 (withf its defining polynomial) is called semi-regular if, locally around p, Cf
can be written as a function graph of some continuous function inx or in y. (We also say that p is parameterizable inx or y, respectively.) The only two cases of non-semi-regular points are isolated points and self-intersections. A segment of a curve in this context is a closed and continuous point set such that each interior point is semi-regular. It follows that a segment is either vertical or a closed connected point set, with all interior points parameterizable inx.
Every instance of the Arr_algebraic_segment_traits_2<Coefficient> class template mod-els the ArrangementTraits_2 and ArrangementOpenBoundaryTraits_2 concepts (but it does not model theArrangementLandmarkTraits_2 concept). The template parameter Coefficient deter-mines the type of the scalar coefficients of the polynomial. Currently supported integral number types are Gmpz, leda::integer, and CORE::BigInt. This is reflected in the statements included in the header file integer_type.h, the listings of which are omitted here. This header file is used by the two example programs listed in this section. The template parameter Coefficient can be substituted in addition with an instance of the Sqrt_extension<NT,Root> class template, where the template parameters NT and Root are substituted in turn with one of the integral num-ber types above. Finally, the template parameter Coefficient can be substituted also with a rational number type, where the type of the numerator and denominator is one of the types above.
The type Curve_2 nested in the Arr_algebraic_segment_traits_2 class template defines an algebraic curve. An object of this type can be constructed by the Construct_curve_2 functor also nested in the class template. Its function operator accepts as an argument an object of type Polynomial_2, nested as well in the traits class-template. The type Polynomial_2 models the concept Polynomial_d. An object of the nested type Polynomial_2 represents a bivariate polynomial. It can be constructed in a few convenient ways, some are exemplified by the programs listed below. Consult the reference guide for the complete set of options.
Example: The example below computes the arrangement depicted in Figure 5.5a. The arrange-ment is induced by four algebraic curves,C1,C2,C3, andC4, of degrees1, 2, 3, and 6, respectively.
For each curve the defining polynomial is constructed first. Then, the algebraic curve is constructed using the Construct_curve_2 functor. Finally, the curve is inserted into the arrangement.
// F i l e : ex_algebraic_curves . cpp
#include <iostream>
C1: 3x − 5y − 2 = 0 C2: x2+ 3y2− 10 = 0 C3: x2+ y2+ xy2= 0 C4: x6+ y6− x3y3− 12 = 0
C1
C2 C3
C4
(a) An arrangement of four algebraic curves, as con-structed by the program coded in ex_algebraic_
curves.cpp.
C5: x = 0 C3: x2− y3= 0 C4: y2− x2+ 1 = 0 C1: x4+ y3− 1 = 0 C2: 2x2+ 5y2− 7 = 0
p3 p2
p1
(b) An arrangement of five algebraic segments and three isolated points, as constructed by the program coded in algebraic_segments.cpp. The supporting curves are drawn as dashed lines.
Fig. 5.5: Arrangements of algebraic curves.
#include <CGAL/ b a s i c . h>
#include <CGAL/Gmpz. h>
#include <CGAL/Arrangement_2 . h>
#include <CGAL/Arr_algebraic_segment_traits_2 . h>
#include "integer_type . h"
#include " arr_print . h"
typedef CGAL: : Arr_algebraic_segment_traits_2<Integ er> Tr a its ; typedef CGAL: : Arrangement_2<Traits> Arrangement ;
typedef Tr a its : : Curve_2 Curve ;
typedef Tr a its : : Polynomial_2 Polynomial ;
int main ( ) {
CGAL: : set_pretty_mode( std : : cout ) ; // f o r nice p r i n t o u t s . Tr a its t r a i t s ;
Arrangement a r r(& t r a i t s ) ;
// Functor to create a curve from a Polynomial .
Tr a its : : Construct_curve_2 construct_cv = t r a i t s . construct_curve_2_object ( ) ; Polynomial x = CGAL: : s h i f t ( Polynomial ( 1 ) , 1 , 0 ) ;
Polynomial y = CGAL: : s h i f t ( Polynomial ( 1 ) , 1 , 1 ) ;
// Construct an unbounded l i n e (C1) with the equation 3x − 5y − 2 = 0.
Polynomial f 1 = 3∗x − 5∗y − 2;
Curve cv1 = construct_cv( f 1 ) ;
std : : cout << " I n s e r t i n g␣curve␣" << f 1 << std : : endl ; CGAL: : i n s e r t ( arr , cv1 ) ;
// Construct the e l l i p s e (C2) with the equation x^2 + 3∗y^2 − 10 = 0.
Polynomial f 2 = CGAL: : ipower ( x , 2) + 3∗CGAL: : ipower (y , 2) − 10;
Curve cv2 = construct_cv( f 2 ) ;
std : : cout << " I n s e r t i n g␣curve␣" << f 2 << std : : endl ; CGAL: : i n s e r t ( arr , cv2 ) ;
// Construct a cu bic curve (C3) with the equation x^2 + y^2 + xy^2 = 0 , // with i s o l a t e d point at (0 ,0) and v e r t i c a l asymptote at x = 1 .
Polynomial f 3 = CGAL: : ipower ( x , 2) + CGAL: : ipower ( y , 2) + x∗CGAL: : ipower (y , 2);
Curve cv3 = construct_cv( f 3 ) ;
std : : cout << " I n s e r t i n g␣curve␣" << f 3 << std : : endl ; CGAL: : i n s e r t ( arr , cv3 ) ;
// Construct a curve o f degree 6 (C4) with the equation // x^6 + y^6 − x^3y^3 − 12 = 0.
Polynomial f 4 = CGAL: : ipower ( x , 6) + CGAL: : ipower ( y , 6) − CGAL: : ipower (x , 3)∗CGAL: : ipower (y , 3) − 12;
Curve cv4 = construct_cv( f 4 ) ;
std : : cout << " I n s e r t i n g␣curve␣" << f 4 << std : : endl ; CGAL: : i n s e r t ( arr , cv4 ) ;
print_arrangement_size ( a r r ) ; // p r i n t the arrangement s i z e return 0 ;
}
The Arr_algebraic_segment_traits_2 class template carries state to expedite some of its computations. Thus, it is essential to have only one copy of the traits object during the life time of a program that utilizes this traits class. To this end, the example above uses the constructor of the Arrangement_2 data structure that accepts the traits object as input. Carrying state is not a unique property of the Arr_algebraic_segment_traits_2 class template; it is common to many traits classes, especially to traits classes that handle algebraic curves. Therefore, as a general rule, if your application requires direct access to a traits object, define it locally, and pass it to the constructor of the Arrangement_2 data structure to avoid the construction of a duplicate traits-class object.
A weaklyx-monotone segment of an algebraic curve is represented by the X_monotone_curve_
2type nested in the traits class-template. You can construct such segments in two ways as follows:
(i) using the Make_x_monotone_2 functor or (ii) using the Construct_x_monotone_segment_2 functor. Both functors are nested in the traits class-template. The former is required by the con-cept ArrangementTraits_2 our traits class models; see Section5.1.3. The latter enables the con-struction of individual segments. The X_monotone_curve_2 type represents weaklyx-monotone segments of a curve; however, for technical reasons, segments may need to be further subdi-vided into several sub-segments, called terminal segments. Therefore, Construct_x_monotone_
segment_2 constructs a sequence of X_monotone_curve_2 objects, whose union represents the desired weakly x-monotone segment. The function operator of the Construct_x_monotone_
segment_2 functor accepts as arguments the underlying algebraic curve, the leftmost point of the segment, the rightmost point of the segment, and an output iterator associated with a con-tainer of output terminal segments. The function operator is overloaded. In addition to the variant above, there is one that accepts the underlying algebraic curve, a single point p, an enumerator that delimits the segment, and an output iterator. It returns the maximal x-monotone segment that contains p. The enumerator specifies whether p is interior to the returned segment, its left
endpoint, or its right endpoint. The third variant accepts only two delimiting points and an output iterator. It constructs line segments.
advanced
The subdivision into terminal segments is due to the internal representation of x-monotone segments, which is based on a vertical decomposition. We assume the defining polynomialf of the curveC to be square-free. That is, it contains no divisor g2 of total degree greater than zero. We define a (complex) critical point p ∈ C2 byf(p) = 0 = ∂f∂y(p). An x-coordinate α ∈ R is critical, either if some critical point has x-coordinate α, or if the leading coefficient of f, considered as a polynomial iny, vanishes. In particular, vertical lines and isolated points of C can only take place at critical x-coordinates. Between two consecutive critical x-coordinates the curve decomposes into a finite number of x-monotone segments (the same holds on the left of the leftmost and on the right of the rightmost criticalx-coordinate). The type X_monotone_curve_2 is only capable of representing such segments (and sub-segments of them). Formally, a terminal segment is either a vertical line-segment or a segment of anx-monotone curve whose x-range does not contain critical points in its interior. The figure below depicts a quartic curve and its decomposition into terminal segments. Notice that six vertices split the curve into x-monotone segments, and four additional vertices further split the correspondingx-monotone segments into terminal segments.
advanced
The type Algebraic_real_1 must be defined by any model of theAlgebraicKernel_d_1 con-cept. The traits class-template Arr_algebraic_segment_traits_2 exploits an instance of the Algebraic_kernel_d_1class template, which models the conceptAlgebraicKernel_d_1. The ex-ploited instance is nested in the traits class-template. You can use this model to create algebraic numbers as roots of univariate polynomials, and process them, for instance, compare them or ap-proximate them to any precision. See the documentation of the conceptAlgebraicKernel_d_1 for more information. Coordinates of points are represented by the type Algebraic_real_1 nested in the traits class-template. This type is defined as the corresponding type nested in the instance of Algebraic_kernel_d_1.
q p
You can construct an object of type Point_2 using the Construct_
point_2functor nested in the traits class-template. Its function operator is overloaded with a couple of variants that accepts thex and y coordinates of the point. Their types must be either Algebraic_real_1 or Coefficient.
Another efficient variant accepts a triplex, C, i, which identifies the ith point (counted from the bottom) in the fiber of C at the x-coordinate x.20 In the example depicted in the figure to the right, ifxp denotes the x-coordinate of p, and C represents the algebraic curve, then p could be represented by xp, C, 3. If xq is thex-coordinate of q, then xq, C, 1 is a valid representation ofq. Points are presented internally using the triple described above. The y-coordinates of points are not explicitly stored.
Although they-coordinate of a point represented by an object of the nested type Algebraic_real_1 can be obtained, we advise caution with that option, since computing an explicit representation of they-coordinate can be rather expensive.
Try: The curve depicted in the figure to the right belongs to a family of curves called Ovals of Cassini. A curve in this family is the locus of points such that for each point the product of its distances from two fixed points, a distance2a apart, is a constant b2. In the figure abovea = 1 and b2 =
4/3. The polynomial (y2+ x2+ 1)2− 4y2 = 4/3 defines the curve. Write a program that constructs an arrangement induced by this curve. Verify that the resulting arrangement consists of 2 faces, 10 edges, and 10 vertices.
20The fiber of a curveC at some x-coordinate x is the set of all points onC with x-coordinate x. Formally, for a curveC and x∈ R, the fiber of C at x isC ∩ {(x, b) | b ∈ R}.
Example: The program listed below exemplifies the method to construct points and the various methods to construct algebraic segments. The computed arrangement is depicted in Figure5.5b.
// F i l e : ex_algebraic_segments . cpp
#include <iostream>
#include <CGAL/ b a s i c . h>
#include <CGAL/Gmpz. h>
#include <CGAL/Arrangement_2 . h>
#include <CGAL/Arr_algebraic_segment_traits_2 . h>
#include "integer_type . h"
#include " arr_print . h"
typedef CGAL: : Arr_algebraic_segment_traits_2<Integ er> Tr a its ; typedef CGAL: : Arrangement_2<Traits> Arrangement ;
typedef Tr a its : : Curve_2 Curve ;
typedef Tr a its : : Polynomial_2 Polynomial ;
typedef Tr a its : : Algebraic_real_1 Algebraic_real ;
typedef Tr a its : : X_monotone_curve_2 X_monotone_curve;
typedef Tr a its : : Point_2 Point ;
int main ( ) {
Tr a its t r a i t s ;
Tr a its : : Make_x_monotone_2 make_xmon = t r a i t s . make_x_monotone_2_object ( ) ; Tr a its : : Construct_curve_2 ctr_cv = t r a i t s . construct_curve_2_object ( ) ; Tr a its : : Construct_point_2 ctr_pt = t r a i t s . construct_point_2_object ( ) ; Tr a its : : Construct_x_monotone_segment_2 construct_xseg =
t r a i t s . construct_x_monotone_segment_2_object( ) ; Polynomial x = CGAL: : s h i f t ( Polynomial ( 1 ) , 1 , 0 ) ; Polynomial y = CGAL: : s h i f t ( Polynomial ( 1 ) , 1 , 1 ) ;
// Construct a curve (C1) with the equation x^4+y^3−1=0.
Curve cv1 = ctr_cv (CGAL: : ipower (x , 4) + CGAL: : ipower (y , 3) − 1);
// Construct a l l x−monotone segments using the Make_x_mononotone functor . std : : vector<CGAL: : Object> pre_segs ;
make_xmon( cv1 , std : : back_inserter ( pre_segs ) ) ; // Cast a l l CGAL: : Objects i n t o X_monotone_segment
// ( the vector might a l s o contain Point o b j e c t s f o r i s o l a t e d points , // but not in t h i s case ) .
std : : vector<X_monotone_curve> seg s ;
for ( size_t i = 0 ; i < pre_segs . s i z e ( ) ; ++i ) { X_monotone_curve cur r ;
bool check = CGAL: : a s s i g n ( curr , pre_segs [ i ] ) ; CGAL_assertion( check ) ;
seg s . push_back ( cur r ) ; }
// Construct an e l l i p s e (C2) with the equation 2∗x^2+5∗y^2−7=0.
Curve cv2 = ctr_cv (2∗CGAL: : ipower (x,2)+5∗CGAL: : ipower (y,2) −7);
// Construct point on the upper arc ( counting o f arc numbers s t a r t s with 0 ) . Point p11 = ctr_pt ( Algebraic_real ( 0 ) , cv2 , 1 ) ;
construct_xseg ( cv2 , p11 , Tr a its : :POINT_IN_INTERIOR, std : : back_inserter ( seg s ) ) ;
// Construct a v e r t i c a l cusp (C3) with the equation x^2−y^3=0.
Curve cv3 = ctr_cv (CGAL: : ipower (x , 2)−CGAL: : ipower (y , 3));
// Construct a segment containing the cusp point .
// This adds two X_monotone_curve o b j e c t s to the vector , // because the cusp i s a c r i t i c a l point .
Point p21 = ctr_pt ( Algebraic_real (−2), cv3 , 0);
Point p22 = ctr_pt ( Algebraic_real ( 2 ) , cv3 , 0 ) ;
construct_xseg ( cv3 , p21 , p22 , std : : back_inserter ( seg s ) ) ; // Construct an unbounded curve , s t a r t i n g at x=3.
Point p23 = ctr_pt ( Algebraic_real ( 3 ) , cv3 , 0 ) ;
construct_xseg ( cv3 , p23 , Tr a its : :MIN_ENDPOINT, std : : back_inserter ( seg s ) ) ; // Construct another conic (C4) with the equation y^2−x^2+1=0.
Curve cv4 = ctr_cv (CGAL: : ipower (y,2)−CGAL: : ipower (x,2)+1);
Point p31 = ctr_pt ( Algebraic_real ( 2 ) , cv4 , 1 ) ;
construct_xseg ( cv4 , p31 , Tr a its : :MAX_ENDPOINT, std : : back_inserter ( seg s ) ) ; // Construct a v e r t i c a l segment (C5 ) .
Curve cv5 = ctr_cv ( x ) ;
Point v1 = ctr_pt ( Algebraic_real ( 0 ) , cv3 , 0 ) ; Point v2 = ctr_pt ( Algebraic_real ( 0 ) , cv2 , 1 ) ;
construct_xseg ( cv5 , v1 , v2 , std : : back_inserter ( seg s ) ) ; Arrangement a r r(& t r a i t s ) ;
CGAL: : i n s e r t ( arr , seg s . begin ( ) , seg s . end ( ) ) ;
// Add some i s o l a t e d p o i n t s (must be wrapped i n t o CGAL: : Object ) . std : : vector<CGAL: : Object> iso la ted_pts ;
// p1
iso la ted_pts . push_back (CGAL: : make_object( ctr_pt ( Algebraic_real ( 2 ) , cv4 , 0 ) ) ) ;
// p2
iso la ted_pts . push_back (CGAL: : make_object( ctr_pt ( I n t e g e r ( 1 ) , I n t e g e r ( 2 ) ) ) ) ; // p3
iso la ted_pts . push_back (CGAL: : make_object( ctr_pt ( Algebraic_real(−1), Algebraic_real ( 2 ) ) ) ) ; CGAL: : i n s e r t ( arr , iso la ted_pts . begin ( ) , iso la ted_pts . end ( ) ) ;
print_arrangement_size ( a r r ) ; // p r i n t the arrangement s i z e return 0 ;
}