CGAL 6.1 - CGAL Basic Viewer
Loading...
Searching...
No Matches
User Manual

Author
Guillaume Damiand and Mostafa Ashraf

Introduction

The basic viewer package provides interactive visualization for most CGAL packages, such as 2D Arrangements, 2D Boolean Set, Linear cell complex, 3D Nef, 2D Periodic Triangulations, 3D Point Set, 2D Polygon, 3D Polyhedral Surface, 2D Straight Skeleton, Surface Mesh, 2D Triangulation, 3D Triangulation, 2D Voronoi Diagram, and more.

The most simple use case of the basic viewer is the call of the global draw() function. There is one draw function for each CGAL package. Such a call opens a new interactive window showing the given model and allowing to navigate in the scene, show or hide some specific cells, show the interior of the model if any, etc.

The drawing can be tuned using Graphics_scene_options, for example to change the color of some cells or hide some cells.

More complex usage can be achieved by using the Graphics_scene class. Different CGAL data structures can be added in a same Graphics_scene allowing to visualize them simultaneously in a same window. Like for the draw function, the drawing parameters can be tuned thanks to the Graphics_scene_options.

Two classes are based on Qt.

Software Design

The diagram in Figure 118.1 shows the main classes and functions of this package.

Figure 118.1 UML diagram of the classes and functions of the package (only partial representation).


Basic Usage: The Global Draw Functions

A first simple solution provided to draw the different data structures of CGAL is the global draw() function. This function is templated by the type of the data structure to draw. The following example shows how it can be used.


File Basic_viewer/draw_lcc.cpp

#include <CGAL/Linear_cell_complex_for_combinatorial_map.h>
#include <CGAL/draw_linear_cell_complex.h>
using Point=LCC::Point;
int main()
{
LCC lcc;
lcc.make_hexahedron(Point(0,0,0), Point(5,0,0),
Point(5,5,0), Point(0,5,0),
Point(0,5,4), Point(0,0,4),
Point(5,0,4), Point(5,5,4));
CGAL::draw(lcc);
return EXIT_SUCCESS;
}
void draw(const PS2 &ps2, const GSOptions &gso)

This example creates a 3D linear cell complex of a hexahedron, and draws it. The result is shown in the following figure.

Figure 118.2 Example of drawing of a 3D LCC.


Users can interact with the viewer using some specific keys or mouse interactions. Pressing key 'h' makes pop up a help window showing the different shortcuts and mouse interactions. The main functionalities of the viewer are:

  • Versatile Rendering:

    The viewer supports the rendering of various geometric elements, such as points, edges, faces, rays, and lines. Different rendering modes, including mono and colored representation, are available for these elements.

  • Camera Control:

    The viewer allows users to move the camera, switch between 2D and 3D viewing modes, adjusting the camera accordingly. Camera settings, such as orthographic or perspective projection, can be configured based on the dimension of the scene.

  • User Interaction:

    Users can interact with the viewer through keyboard inputs, enabling them to control rendering options, toggle the display of elements, and adjust visual parameters. Key presses are mapped to specific actions, such as toggling the clipping plane, changing rendering modes, adjusting the size of elements, and modifying ambient light color.

  • Clipping Plane:

    The viewer includes support for a clipping plane, enabling users to selectively render parts of the scene. The clipping plane can be toggled on and off, and its rendering style can be modified (solid, wireframe, etc.).

Tuning with Graphics Scene Options

There is one specialization of each draw function that takes graphics scene options as additional parameter, allowing to tune the drawing.

The Graphics_scene_options class provides a set of options and customization parameters for rendering geometric structures in a graphics scene. Its main purpose is to allow users to control the visual appearance of various cells such as vertices, edges, faces, and volumes in a graphical representation of a given data structure.

The following example shows how to use graphics scene options to tune the drawing of a surface mesh. We define our own class My_graphics_scene_options that inherits from Graphics_scene_options to get all the default parameters. In this class, we only override the two methods colored_vertex and vertex_color to draw all vertices in color, and chose randomly green or blue colors for one out of two vertices.


File Basic_viewer/draw_surface_mesh_vcolor.cpp

