"Fossies" - the Fresh Open Source Software Archive

Member "doc_html/Three/index.html" (8 Nov 2019, 100440 Bytes) of package /linux/misc/CGAL-5.0-doc_html.tar.xz:


Caution: In this restricted "Fossies" environment the current HTML page may not be correctly presentated and may have some non-functional links. You can here alternatively try to browse the pure source code or just view or download the uninterpreted raw source code. If the rendering is insufficient you may try to find and view the page on the project site itself.

\( \newcommand{\E}{\mathrm{E}} \) \( \newcommand{\A}{\mathrm{A}} \) \( \newcommand{\R}{\mathrm{R}} \) \( \newcommand{\N}{\mathrm{N}} \) \( \newcommand{\Q}{\mathrm{Q}} \) \( \newcommand{\Z}{\mathrm{Z}} \) \( \def\ccSum #1#2#3{ \sum_{#1}^{#2}{#3} } \def\ccProd #1#2#3{ \sum_{#1}^{#2}{#3} }\)

CGAL 5.0 - Three
User Manual

Author
Laurent Rineau, Sebastien Loriot, Andreas Fabri, Maxime Gimeno

This package regroups the files making the API for creating and adding a new plugin to the Polyhedron_demo.

Understanding the Polyhedron Demo

There are several levels in this demo.

  • The MainWindow, which contains the UI elements.
  • Among these elements is the Viewer, which is the drawable surface that handles all the drawing and all the keyboard and mouse events.
  • The Viewer has a reference to a CGAL::Three::Scene_interface, which contains a list of all the items (the drawn elements).

A plugin usually make use of objects that inherit from CGAL::Three::Scene_item or uses some of them to demonstrate a CGAL feature, so it might have to deal with the above elements.

Creating a Simple Plugin

The Plugin Itself

A basic plugin will inherit from CGAL::Three::Polyhedron_demo_plugin_interface. It must be created in the corresponding folder named after its package, and containing all the files created for the plugin, and a CMakeLists.txt file. Its name must be of the form Xxxx_yyyy_plugin.

The class must contain the following lines :

Q_OBJECT
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")

Your plugin must override the three pure virtual functions inherited from Polyhedron_demo_plugin_interface :

  • actions() that will hold the actions of the plugin
  • applicable() that will decide if the plugin can be used with the current selection of items
  • init() that will declare and link the actions of the plugin

The function init() is used just like a constructor. This is where you will connect all the actions, signals and slots of your plugin.
This is also where you will declare the submenu of Operations in which your actions will be displayed.

As a plugin is a QObject, it uses automoc and therefore must contain the line:

#include "Xxxx_yyyy_plugin.moc"

Example :

#include <CGAL/Three/Polyhedron_demo_plugin_interface.h>
#include <QApplication>
#include <QObject>
#include <QAction>
#include <QMainWindow>
#include <QInputDialog>
#include <QMessageBox>
#include "Messages_interface.h"
//This plugin creates an action in Operations depending on EXAMPLE_COMPLEXITY.
class BasicPlugin :
public QObject,
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")
public:
//decides if the plugin's actions will be displayed or not.
bool applicable(QAction*) const Q_DECL_OVERRIDE
{
return true;
}
//the list of the actions of the plugin.
QList<QAction*> actions() const Q_DECL_OVERRIDE
{
return _actions;
}
//this acts like a constructor for the plugin. It gets the references to the main window and the scene, and connects the action.
void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* sc, Messages_interface* mi) Q_DECL_OVERRIDE
{
//gets the reference to the message interface, to display text in the console widget
this->messageInterface = mi;
//get the references
this->scene = sc;
this->mw = mainWindow;
//creates the action
QAction *actionHelloWorld= new QAction(QString("Hello World"), mw);
//specifies the subMenu
actionHelloWorld->setProperty("submenuName", "Basic");
//links the action
if(actionHelloWorld) {
connect(actionHelloWorld, SIGNAL(triggered()),
this, SLOT(helloWorld()));
_actions << actionHelloWorld;
}
}
private Q_SLOTS:
void helloWorld()
{
messageInterface->information(QString("Hello World!"));
}
private:
QList<QAction*> _actions;
Messages_interface* messageInterface;
//The reference to the scene
//The reference to the main window
QMainWindow* mw;
};
#include "Basic_plugin.moc"

Once you have written your plugin, you must add it to the project using the CMakeLists.txt file.
If you created your plugin in an existing directory, the CMakeLists.txt file already exists. If you created a new directory, you must create it.

The CMakeLists.txt file :

