-
Notifications
You must be signed in to change notification settings - Fork 92
Mapping
For the moment, no documentation but the one from the CGAL manual is available. On this page we give general principals and exceptions of how the mapping between C++ and target languages is done.
In general all classes and functions in CGAL have template parameters. Since template parameters are not supported in all target languages of SWIG, each instantiation of a template class or function must be named. For principal kernel primitives, the choice that has been made is to propose primitives instantiated with Exact_predicates_inexact_constructions_kernel) and to keep their name in the target languages. For example a Java object of type Point_2
in Java internally corresponds to a C++
object of type Point_2<Exact_predicates_inexact_constructions_kernel>
. This kernel provides safe and exact predicates but no guarantee for constructions. However, for intersection
global functions, we use filtered constructions: the intersection is computed with an exact kernel, but an embedding with floating points coordinates is returned.
SWIG has the notion of module that they document like this:
Each invocation of SWIG requires a module name to be specified. The module name is used to name the resulting target language extension module. Exactly what this means and what the name is used for depends on the target language, for example the name can define a target language namespace or merely be a useful name for naming files or helper classes. Essentially, a module comprises target language wrappers for a chosen collection of global variables/functions, structs/classes and other C/C++ types.
Internally, CGAL is organized into packages which basically gather a set of related data structures and algorithms. To increase the flexibility, we choose to represent each CGAL package as a SWIG module (see this page for the list of packages currently available).
- SWIG cannot handle nested classes. A nested class in CGAL is exposed as a non-nested class in the bindings with the name prefixed by the name of the containing class, e.g.,
Delaunay_triangulation_2_Vertex_handle
. - There is no direct support for class iterators, input iterators and output iterators so that for each target language, specific code should be written. This is currently done for Python and Java. A generic solution is provided for non-supported target languages.
Since some target languages do not support passing primitive types (such as int, double, enum) by reference, whenever a reference to such a type is required we use a template class Reference_wrapper<T>
which provides methods T object()
and void set(T)
to mimic the passing by reference mechanism. For example, in the package Kernel
we provide a class Ref_int
which is an integer wrapped in the aforementioned class.
CGAL::Object are implemented using template functions. As this mechanism is not available in target languages, we explicitly added functions to check the type of object stored and to retrieve it. For each object type FOO
in the kernel, bool is_FOO()
and FOO get_FOO()
are available. Also an important remark is that for technical reason, the AABB_tree package provides its own Object
type that has the same API but is not compatible with the one from the Kernel.
Example in Java:
import CGAL.Kernel.Point_3;
import CGAL.Kernel.Segment_3;
import CGAL.Kernel.CGAL_Kernel;
import CGAL.Kernel.Object;
....
Segment_3 seg1=new Segment_3(new Point_3(-1,0,0),new Point_3(1,0,0));
Segment_3 seg2=new Segment_3(new Point_3(0,1,0),new Point_3(0,-1,0));
Object obj=CGAL_Kernel.intersection(seg1,seg2);
assert obj.is_Point_3();
System.out.println(obj.get_Point_3());
Example in Python:
from CGAL import CGAL_Kernel
from CGAL.CGAL_Kernel import Point_2
from CGAL.CGAL_Kernel import Triangle_2
t1=Triangle_2(Point_2(0,0),Point_2(1,0),Point_2(0,1))
t2=Triangle_2(Point_2(1,1),Point_2(1,0),Point_2(0,1))
object = CGAL_Kernel.intersection(t1,t2)
assert object.is_Segment_2()
print object.get_Segment_2()
boost::optional<T> are usually wrapped to an object Optional_T
and its API is bool empty()
and T value()
that respectively checks that the optional is empty and returns the corresponding value.
The organization into modules implies a specific way to import types in Java. A class wrapped in a SWIG module named FOO
will be inside the package CGAL.FOO
in Java. For example, to use the Point_2
class, one needs to do import CGAL.Kernel.Point_2;
Global functions wrapped in a SWIG module named FOO
are available as static methods of the class CGAL.FOO.CGAL_FOO
.
Example:
import CGAL.Kernel.Point_2;
import CGAL.Kernel.CGAL_Kernel;
Point_2 p1=new Point_2(0,0);
Point_2 p2=new Point_2(1,1);
double d= CGAL_Kernel.squared_distance(p1,p2);
The organization into modules implies a specific way to import types in Java. A class wrapped in a SWIG module named FOO
will be inside the package CGAL.FOO
in Java, and in the module CGAL.CGAL_FOO
in Python. For example, to use the Point_2
class, one needs in Java to do import CGAL.Kernel.Point_2;
and from CGAL.CGAL_Kernel import Point_2
in Python.
Global functions wrapped in a SWIG module named FOO
are available as static methods of the class CGAL.FOO.CGAL_FOO
in Java and in the module CGAL.CGAL_FOO
in Python.
Since each SWIG generated class has a finalize()
method, it is costly to collect by the garbage collector. To avoid this overhead, each method returning an object generated by SWIG exits in another version where the first argument takes an already allocated Java object of the correct type and updates it.
Example:
Sphere_3 s = XXX;
//you can write
Point_3 c = s.center();
//or
s.center(c);
Pseudo-nested iterator on objects of type ObjectA
is both iterator over ObjectA
and iterable range of ObjectA
(that is to say these classes implements Iterable<ObjectA>, Iterator<ObjectA>
).
Example:
Delaunay_triangulation_2 dt2=XXX;
for (Point_2 p : dt2.points()){
//do something
}
Delaunay_triangulation_2_Point_iterator points=dt2.points();
while( points.hasNext() ){
Point_2 p=points.next();
//do something
}
For efficiency reason, each pseudo-nested iterator on objects of type ObjectA
uses a private internal member ObjectA objectInstance
that is returned by the method ObjectA next()
. When next()
is called,objectInstance
. This implies that to keep a copy of a given object during iteration, a deep copy must be done by calling ObjectA ObjectA.clone()
or void ObjectA.clone(ObjectA)
. Note that the method ObjectA slow_next()
makes such a deep copy of the next element.
Example:
Point_2 escaped=null;
int i=0;
for (Point_2 p : delaunay_triangulation.points() )
{
i+=1;
if (i==10) escaped=p.clone();
}
//the 10th point need to be cloned to be correct
System.out.print(escaped);
Output iterator expecting object of type ObjectA
is mapped to Collection<ObjectA>
from java.util
.
Example:
LinkedList<Constrained_Delaunay_triangulation_2_Edge> edge_list=
new LinkedList<Constrained_Delaunay_triangulation_2_Edge>();
Constrained_Delaunay_triangulation_2 cdt=XXX;
Constrained_Delaunay_triangulation_2_Vertex_handle v=XXX;
//collect all constrained edges incident to a vertex
cdt2.incident_constraints(v,edge_list);
Input iterator range of object of type ObjectA
is mapped to Iterator<ObjectA>
.
Example:
List<Point_2> input_points= new LinkedList<Point_2>();
Random rand = new Random();
for (int i=0; i < 1000000; ++i){
double x=rand.nextDouble();
double y=rand.nextDouble();
input_points.add(new Point_2(x,y));
}
Delaunay_triangulation_2 dt2 = new Delaunay_triangulation_2();
//insert a range of points to take advantage of the spatial sorting
dt2.insert(input_points.iterator());
Getting access to first and second fields of a std::pair<T1,T2> in SWIG is done through functions T1 getFirst()
, void setFirst(T1)
, T2 getSecond()
and void setSecond(T1)
.
Example:
Triangulation_2_Edge e=...
Triangulation_2_Face_handle f=e.getFirst();
C++
enum
are wrapped to Java enum
.
Example:
import CGAL.Kernel.Point_3;
import CGAL.Kernel.Sphere_3;
import CGAL.Kernel.Bounded_side;
Point_3 p=new Point_3(0,0,0);
Sphere_3 s=new Sphere_3(p,1);
assert s.bounded_side(p)==Bounded_side.ON_BOUNDED_SIDE;
Since operator overloading in java does not exist in the language, the C++ operators are mapped to the following functions in java:
-
operator+
is mapped toplus
-
operator-
is mapped tominus
-
operator*
is mapped totimes
-
operator/
is mapped todiv
-
operator+=
is mapped toiplus
-
operator-=
is mapped toiminus
-
operator*=
is mapped toitimes
-
operator/=
is mapped toidiv
-
operator==
is mapped toequals
-
operator!=
is mapped tonot_equals
-
operator<
is mapped tolt
-
operator<=
is mapped tole
-
operator>
is mapped togt
-
operator>=
is mapped toge
The organization into modules implies a specific way to import types in Python. A class wrapped in a SWIG module named FOO
will be in the module CGAL.CGAL_FOO
in Python. For example, to use the Point_2
class, one needs from CGAL.CGAL_Kernel import Point_2
. Global functions wrapped in a SWIG module named FOO
are available in the module CGAL.CGAL_FOO
.
Example:
import CGAL.Kernel.Point_2;
import CGAL.Kernel.CGAL_Kernel;
Point_2 p1=new Point_2(0,0);
Point_2 p2=new Point_2(1,1);
double d= CGAL_Kernel.squared_distance(p1,p2);
Pseudo-nested iterator are mapped to Python iterator.
Example:
dt2=CGAL_Triangulation_2.Delaunay_triangulation_2()
for v in dt2.finite_vertices():
print v.point()
Output iterators are mapped to Python list.
Example:
edges=[]
cdt2=CGAL_Triangulation_2.Constrained_Delaunay_triangulation_2()
v=CGAL_Triangulation_2.Constrained_Delaunay_triangulation_2_Vertex_handle()
//collect all constrained edges incident to a vertex
cdt2.incident_constraints(v,edges);
Input iterators are mapped to Python iterators and iterable container.
Example:
lst=[]
lst.append(CGAL_Kernel.Point_2(0,0))
lst.append(CGAL_Kernel.Point_2(1,0))
lst.append(CGAL_Kernel.Point_2(1,1))
dt2=CGAL_Triangulation_2.Delaunay_triangulation_2()
//insert a range of points to take advantage of the spatial sorting
dt2.insert(lst)
//or
dt2.insert(lst.__iter()__)
Getting access to first and second fields of a std::pair<T1,T2> in SWIG is done thanks to the tuple interface in Python. Example:
e=Triangulation_3_Edge(....)
#access to the coordinates of the opposite vertex to the edge.
e[0].vertex(e[1]).point()
C++
enum
are wrapped to constant in the module they are defined.
Example:
from CGAL.CGAL_Kernel import Point_3
from CGAL.CGAL_Kernel import Sphere_3
from CGAL.CGAL_Kernel import ON_BOUNDED_SIDE
p=Point_3(0,0,0)
s=Sphere_3(p,1)
assert s.bounded_side(p)==ON_BOUNDED_SIDE