#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/draw_surface_mesh.h>
#include <CGAL/Graphics_scene_options.h>
#include <fstream>
using Point=Kernel::Point_3;
// Inherit from CGAL::Graphics_scene_options to get all the default values.
struct My_graphics_scene_options:
typename boost::graph_traits<Mesh>::vertex_descriptor,
typename boost::graph_traits<Mesh>::edge_descriptor,
typename boost::graph_traits<Mesh>::face_descriptor>
{
// All vertices are colored.
bool colored_vertex(const Mesh&,
typename boost::graph_traits<Mesh>::vertex_descriptor) const
{ return true; }
// Change the color of vertices randomly.
CGAL::IO::Color vertex_color(const Mesh&,
typename boost::graph_traits<Mesh>::vertex_descriptor) const
{
static bool v_green=true;
v_green=!v_green;
if(v_green) // 1 vertex out of two green (randomly)
{ return CGAL::IO::Color(0,220,0); }
else // the others are blue
{ return CGAL::IO::Color(0,0,220); }
}
};
int main(int argc, char* argv[])
{
const std::string filename = (argc>1) ? argv[1] : CGAL::data_file_path("meshes/elephant.off");
Mesh sm;
if(!CGAL::IO::read_polygon_mesh(filename, sm))
{
std::cerr << "Invalid input file: " << filename << std::endl;
return EXIT_FAILURE;
}
// Draw the mesh using the new graphics scene option.
CGAL::draw(sm, My_graphics_scene_options());
return EXIT_SUCCESS;
}
std::string data_file_path(const std::string &filename)
The class Graphics_scene_options is used to tune the way that the cells of a given data structure of ...
Definition: Graphics_scene_options.h:28

The result of this example can be shown in Figure 118.3.

Figure 118.3 Example of tuned drawing of a 3D surface mesh where some vertex colors are changed.


The drawing options can depend on the cells. In the following example the color of each face depends on its height.


File Basic_viewer/draw_surface_mesh_height.cpp

#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/draw_surface_mesh.h>
#include <CGAL/Graphics_scene_options.h>
#include <iostream>
typedef Kernel::Point_3 Point;
struct Colored_faces_given_height:
typename Mesh::Vertex_index,
typename Mesh::Edge_index,
typename Mesh::Face_index>
{
Colored_faces_given_height(const Mesh& sm)
{
if(sm.is_empty()) return;
double m_min_y=0., m_max_y=0.;
bool first=true;
for(typename Mesh::Vertex_index vi: sm.vertices())
{
if(first)
{ m_min_y=sm.point(vi).y(); m_max_y=m_min_y; first=false; }
else
{
m_min_y=(std::min)(m_min_y, sm.point(vi).y());
m_max_y=(std::max)(m_max_y, sm.point(vi).y());
}
}
this->colored_face=[](const Mesh &, typename Mesh::Face_index)->bool { return true; };
this->face_color=[m_min_y, m_max_y]
(const Mesh& sm, typename Mesh::Face_index fi)->CGAL::IO::Color
{
double res=0.;
std::size_t n=0;
for(typename Mesh::Vertex_index vi: vertices_around_face(sm.halfedge(fi), sm))
{
res+=sm.point(vi).y();
++n;
}
// Random color depending on the "height" of the facet
CGAL::Random random(static_cast<unsigned int>(30*((res/n)-m_min_y)/(m_max_y-m_min_y)));
return CGAL::get_random_color(random);
};
}
};
int main(int argc, char* argv[])
{
const std::string filename = (argc>1) ? argv[1] : CGAL::data_file_path("meshes/elephant.off");
Mesh sm;
if(!CGAL::IO::read_polygon_mesh(filename, sm))
{
std::cerr << "Invalid input file: " << filename << std::endl;
return EXIT_FAILURE;
}
CGAL::draw(sm, Colored_faces_given_height(sm));
return EXIT_SUCCESS;
}

The result of this example is shown in Figure 118.4.

Figure 118.4 Example of mesh drawing with a color for each face computed depending on its height.


The Graphics Scene

It is possible to do more advanced visualizations by using the class Graphics_scene. It is a container class for various geometric elements, such as points, segments, rays, lines, faces and texts. It provides several methods to add elements, possibly with individual colors. Things are a little bit different for faces. You must call face_begin() to start a new face (possibly with a color), add all the points in the face by using add_point_in_face(), and call face_end() to finish the face. Note that the given polygonal face is internally triangulated.

This class also stores the normal of the faces (computed automatically), and possibly the normal of the vertices that can be provided by the users.

Users can either fill directly a Graphics_scene using these methods, or by using a global function add_to_graphics_scene() that fills the scene using all the geometric elements of a given CGAL data structure. Like for the draw functions, there is an overload of add_to_graphics_scene() for each CGAL package.

A Graphics_scene can be drawn using the basic viewer thanks to the draw_graphics_scene() function.

The following example shows in a same viewer both a Point_set_3 and a Polyhedron_3. Note that, like the draw functions, each add_to_graphics_scene() can use an optional Graphics_scene_options as parameter allowing to tune the drawing of the given data structure. In the example, we change the color of all vertices of the point set.