include( polyhedron_demo_macros )
polyhedron_demo_plugin(basic_plugin Basic_plugin)

Adding a Dialog to Your Plugin

This section describes how to add a dialog when an action is triggered.

For a minimalist dialog intended, for instance, to get a single parameter, you can create a QInputDialog:

void helloWorld()
{
bool ok = false;
const unsigned int parameter =
QInputDialog::getInt((QWidget*)mw,
tr("Hello World"), // dialog title
tr("Hello dear user! What integer would you want me to display for you ? "), // field label
10, // default value = fast
0, // min
100, // max
1, // step
&ok);
if(!ok) return;
messageInterface->information(QString("You asked me to display %1, so here it is : %1").arg(parameter));
}

For a more elaborate interface, you will have to use the designer tool of QtCreator. Create a new Qt Designer form (file->New file or Project->Qt->).

menu_1.png
menu_2.png

Then select among the template/form choices.

menu_3.png

Name it Xxxx_yyyy_dialog.ui (you may have to rename it once it is created as QtCreator tends to forget the capital letters), and add it to the project in the CMakeLists :

qt5_wrap_ui( basicUI_FILES Basic_dialog.ui )
polyhedron_demo_plugin(basic_plugin Basic_plugin ${basicUI_FILES})

Add a layout to the dialog with a drag and drop and lay it out.

menu_4.png

Edit the ui file with the editor.

menu_5.png

then add the following line to your plugin file:

#include "ui_Xxxx_yyyy_plugin.h"

You can then add a new class to your cpp file, inheriting from QDialog and your own dialog, accessible with "Ui::".
Add the macro Q_OBJECT and the line setupUi(this) in your constructor.

class ComplexDialog :
public QDialog,
public Ui::BasicDialog
{
Q_OBJECT
public:
ComplexDialog(QWidget* =0)
{
setupUi(this);
}
};

You can populate the dialog in the action, using dialog.show() or dialog.exec().

void helloWorld()
{
//creates a new dialog
ComplexDialog *dialog = new ComplexDialog();
//opens the dialog
if(!dialog->exec())
return;
QString result = dialog->lineEdit->text();
bool ok = false;
int int_res = result.toInt(&ok);
if(!ok)
{
QMessageBox::warning(mw,
"ERROR",
tr("This is not an integer !")
);
return;
}
messageInterface->information(QString("You asked me to display %1, so here it is : %1").arg(int_res));
}

Adding a Warning/Error Box to Your Plugin

It is really simple to add a pop-up box with Qt. Use a QMessageBox and give it some arguments :

QString result = dialog->lineEdit->text();
bool ok = false;
int int_res = result.toInt(&ok);
if(!ok)
{
QMessageBox::warning(mw,
"ERROR",
tr("This is not an integer !")
);
return;
}

Adding a Dock Widget