File Basic_viewer/draw_mesh_and_points.cpp

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/poisson_surface_reconstruction.h>
#include <CGAL/IO/read_points.h>
#include <CGAL/draw_polyhedron.h>
#include <CGAL/draw_point_set_3.h>
#include <CGAL/Graphics_scene_options.h>
#include <CGAL/Qt/Basic_viewer.h>
#include <vector>
#include <iostream>
using Point=Kernel::Point_3;
using Vector=Kernel::Vector_3;
using Pwn=std::pair<Point, Vector>;
using Polyhedron=CGAL::Polyhedron_3<Kernel>;
struct Graphics_scene_options_green_points:
public CGAL::Graphics_scene_options<PS3, typename PS3::const_iterator,
typename PS3::const_iterator,
typename PS3::const_iterator>
{
bool colored_vertex(const PS3&, typename PS3::const_iterator) const
{ return true; }
CGAL::IO::Color vertex_color(const PS3&, typename PS3::const_iterator) const
{ return CGAL::IO::Color(0,220,0); }
};
int main(void)
{
std::vector<Pwn> points;
if(!CGAL::IO::read_points(CGAL::data_file_path("points_3/kitten.xyz"), std::back_inserter(points),
CGAL::parameters::point_map(CGAL::First_of_pair_property_map<Pwn>())
.normal_map(CGAL::Second_of_pair_property_map<Pwn>())))
{
std::cerr << "Error: cannot read input file " << CGAL::data_file_path("points_3/kitten.xyz") << std::endl;
return EXIT_FAILURE;
}
Polyhedron output_mesh;
double average_spacing = CGAL::compute_average_spacing<CGAL::Sequential_tag>
(points, 6, CGAL::parameters::point_map(CGAL::First_of_pair_property_map<Pwn>()));
if (CGAL::poisson_surface_reconstruction_delaunay
(points.begin(), points.end(),
CGAL::First_of_pair_property_map<Pwn>(),
CGAL::Second_of_pair_property_map<Pwn>(),
output_mesh, average_spacing))
{
PS3 point_set;
for(Pwn& it: points)
{ point_set.insert(it.first); }
CGAL::add_to_graphics_scene(point_set, scene, Graphics_scene_options_green_points());
CGAL::add_to_graphics_scene(output_mesh, scene);
}
else
{ return EXIT_FAILURE; }
return EXIT_SUCCESS;
}
The class Graphics_scene stores points, segments, triangles, rays, and lines.
Definition: Graphics_scene.h:10
void draw_graphics_scene(const Graphics_scene &graphic_scene, const char *title="CGAL Basic Viewer")
opens a new window and draws the given Graphics_scene (which must have been filled before).
Definition: Basic_viewer.h:172
void add_to_graphics_scene(const PS2 &ps2, CGAL::Graphics_scene &gs, const GSOptions &gso)

The result of this example is shown in Figure 118.5 where we can see in green the original points of the point cloud, superposed with the polyhedron surface reconstructed by the Poisson surface reconstruction method.

Figure 118.5 Example of drawing of a point cloud and a polyhedron in a same viewer.


The Basic Viewer Class

The class CGAL::Qt::Basic_viewer is a Qt widget that inherits from QGLViewer and mainly stores a Graphics_scene and allows to visualize it and interact with the scene. Since this class is a Qt widget, it can be used into more complex Qt code to create more advanced demos.

In the following example, we create two graphics scenes, one filled with a point cloud, a second one filled with the polyhedral surface reconstructed by the Poisson surface reconstruction method. Then, we create two basic viewers associated with these two scenes. The two basic viewers are added into a Qt layout allowing to visualize the two scenes side by side.


File Basic_viewer/draw_several_windows.cpp

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/poisson_surface_reconstruction.h>
#include <CGAL/IO/read_points.h>
#include <CGAL/draw_polyhedron.h>
#include <CGAL/draw_point_set_3.h>
#include <CGAL/Graphics_scene_options.h>
#include <CGAL/Qt/Basic_viewer.h>
#ifdef CGAL_USE_BASIC_VIEWER
#include <QMainWindow>
#endif
#include <vector>
#include <iostream>
using Point=Kernel::Point_3;
using Vector=Kernel::Vector_3;
using Pwn=std::pair<Point, Vector>;
using Polyhedron=CGAL::Polyhedron_3<Kernel>;
int main(void)
{
std::vector<Pwn> points;
if(!CGAL::IO::read_points(CGAL::data_file_path("points_3/kitten.xyz"), std::back_inserter(points),
CGAL::parameters::point_map(CGAL::First_of_pair_property_map<Pwn>())
.normal_map(CGAL::Second_of_pair_property_map<Pwn>())))
{
std::cerr << "Error: cannot read input file " << CGAL::data_file_path("points_3/kitten.xyz") << std::endl;
return EXIT_FAILURE;
}
Polyhedron output_mesh;
double average_spacing = CGAL::compute_average_spacing<CGAL::Sequential_tag>
(points, 6, CGAL::parameters::point_map(CGAL::First_of_pair_property_map<Pwn>()));
if (!CGAL::poisson_surface_reconstruction_delaunay
(points.begin(), points.end(),
CGAL::First_of_pair_property_map<Pwn>(),
CGAL::Second_of_pair_property_map<Pwn>(),
output_mesh, average_spacing))
{ return EXIT_FAILURE; }
PS3 point_set;
for(Pwn& it: points)
{ point_set.insert(it.first); }
CGAL::Graphics_scene scene1, scene2;
CGAL::add_to_graphics_scene(point_set, scene1);
CGAL::add_to_graphics_scene(output_mesh, scene2);
#ifdef CGAL_USE_BASIC_VIEWER
#if defined(CGAL_TEST_SUITE)
bool cgal_test_suite=true;
#else
bool cgal_test_suite=qEnvironmentVariableIsSet("CGAL_TEST_SUITE");
#endif
if (cgal_test_suite) { return EXIT_SUCCESS; }
int argc=1;
const char* argv[2]={"Draw several windows example","\0"};
QApplication app(argc,const_cast<char**>(argv));
QMainWindow* mainWindow=new QMainWindow;
QWidget *centralWidget = new QWidget(mainWindow);
QHBoxLayout* layout = new QHBoxLayout(mainWindow);
CGAL::Qt::Basic_viewer bv1(mainWindow, scene1);
CGAL::Qt::Basic_viewer bv2(mainWindow, scene2);
bv1.draw_vertices(true);
layout->addWidget(&bv1);
layout->addWidget(&bv2);
centralWidget->setLayout(layout);
mainWindow->setCentralWidget(centralWidget);
mainWindow->show();
app.exec();
#endif
return EXIT_SUCCESS;
}
The class Basic_viewer is a Qt widget based on QGLViewer that allows to visualize 3D elements: points...
Definition: Basic_viewer.h:13

The result of this example is shown in Figure 118.6.

Figure 118.6 Example of drawing of two Basic_viewer side by side.


Adding Interaction

In the previous examples, the models are only drawn once and there is no interaction with the user to update the drawing.

It is possible to define such interactions thanks to the class CGAL::Qt::QApplication_and_basic_viewer. In this class, you can define your own function that is called automatically by the viewer when a user presses a key. This can be used to change some parameters and update the drawing accordingly.

We illustrate this possibility in the following example that shows a surface mesh, while coloring the small faces in red. To do so, we use our own graphics scene options than change the color of a face depending on its size. A face is considered small if its size is below a certain threshold. This threshold can be updated by the user, pressing key 'I' to increase it and 'D' to decrease it. This is done in the key pressed function defined in the QApplication_and_basic_viewer. When the threshold changes, the graphics scene is recomputed to take the modification of the size threshold into account.


File Basic_viewer/draw_surface_mesh_small_faces.cpp