This section describes how to add a dock widget to the application.
You can make your plugin inherit from CGAL::Three::Polyhedron_demo_plugin_helper, which gives acces to the function CGAL::Three::Polyhedron_demo_plugin_helper::addDockWidget. This will manage automatically the position and tabification of a dock widget.
Just like with the Dialog, create a new Qt Designer form (file->New file or Project->Qt->Qt Designer Form), choose `QDockWidget in Widgets

menu_6.png

Add it to the project in the CMakeLists.txt :

qt5_wrap_ui( dockUI_FILES Basic_dock_widget.ui )
polyhedron_demo_plugin(dock_widget_plugin Dock_widget_plugin ${dockUI_FILES})

Edit the ui file with the editor, then add the following line to your plugin file:

#include "ui_Basic_dock_widget.h"

As for the Dialog, create a new class for your widget

class DockWidget :
public QDockWidget,
public Ui::BasicDockWidget
{
public:
DockWidget(QString name, QWidget *parent)
:QDockWidget(name,parent)
{
setupUi(this);
}
};

and add a reference to an object of this type as private members of your plugin:

private:
DockWidget* dock_widget;

and initialize it in the init function, where you will also use signal/slots to connect it to the plugin:

//this acts like a constructor for the plugin. It gets the references to the mainwindow and the scene, and connects the action.
void init(QMainWindow* mw, CGAL::Three::Scene_interface* sc, Messages_interface* mi) Q_DECL_OVERRIDE
{
//gets the reference to the message interface, to display text in the console widget
this->messageInterface = mi;
//get the references
this->scene = sc;
this->mw = mw;
//creates the action
QAction *actionHelloWorld= new QAction(QString("Open Dock Widget"), mw);
//specifies the subMenu
actionHelloWorld->setProperty("submenuName", "Basic");
//links the action
if(actionHelloWorld) {
connect(actionHelloWorld, SIGNAL(triggered()),
this, SLOT(helloWorld()));
_actions << actionHelloWorld;
}
dock_widget = new DockWidget("Print a number", mw);
dock_widget->setVisible(false); // do not show at the beginning
addDockWidget(dock_widget);
connect(dock_widget->pushButton, SIGNAL(clicked(bool)),
this, SLOT(on_dock_button_clicked()));
}

You can use the functions show()/hide() to make your dock widget visible or not.

void helloWorld()
{
// dock widget should be instancied in init()
if(dock_widget->isVisible()) { dock_widget->hide(); }
else { dock_widget->show(); }
}
void on_dock_button_clicked()
{
messageInterface->information(QString("Here is your number :%1").arg(dock_widget->spinBox->value()));
}

By default, a dock widget will remain visible next time the demo is launched if it has not been closed. If you want to avoid that behavior, override the function closure() in you plugin, and simply call hide() on your dock widget in it.

void closure()Q_DECL_OVERRIDE
{
dock_widget->hide();
}

Using a Scene_item

Using an existing item

You can get a reference to the items present in the scene in your plugin. To do so, you will need to use the reference to the scene that you can get in the init() function :

void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* sc )
{
//get the references
this->scene = sc;
}

This Scene_interface will give you access to the following functions :

that give you access to any item in the scene.

Example of use :

//get a reference to the selected item.
CGAL::Three::Scene_item *item = scene->item(scene->mainSelectionIndex());
messageInterface->information(QString("The selected item's name is : %1").arg(item->name()));

Don't forget to adapt your applicable() function :

//This plugin is only applicable if there is exactly one selected item.
bool applicable(QAction*) const Q_DECL_OVERRIDE
{
return scene->selectionIndices().size() ==1;
}

Creating an item of an existing type

You might want your plugin to create a new item. This is possible thanks to the scene.

void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* sc )
{
//get the references
this->scene = sc;
}

Simply create a new instance of the item you want and call the function CGAL::Three::Scene_interface::addItem :

//creates a plane item
Scene_plane_item *new_item = new Scene_plane_item(scene);
new_item->setName("Trivial Plane");
new_item->setColor(Qt::blue);
new_item->setNormal(0.0,0.0,1.0);
scene->addItem(new_item);

Once your code is written, you will need to link the item's library to your plugin thanks to the CMakeLists, using the command target_link_library :

polyhedron_demo_plugin(basic_item_plugin Basic_item_plugin)
#  links the library containing the scene_plane_item with the plugin
target_link_libraries(basic_item_plugin PUBLIC scene_basic_objects)

Creating a new type of item

If you cannot use an existing type of item, like the Scene_polyhedron_item or the Scene_c3t3_item for your plugin, you will have to create one, by deriving CGAL::Three::Scene_item.
An item is simply a graphic representation of a geometric data set. You need to compute this data, and to display it in CGAL::Three::Scene_item::draw.

// The special Scene_item only for triangles
//this is used by the Qt's MOC system to manage the metadata.
#ifdef scene_triangle_item_EXPORTS
# define SCENE_TRIANGLE_ITEM_EXPORT Q_DECL_EXPORT
#else
# define SCENE_TRIANGLE_ITEM_EXPORT Q_DECL_IMPORT
#endif
class Scene_triangle_item : public CGAL::Three::Scene_item
{
Q_OBJECT
public :
Scene_triangle_item(double ax,double ay, double az,
double bx,double by, double bz,
double cx,double cy, double cz);
// Indicates if rendering mode is supported
bool supportsRenderingMode(RenderingMode m) const Q_DECL_OVERRIDE {
return (m == Flat);
}
//Displays the item
void draw(CGAL::Three::Viewer_interface* viewer) const Q_DECL_OVERRIDE;
//Specifies that the buffers need to be initialized again.
//Is mostly called after a change of geometry in the data.
void invalidateOpenGLBuffers() Q_DECL_OVERRIDE;
//fills the std::vector
void computeElements(double ax,double ay, double az,
double bx,double by, double bz,
double cx,double cy, double cz) const Q_DECL_OVERRIDE;
Scene_item* clone() const Q_DECL_OVERRIDE {return 0;}
QString toolTip() const Q_DECL_OVERRIDE {return QString();}
private:
//contains the data
mutable std::vector<float> vertices;
mutable int nb_pos;
mutable QOpenGLShaderProgram *program;
//Fills the buffers with data. The buffers allow us to give data to the shaders.
void initializeBuffers(CGAL::Three::Viewer_interface *viewer)const Q_DECL_OVERRIDE;
}; //end of class Scene_triangle_item

The minimalist item above is designed to draw a simple triangle. There are several steps that need to be followed when creating an item :

- Computing the data

One way to store the data you computed is to use member std::vector. It must be done every time a change occurs in the item's geometry (usually done in a function called invalidateOpenGLBuffers).

//Fills the position vector with data.
void Scene_triangle_item::computeElements(double ax, double ay, double az,
double bx, double by, double bz,
double cx, double cy, double cz)const
{
vertices.resize(9);
vertices[0] = ax; vertices[1] = ay; vertices[2] = az;
vertices[3] = bx; vertices[4] = by; vertices[5] = bz;
vertices[6] = cx; vertices[7] = cy; vertices[8] = cz;
}

- Filling the OpenGL buffers

The application uses OpenGL VBOs to display the geometry. Those are buffers that will stream their data to the GPU. This step consists to put the data stored in the std::vector in those buffers. In this exemple, we only need one VBO and one VAO, as we are only storing one kind of data. But if we wanted to store normals and colors, for instance, we would need as much VBOs, and if there were several types of display, let's say multiple RenderingMode, we would need as much VAOs. (See OpenGL doc about VBOs/VAOs for more details).

void Scene_triangle_item::initializeBuffers(CGAL::Three::Viewer_interface *viewer)const
{
//vao containing the data for the facets
{
program = getShaderProgram(PROGRAM_WITH_LIGHT, viewer);
program->bind();
vaos[0]->bind();
buffers[0].bind();
buffers[0].allocate(vertices.data(),
static_cast<GLsizei>(vertices.size()*sizeof(float)));
program->enableAttributeArray("vertex");
program->setAttributeBuffer("vertex",GL_FLOAT,0,3);
buffers[0].release();
vaos[0]->release();
program->release();
}
//once the buffers are fill, we can empty the vectors to optimize memory consumption
nb_pos = vertices.size();
vertices.resize(0);
//"Swap trick" insures that the memory is indeed freed and not kept available
std::vector<float>(vertices).swap(vertices);
are_buffers_filled = true;
}

The code above gets a ShaderProgram, that holds the characteristics of the display (here a basic lighting), binds its VAO and VBO and gives it the data.

- Displaying the data

Originally, it's the viewer that requires displaying. It calls the scene, that calls each visible item's draw(),drawEdges() and drawPoints() functions individually. Therefore, this is in those functions that the display of the data must be handled :

{
//The filling of the buffers should be performed in this function, because it needs a valid openGL context, and we are certain to have one in this function.
if(!are_buffers_filled)
{
computeElements(0, 0, 0,
1, 0, 0,
0.5, 0.5, 0);
initializeBuffers(viewer);
}
//Binds the vao corresponding to the type of data we are drawing.
vaos[0]->bind();
//Gets the program corresponding to the type of data we are drawing.
//Here we want triangles with light effects.
program = getShaderProgram(PROGRAM_WITH_LIGHT);
//Gives most of the uniform values to the shaders.
attribBuffers(viewer, PROGRAM_WITH_LIGHT);
//Binds the program chosen before to use the right shaders.
program->bind();
//Gives the wanted color to the fragment shader as uniform value.
program->setAttributeValue("colors", this->color());
//Draws the items
viewer->glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(nb_pos/3));
//clean up
vaos[0]->release();
program->release();
}

To display, you need to call the same program that got configured previously, and to bind it before you call the OpenGL drawing function.
If you created your item in a specific file and you need to use it outside your plugin (like in another plugin), it is recommended to put it in the demo's root directory, and you will have to define your item in the general Polyhedron_demo's CMakeLists.txt by using the macro add_item :

add_item(scene_trivial_item Scene_trivial_item.cpp)
target_link_libraries(scene_trivial_item PUBLIC scene_dependances_item)

Using a Scene_group_item

This section will explain how to use a group item in a plugin.

A group item is a virtual item that is not seen in the viewer, nor drawn, but acts as a parent for a list of actual items. Its main goal lies within the interaction with the SceneView (which is the list of items on the left of the viewer). With group items, this view becomes hierarchic, which allows to interact with several objects at the same time, and organize this view.

To use a group item in your plugin, you will need a CGAL::Three::Scene_group_item, of course, and a CGAL::Three::Scene_interface.
Create a new Scene_group_item, add it to the scene with CGAL::Three::Scene_interface::addItem() and give it the items you want to regroup with CGAL::Three::Scene_interface::changeGroup().
Keep in mind that you must pass items to a group only if those items are already in the scene.
You also have the possibility to "lock" a child of a group with CGAL::Three::Scene_group_item::lockChild() to prevent it from being moved or erased.

//Create a new group
Scene_group_item *group = new Scene_group_item("New group");
//add it to the scene
scene->addItem(group);
//Then give it its children
scene->changeGroup(item, group);
scene->changeGroup(new_item,group);

Creating an IO Plugin

An IO plugin is a plugin desined to load from and save to a certain type of file. Its name is generally of the form Xxxx_yyyy_io_plugin
It inherits from the CGAL::Three::Polyhedron_demo_io_plugin_interface. It must implement the following functions :

Creating an External Plugin

An external plugin is a plugin that is written in any directory but the official Plugin directory, and which will not be automatically loaded along with the others. To create an external plugin, you must make a new Cmake project.

project( Example_plugin )

Configure CMake as you desire and fetch the right Qt5 packages :

# Find includes in corresponding build directories
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# Instruct CMake to run moc automatically when needed.
set(CMAKE_AUTOMOC ON)
cmake_minimum_required(VERSION 3.1)

#Find CGAL
find_package(CGAL COMPONENTS Qt5)
include( ${CGAL_USE_FILE} )
# Find Qt5 itself
find_package(Qt5
             QUIET
             COMPONENTS            OpenGL Script Svg Xml
             OPTIONAL_COMPONENTS   ScriptTools)

You will probably have to fetch the libraries exported by the Polyhedron_demo, like the Scene_items.

find_package(CGAL_polyhedron_demo
HINTS "${CGAL_DIR}" "${CGAL_DIR}/Polyhedron/demo/Polyhedron-build"
)
include( ${CGAL_POLYHEDRON_DEMO_USE_FILE} )

Be careful, the polyhedron_demo build directory must contain a build of the demo that comes from the same CGAL version than the one of your CGAL_DIR. Finally, you can declare your plugin

polyhedron_demo_plugin(example_plugin Example_plugin)

If you need targets from the Polyhedron_demo, you will have to add the prefix 'Polyhedron_' to the target's name, as the exported targets belong to the namespace Polyhedron_

polyhedron_demo_plugin(basic_item_plugin Basic_item_plugin)
target_link_libraries(basic_item_plugin PUBLIC Polyhedron_scene_basic_objects)

Notice that an external plugin will not be automatically loaded in the Polyhedron demo. It must be built in its own project.

Complete CMakeLists :

project( Example_plugin )
# Find includes in corresponding build directories
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# Instruct CMake to run moc automatically when needed.
set(CMAKE_AUTOMOC ON)
cmake_minimum_required(VERSION 3.1)

#Find CGAL
find_package(CGAL COMPONENTS Qt5)
include( ${CGAL_USE_FILE} )
# Find Qt5 itself
find_package(Qt5
             QUIET
             COMPONENTS            OpenGL Script Svg Xml
             OPTIONAL_COMPONENTS   ScriptTools)

if(Qt5_FOUND AND CGAL_FOUND)
  find_package(CGAL_polyhedron_demo)

include( ${CGAL_POLYHEDRON_DEMO_USE_FILE} )
# Let plugins be compiled in the same directory as the executable.
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")


  polyhedron_demo_plugin(example_plugin Example_plugin)

endif()

Examples

All the examples have been constructed as external plugins in CGAL/Three/demo/Three/Example_plugin. You will have to use "Load plugin" in the File menu or set the environment variable POLYHEDRON_DEMO_PLUGINS_PATH if you want to test it.

Creating a Basic Plugin


File Three/Example_plugin/Basic_plugin.cpp

/*
Change the value of EXAMPLE_COMPLEXITY in the first line to change the behavior :
- 0 : prints "Hello World!" in the Info and console widgets
- 1 : pops-up a simple dialog asking to enter an integer , then prints it in the Info and console widgets
- 2 : pops-up a little more elaborated dialog asking to enter an integer , then prints it in the Info and console widgets if it was indeed an integer, else pops-up an error message box.
*/
#define EXAMPLE_COMPLEXITY 0
#include "ui_Basic_dialog.h"
#include <CGAL/Three/Polyhedron_demo_plugin_interface.h>
#include <QApplication>
#include <QObject>
#include <QAction>
#include <QMainWindow>
#include <QInputDialog>
#include <QMessageBox>
#include "Messages_interface.h"
class ComplexDialog :
public QDialog,
public Ui::BasicDialog
{
Q_OBJECT
public:
ComplexDialog(QWidget* =0)
{
setupUi(this);
}
};
//This plugin creates an action in Operations depending on EXAMPLE_COMPLEXITY.
class BasicPlugin :
public QObject,
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")
public:
//decides if the plugin's actions will be displayed or not.
bool applicable(QAction*) const Q_DECL_OVERRIDE
{
return true;
}
//the list of the actions of the plugin.
QList<QAction*> actions() const Q_DECL_OVERRIDE
{
return _actions;
}
//this acts like a constructor for the plugin. It gets the references to the main window and the scene, and connects the action.
void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* sc, Messages_interface* mi) Q_DECL_OVERRIDE
{
//gets the reference to the message interface, to display text in the console widget
this->messageInterface = mi;
//get the references
this->scene = sc;
this->mw = mainWindow;
//creates the action
QAction *actionHelloWorld= new QAction(QString("Hello World"), mw);
//specifies the subMenu
actionHelloWorld->setProperty("submenuName", "Basic");
//links the action
if(actionHelloWorld) {
connect(actionHelloWorld, SIGNAL(triggered()),
this, SLOT(helloWorld()));
_actions << actionHelloWorld;
}
}
private Q_SLOTS:
#if EXAMPLE_COMPLEXITY == 0
void helloWorld()
{
messageInterface->information(QString("Hello World!"));
}
#elif EXAMPLE_COMPLEXITY == 1
void helloWorld()
{
bool ok = false;
const unsigned int parameter =
QInputDialog::getInt((QWidget*)mw,
tr("Hello World"), // dialog title
tr("Hello dear user! What integer would you want me to display for you ? "), // field label
10, // default value = fast
0, // min
100, // max
1, // step
&ok);
if(!ok) return;
messageInterface->information(QString("You asked me to display %1, so here it is : %1").arg(parameter));
}
#elif EXAMPLE_COMPLEXITY == 2
void helloWorld()
{
//creates a new dialog
ComplexDialog *dialog = new ComplexDialog();
//opens the dialog
if(!dialog->exec())
return;
QString result = dialog->lineEdit->text();
bool ok = false;
int int_res = result.toInt(&ok);
if(!ok)
{
QMessageBox::warning(mw,
"ERROR",
tr("This is not an integer !")
);
return;
}
messageInterface->information(QString("You asked me to display %1, so here it is : %1").arg(int_res));
}
#endif
private:
QList<QAction*> _actions;
Messages_interface* messageInterface;
//The reference to the scene
//The reference to the main window
QMainWindow* mw;
};
#include "Basic_plugin.moc"

Creating a DockWidget


File Three/Example_plugin/Dock_widget_plugin.cpp

#include "ui_Basic_dock_widget.h"
#include <CGAL/Three/Polyhedron_demo_plugin_helper.h>
#include <QApplication>
#include <QObject>
#include <QAction>
#include <QMainWindow>
#include "Messages_interface.h"
class DockWidget :
public QDockWidget,
public Ui::BasicDockWidget
{
public:
DockWidget(QString name, QWidget *parent)
:QDockWidget(name,parent)
{
setupUi(this);
}
};
//This plugin crates an action in Operations that creates a DOckWidget to display a number in the 'console' dockwidet.
class BasicPlugin :
public QObject,
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")
public:
//decides if the plugin's actions will be displayed or not.
bool applicable(QAction*) const Q_DECL_OVERRIDE
{
return true;
}
//the list of the actions of the plugin.
QList<QAction*> actions() const Q_DECL_OVERRIDE
{
return _actions;
}
//this acts like a constructor for the plugin. It gets the references to the mainwindow and the scene, and connects the action.
void init(QMainWindow* mw, CGAL::Three::Scene_interface* sc, Messages_interface* mi) Q_DECL_OVERRIDE
{
//gets the reference to the message interface, to display text in the console widget
this->messageInterface = mi;
//get the references
this->scene = sc;
this->mw = mw;
//creates the action
QAction *actionHelloWorld= new QAction(QString("Open Dock Widget"), mw);
//specifies the subMenu
actionHelloWorld->setProperty("submenuName", "Basic");
//links the action
if(actionHelloWorld) {
connect(actionHelloWorld, SIGNAL(triggered()),
this, SLOT(helloWorld()));
_actions << actionHelloWorld;
}
dock_widget = new DockWidget("Print a number", mw);
dock_widget->setVisible(false); // do not show at the beginning
addDockWidget(dock_widget);
connect(dock_widget->pushButton, SIGNAL(clicked(bool)),
this, SLOT(on_dock_button_clicked()));
}
private Q_SLOTS:
void helloWorld()
{
// dock widget should be instancied in init()
if(dock_widget->isVisible()) { dock_widget->hide(); }
else { dock_widget->show(); }
}
void on_dock_button_clicked()
{
messageInterface->information(QString("Here is your number :%1").arg(dock_widget->spinBox->value()));
}
void closure()Q_DECL_OVERRIDE
{
dock_widget->hide();
}
private:
QList<QAction*> _actions;
Messages_interface* messageInterface;
DockWidget* dock_widget;
};
#include "Dock_widget_plugin.moc"

Using an Existing Item and Create a New Item of an Existing Type


File Three/Example_plugin/Basic_item_plugin.cpp

#include <CGAL/Three/Polyhedron_demo_plugin_interface.h>
#include <QApplication>
#include <QObject>
#include <QAction>
#include <QMainWindow>
#include <QInputDialog>
#include "Messages_interface.h"
#include "CGAL/Three/Scene_group_item.h"
#include "Scene_plane_item.h"
//This plugin crates an action in Operations that displays the name of the selected item,
//adds a scene_plane_item to the scene, and adds the selected item and the plane to a new group.
class BasicItemPlugin :
public QObject,
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")
public:
//This plugin is only applicable if there is exactly one selected item.
bool applicable(QAction*) const Q_DECL_OVERRIDE
{
return scene->selectionIndices().size() ==1;
}
//the list of the actions of the plugin.
QList<QAction*> actions() const Q_DECL_OVERRIDE
{
return _actions;
}
//this acts like a constructor for the plugin. It gets the references to the mainwindow and the scene, and connects the action.
void init(QMainWindow* mw, CGAL::Three::Scene_interface* sc, Messages_interface* mi) Q_DECL_OVERRIDE
{
//gets the reference to the message interface, to display text in the console widget
this->messageInterface = mi;
//get the references
this->scene = sc;
this->mw = mw;
//creates the action
QAction *actionHelloWorld= new QAction(QString("Create a group"), mw);
//specifies the subMenu
actionHelloWorld->setProperty("submenuName", "Basic");
//links the action
if(actionHelloWorld) {
connect(actionHelloWorld, SIGNAL(triggered()),
this, SLOT(helloWorld()));
_actions << actionHelloWorld;
}
}
private Q_SLOTS:
void helloWorld()
{
//get a reference to the selected item.
CGAL::Three::Scene_item *item = scene->item(scene->mainSelectionIndex());
messageInterface->information(QString("The selected item's name is : %1").arg(item->name()));
//creates a plane item
Scene_plane_item *new_item = new Scene_plane_item(scene);
new_item->setName("Trivial Plane");
new_item->setColor(Qt::blue);
new_item->setNormal(0.0,0.0,1.0);
scene->addItem(new_item);
//Create a new group
Scene_group_item *group = new Scene_group_item("New group");
//add it to the scene
scene->addItem(group);
//Then give it its children
scene->changeGroup(item, group);
scene->changeGroup(new_item,group);
}
private:
QList<QAction*> _actions;
Messages_interface* messageInterface;
//The reference to the scene
//The reference to the main window
QMainWindow* mw;
};
#include "Basic_item_plugin.moc"

Creating a new Type of Item


File Three/Example_plugin/Example_plugin.cpp

#include <QApplication>
#include <QMainWindow>
#include <QAction>
#include <QVector>
#include <CGAL/Three/Scene_item.h>
#include <CGAL/Three/Viewer_interface.h>
#include <CGAL/Three/Scene_group_item.h>
// The special Scene_item only for triangles
//this is used by the Qt's MOC system to manage the metadata.
#ifdef scene_triangle_item_EXPORTS
# define SCENE_TRIANGLE_ITEM_EXPORT Q_DECL_EXPORT
#else
# define SCENE_TRIANGLE_ITEM_EXPORT Q_DECL_IMPORT
#endif
class Scene_triangle_item : public CGAL::Three::Scene_item
{
Q_OBJECT
public :
Scene_triangle_item(double ax,double ay, double az,
double bx,double by, double bz,
double cx,double cy, double cz);
// Indicates if rendering mode is supported
bool supportsRenderingMode(RenderingMode m) const Q_DECL_OVERRIDE {
return (m == Flat);
}
//Displays the item
void draw(CGAL::Three::Viewer_interface* viewer) const Q_DECL_OVERRIDE;
//Specifies that the buffers need to be initialized again.
//Is mostly called after a change of geometry in the data.
void invalidateOpenGLBuffers() Q_DECL_OVERRIDE;
//fills the std::vector
void computeElements(double ax,double ay, double az,
double bx,double by, double bz,
double cx,double cy, double cz) const Q_DECL_OVERRIDE;
Scene_item* clone() const Q_DECL_OVERRIDE {return 0;}
QString toolTip() const Q_DECL_OVERRIDE {return QString();}
private:
//contains the data
mutable std::vector<float> vertices;
mutable int nb_pos;
mutable QOpenGLShaderProgram *program;
//Fills the buffers with data. The buffers allow us to give data to the shaders.
void initializeBuffers(CGAL::Three::Viewer_interface *viewer)const Q_DECL_OVERRIDE;
}; //end of class Scene_triangle_item
Scene_triangle_item::Scene_triangle_item(double ax,double ay, double az,
double bx,double by, double bz,
double cx,double cy, double cz)
: CGAL::Three::Scene_item(1,1)
{
nb_pos = 0;
are_buffers_filled = false;
computeElements(ax, ay, az,
bx, by, bz,
cx, cy, cz);
invalidateOpenGLBuffers();
}
//Fills the position vector with data.
void Scene_triangle_item::computeElements(double ax, double ay, double az,
double bx, double by, double bz,
double cx, double cy, double cz)const
{
vertices.resize(9);
vertices[0] = ax; vertices[1] = ay; vertices[2] = az;
vertices[3] = bx; vertices[4] = by; vertices[5] = bz;
vertices[6] = cx; vertices[7] = cy; vertices[8] = cz;
}
{
//The filling of the buffers should be performed in this function, because it needs a valid openGL context, and we are certain to have one in this function.
if(!are_buffers_filled)
{
computeElements(0, 0, 0,
1, 0, 0,
0.5, 0.5, 0);
initializeBuffers(viewer);
}
//Binds the vao corresponding to the type of data we are drawing.
vaos[0]->bind();
//Gets the program corresponding to the type of data we are drawing.
//Here we want triangles with light effects.
program = getShaderProgram(PROGRAM_WITH_LIGHT);
//Gives most of the uniform values to the shaders.
attribBuffers(viewer, PROGRAM_WITH_LIGHT);
//Binds the program chosen before to use the right shaders.
program->bind();
//Gives the wanted color to the fragment shader as uniform value.
program->setAttributeValue("colors", this->color());
//Draws the items
viewer->glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(nb_pos/3));
//clean up
vaos[0]->release();
program->release();
}
//Specifies that the buffers need to be initialized again.
//Is mostly called after a change of geometry in the data.
void Scene_triangle_item::invalidateOpenGLBuffers()
{
are_buffers_filled = false;
}
void Scene_triangle_item::initializeBuffers(CGAL::Three::Viewer_interface *viewer)const
{
//vao containing the data for the facets
{
program = getShaderProgram(PROGRAM_WITH_LIGHT, viewer);
program->bind();
vaos[0]->bind();
buffers[0].bind();
buffers[0].allocate(vertices.data(),
static_cast<GLsizei>(vertices.size()*sizeof(float)));
program->enableAttributeArray("vertex");
program->setAttributeBuffer("vertex",GL_FLOAT,0,3);
buffers[0].release();
vaos[0]->release();
program->release();
}
//once the buffers are fill, we can empty the vectors to optimize memory consumption
nb_pos = vertices.size();
vertices.resize(0);
//"Swap trick" insures that the memory is indeed freed and not kept available
std::vector<float>(vertices).swap(vertices);
are_buffers_filled = true;
}
#include <CGAL/Three/Polyhedron_demo_plugin_helper.h>
//The actual plugin
using namespace CGAL::Three;
class Q_DECL_EXPORT Polyhedron_demo_example_plugin :
public QObject,
{
//Configures CMake to use MOC correctly
Q_OBJECT
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")
public :
// Adds an action to the menu and configures the widget
void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface*) Q_DECL_OVERRIDE{
//get the references
this->scene = scene_interface;
this->mw = mainWindow;
//creates and link the actions
QAction* actionDrawTriangle= new QAction("Draw Triangle", mw);
if(actionDrawTriangle) {
connect(actionDrawTriangle, SIGNAL(triggered()),
this, SLOT(draw_triangle()));
_actions << actionDrawTriangle;
}
}
bool applicable(QAction*) const Q_DECL_OVERRIDE
{
return true;
}
QList<QAction*> actions() const Q_DECL_OVERRIDE{
return _actions;
}
public Q_SLOTS:
void draw_triangle() {
triangle = new Scene_triangle_item(0, 0, 0,
1, 0, 0,
0.5, 0.5, 0);
triangle->setName(QString("Basic triangle"));
scene->addItem(triangle);
}
private:
QList<QAction*> _actions;
}; //end of class Polyhedron_demo_example_plugin
#include "Example_plugin.moc"