#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/measure.h>
#include <CGAL/Polygon_mesh_processing/triangulate_faces.h>
#include <iostream>
#include <string>
#include <cassert>
#include <CGAL/draw_surface_mesh.h>
typedef Mesh::Vertex_index vertex_descriptor;
typedef Mesh::Face_index face_descriptor;
typedef K::FT FT;
template<class SM>
struct Graphics_scene_options_small_faces:
typename boost::graph_traits<SM>::vertex_descriptor,
typename boost::graph_traits<SM>::edge_descriptor,
typename boost::graph_traits<SM>::face_descriptor>
{
typename boost::graph_traits<SM>::vertex_descriptor,
typename boost::graph_traits<SM>::edge_descriptor,
typename boost::graph_traits<SM>::face_descriptor>;
Graphics_scene_options_small_faces(const SM& sm): Base(), m_sm(sm)
{
std::optional<typename SM::template Property_map<face_descriptor, FT>> faces_size
= sm.template property_map<face_descriptor, FT>("f:size");
m_with_size = faces_size.has_value();
if(!m_with_size)
{ return; }
m_min_size=faces_size.value()[*(sm.faces().begin())];
m_max_size=m_min_size;
FT cur_size;
for (typename SM::Face_range::iterator f=sm.faces().begin(); f!=sm.faces().end(); ++f)
{
cur_size=faces_size.value()[*f];
if (cur_size<m_min_size) m_min_size=cur_size;
if (cur_size>m_max_size) m_max_size=cur_size;
}
this->face_color=[this](const SM& sm,
typename boost::graph_traits<SM>::face_descriptor fh) -> CGAL::IO::Color
{ return this->get_face_color(sm, fh); };
this->colored_face = [](const SM&,
typename boost::graph_traits<SM>::face_descriptor) -> bool
{ return true; };
}
CGAL::IO::Color get_face_color(const SM& sm,
typename boost::graph_traits<SM>::face_descriptor fh)
{
// Default color of faces
CGAL::IO::Color c(75,160,255);
if(!m_with_size) { return c; }
// Compare the size of the face with the % m_threshold
std::optional<typename SM::template Property_map<face_descriptor, FT>> faces_size
= sm.template property_map<face_descriptor, FT>("f:size");
assert(faces_size.has_value());
// If the face is small, color it in red.
if (get(faces_size.value(), fh)<m_min_size + ((m_max_size - m_min_size) / (100 - m_threshold)))
{ return CGAL::IO::Color(255,20,20); }
return c; // Default color
}
const SM& m_sm;
bool m_with_size;
FT m_min_size, m_max_size;
unsigned int m_threshold=85; // 85%
};
int main(int argc, char* argv[])
{
const std::string filename = (argc>1) ? argv[1] : CGAL::data_file_path("meshes/elephant.off");
Mesh sm;
if(!CGAL::IO::read_polygon_mesh(filename, sm))
{
std::cerr << "Invalid input file: " << filename << std::endl;
return EXIT_FAILURE;
}
CGAL::Polygon_mesh_processing::triangulate_faces(sm);
Mesh::Property_map<face_descriptor, FT> faces_size;
bool created;
boost::tie(faces_size, created)=sm.add_property_map<face_descriptor, FT>("f:size",0.);
assert(created);
for(face_descriptor fd : sm.faces())
{ faces_size[fd]=CGAL::Polygon_mesh_processing::face_area(fd, sm); }
Graphics_scene_options_small_faces gsosm(sm);
add_to_graphics_scene(sm, gs, gsosm);
#ifdef CGAL_USE_BASIC_VIEWER
if(app)
{
app.basic_viewer().on_key_pressed=
[&sm, &gsosm, &gs] (QKeyEvent* e, CGAL::Qt::Basic_viewer* basic_viewer) -> bool
{
const ::Qt::KeyboardModifiers modifiers = e->modifiers();
if ((e->key() == ::Qt::Key_I) && (modifiers == ::Qt::NoButton))
{
gsosm.m_threshold+=5;
if(gsosm.m_threshold>100) { gsosm.m_threshold=100; }
basic_viewer->displayMessage
(QString("Small faces threshold=%1.").arg(gsosm.m_threshold));
gs.clear();
add_to_graphics_scene(sm, gs, gsosm);
basic_viewer->redraw();
}
else if ((e->key() == ::Qt::Key_D) && (modifiers == ::Qt::NoButton))
{
if(gsosm.m_threshold<5) { gsosm.m_threshold=0; }
else { gsosm.m_threshold-=5; }
basic_viewer->displayMessage
(QString("Small faces threshold=%1.").arg(gsosm.m_threshold));
gs.clear();
add_to_graphics_scene(sm, gs, gsosm);
basic_viewer->redraw();
}
else
{
// Return false will call the base method to process others/classicals key
return false;
}
return true;
};
// Here we add shortcut descriptions
app.basic_viewer().setKeyDescription(::Qt::Key_I, "Increase threshold for small faces");
app.basic_viewer().setKeyDescription(::Qt::Key_D, "Decrease threshold for small faces");
// Then we run the app
app.run();
}
#endif
sm.remove_property_map(faces_size);
return EXIT_SUCCESS;
}
void clear()
clears the scene, i.e., removes all points, segments, triangles, and text.
The class QApplication_and_basic_viewer regroups a Basic_viewer and Qt QApplication.
Definition: Basic_viewer.h:151

The result of this example is shown in Figure 118.7, showing to the left the initial drawing of the 3D model, and to the right the same model after having changed the size threshold.

Figure 118.7 Two examples of drawing of a mesh with small faces in red. Left: With the initial threshold. Right: After having increased the threshold.


Design and Implementation History

This package was started by Mostafa Ashraf during his 2022 GSoC project. Guillaume Damiand, who mentored the project, reworked large parts of the package, wrote examples and the manual.