mirror of https://github.com/CGAL/cgal
Merge pull request #4212 from maxGimeno/Demo-Save_the_scene_entirely-maxGimeno
CGAL 3D Demo: Enhancement of the Scene Saving System
This commit is contained in:
commit
0c8e9cb0c1
|
|
@ -0,0 +1,39 @@
|
|||
# - Try to find the LibSSH libraries
|
||||
# This module defines:
|
||||
# LIBSSH_FOUND - system has LibSSH lib
|
||||
# LIBSSH_INCLUDE_DIR - the LibSSH include directory
|
||||
# LIBSSH_LIBRARIES_DIR - directory where the LibSSH libraries are located
|
||||
# LIBSSH_LIBRARIES - Link these to use LibSSH
|
||||
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/CGAL_GeneratorSpecificSettings.cmake)
|
||||
|
||||
if(LIBSSH_INCLUDE_DIR)
|
||||
set(LIBSSH_in_cache TRUE)
|
||||
else()
|
||||
set(LIBSSH_in_cache FALSE)
|
||||
endif()
|
||||
if(NOT LIBSSH_LIBRARIES)
|
||||
set(LIBSSH_in_cache FALSE)
|
||||
endif()
|
||||
|
||||
# Is it already configured?
|
||||
if( NOT LIBSSH_in_cache )
|
||||
|
||||
find_path(LIBSSH_INCLUDE_DIR
|
||||
NAMES "libssh/libssh.h"
|
||||
)
|
||||
|
||||
find_library(LIBSSH_LIBRARIES NAMES ssh libssh
|
||||
HINTS "/usr/lib"
|
||||
"usr/lib/x86_64-linux-gnu"
|
||||
PATH_SUFFIXES lib
|
||||
DOC "Path to the LIBSSH library"
|
||||
)
|
||||
endif()
|
||||
|
||||
SET(LIBSSH_FOUND TRUE)
|
||||
if( NOT LIBSSH_LIBRARIES OR NOT LIBSSH_INCLUDE_DIR)
|
||||
SET(LIBSSH_FOUND FALSE)
|
||||
endif()
|
||||
|
|
@ -64,7 +64,7 @@ include(${CGAL_USE_FILE})
|
|||
find_package(Qt5
|
||||
QUIET
|
||||
COMPONENTS OpenGL Script
|
||||
OPTIONAL_COMPONENTS ScriptTools)
|
||||
OPTIONAL_COMPONENTS ScriptTools WebSockets)
|
||||
|
||||
if(Qt5_FOUND)
|
||||
|
||||
|
|
@ -95,6 +95,11 @@ if( POLYHEDRON_DEMO_ACTIVATE_CONCURRENCY )
|
|||
endif()
|
||||
endif()
|
||||
|
||||
#find libssh for scene sharing
|
||||
find_package(LibSSH)
|
||||
if( NOT LIBSSH_FOUND )
|
||||
message("NOTICE : The SSH features will be disabled.")
|
||||
endif()
|
||||
|
||||
# Activate concurrency ? (turned OFF by default)
|
||||
option(CGAL_ACTIVATE_CONCURRENT_MESH_3
|
||||
|
|
@ -131,7 +136,7 @@ if(CGAL_Qt5_FOUND AND Qt5_FOUND)
|
|||
qt5_wrap_ui( statisticsUI_FILES Statistics_on_item_dialog.ui)
|
||||
qt5_wrap_ui( FileLoaderDialogUI_files FileLoaderDialog.ui )
|
||||
qt5_wrap_ui( Show_point_dialogUI_FILES Show_point_dialog.ui )
|
||||
qt5_wrap_ui( PreferencesUI_FILES Preferences.ui Details.ui)
|
||||
qt5_wrap_ui( PreferencesUI_FILES Preferences.ui Details.ui SSH_dialog.ui)
|
||||
qt5_wrap_ui( Show_point_dialogUI_FILES Show_point_dialog.ui )
|
||||
qt5_wrap_ui( ViewerUI_FILES LightingDialog.ui)
|
||||
qt5_generate_moc( "File_loader_dialog.h" "${CMAKE_CURRENT_BINARY_DIR}/File_loader_dialog_moc.cpp" )
|
||||
|
|
@ -195,6 +200,11 @@ if(CGAL_Qt5_FOUND AND Qt5_FOUND)
|
|||
target_link_libraries(demo_framework
|
||||
PUBLIC Qt5::OpenGL Qt5::Widgets Qt5::Gui Qt5::Script
|
||||
)
|
||||
if(TARGET Qt5::WebSockets)
|
||||
target_link_libraries(demo_framework PUBLIC Qt5::WebSockets)
|
||||
message(STATUS "Qt5WebSockets was found. Using WebSockets is therefore possible.")
|
||||
endif()
|
||||
|
||||
cgal_add_compilation_test(demo_framework)
|
||||
# Let's define `three_EXPORT` during the compilation of `demo_framework`,
|
||||
# in addition of `demo_framework_EXPORT` (defined automatically by
|
||||
|
|
@ -329,11 +339,20 @@ if(CGAL_Qt5_FOUND AND Qt5_FOUND)
|
|||
MainWindow.cpp
|
||||
Polyhedron_demo.cpp
|
||||
File_loader_dialog_moc.cpp
|
||||
Use_ssh.cpp
|
||||
${CGAL_Qt5_RESOURCE_FILES} ${CGAL_Qt5_MOC_FILES}
|
||||
${FileLoaderDialogUI_files} ${MainWindowUI_files} ${PreferencesUI_FILES}
|
||||
${statisticsUI_FILES} ${SubViewerUI_files})
|
||||
target_link_libraries(polyhedron_demo PUBLIC
|
||||
demo_framework point_dialog Qt5::Gui Qt5::OpenGL Qt5::Widgets Qt5::Script)
|
||||
if(LIBSSH_FOUND)
|
||||
add_definitions(-DCGAL_USE_SSH)
|
||||
target_link_libraries(polyhedron_demo PUBLIC ${LIBSSH_LIBRARIES})
|
||||
endif() #libssh
|
||||
if(TARGET Qt5::WebSockets)
|
||||
add_definitions(-DCGAL_USE_WEBSOCKETS)
|
||||
target_link_libraries(polyhedron_demo PUBLIC Qt5::WebSockets)
|
||||
endif()
|
||||
add_executable ( Polyhedron_3 Polyhedron_3.cpp )
|
||||
target_link_libraries( Polyhedron_3 PRIVATE polyhedron_demo )
|
||||
add_to_cached_list( CGAL_EXECUTABLE_TARGETS Polyhedron_3 )
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
#ifdef CGAL_USE_SSH
|
||||
# include "CGAL/Use_ssh.h"
|
||||
#endif
|
||||
#include <cmath>
|
||||
|
||||
#include "config.h"
|
||||
|
|
@ -43,6 +46,7 @@
|
|||
#include <QWidgetAction>
|
||||
#include <QJsonArray>
|
||||
#include <QSequentialIterable>
|
||||
#include <QDir>
|
||||
#ifdef QT_SCRIPT_LIB
|
||||
# include <QScriptValue>
|
||||
# ifdef QT_SCRIPTTOOLS_LIB
|
||||
|
|
@ -59,6 +63,7 @@
|
|||
#include "ui_Preferences.h"
|
||||
#include "ui_Details.h"
|
||||
#include "ui_Statistics_on_item_dialog.h"
|
||||
#include "ui_SSH_dialog.h"
|
||||
#include "Show_point_dialog.h"
|
||||
#include "File_loader_dialog.h"
|
||||
#include "Viewer.h"
|
||||
|
|
@ -70,6 +75,14 @@
|
|||
# include <QScriptEngine>
|
||||
# include <QScriptValue>
|
||||
#include "Color_map.h"
|
||||
|
||||
|
||||
#ifdef CGAL_USE_WEBSOCKETS
|
||||
#include <QWebSocketServer>
|
||||
#include <QWebSocket>
|
||||
#include <QNetworkInterface>
|
||||
#endif
|
||||
|
||||
using namespace CGAL::Three;
|
||||
QScriptValue
|
||||
myScene_itemToScriptValue(QScriptEngine *engine,
|
||||
|
|
@ -151,6 +164,7 @@ MainWindow::MainWindow(const QStringList &keywords, bool verbose, QWidget* paren
|
|||
// remove the Load Script menu entry, when the demo has not been compiled with QT_SCRIPT_LIB
|
||||
#if !defined(QT_SCRIPT_LIB)
|
||||
ui->menuBar->removeAction(ui->actionLoadScript);
|
||||
ui->menuBar->removeAction(ui->on_actionLoad_a_Scene_from_a_Script_File);
|
||||
#endif
|
||||
// Save some pointers from ui, for latter use.
|
||||
sceneView = ui->sceneView;
|
||||
|
|
@ -159,9 +173,9 @@ MainWindow::MainWindow(const QStringList &keywords, bool verbose, QWidget* paren
|
|||
CGAL::Three::Three::s_mainviewer = viewer;
|
||||
viewer->setObjectName("mainViewer");
|
||||
viewer_window->showMaximized();
|
||||
viewer_window->setWindowFlags(
|
||||
viewer_window->setWindowFlags(
|
||||
Qt::SubWindow
|
||||
| Qt::CustomizeWindowHint
|
||||
| Qt::CustomizeWindowHint
|
||||
| Qt::WindowMaximizeButtonHint
|
||||
| Qt::WindowSystemMenuHint
|
||||
| Qt::WindowTitleHint
|
||||
|
|
@ -354,7 +368,7 @@ MainWindow::MainWindow(const QStringList &keywords, bool verbose, QWidget* paren
|
|||
|
||||
// Load plugins, and re-enable actions that need it.
|
||||
operationSearchBar.setPlaceholderText("Filter...");
|
||||
searchAction->setDefaultWidget(&operationSearchBar);
|
||||
searchAction->setDefaultWidget(&operationSearchBar);
|
||||
connect(&operationSearchBar, &QLineEdit::textChanged,
|
||||
this, [=](){filterOperations(true);});
|
||||
loadPlugins();
|
||||
|
|
@ -690,7 +704,7 @@ bool MainWindow::load_plugin(QString fileName, bool blacklisted)
|
|||
else{
|
||||
//qdebug << "error: " << qPrintable(loader.errorString());
|
||||
pluginsStatus_map[name] = loader.errorString();
|
||||
|
||||
|
||||
}
|
||||
PathNames_map[name].push_back(fileinfo.absoluteDir().absolutePath());
|
||||
return true;
|
||||
|
|
@ -934,7 +948,7 @@ void MainWindow::viewerShow(float xmin,
|
|||
}
|
||||
|
||||
void MainWindow::viewerShow(Viewer_interface* vi, float x, float y, float z) {
|
||||
|
||||
|
||||
CGAL::qglviewer::ManipulatedCameraFrame backup_frame(*vi->camera()->frame());
|
||||
vi->camera()->fitSphere(CGAL::qglviewer::Vec(x, y, z),
|
||||
vi->camera()->sceneRadius()/100);
|
||||
|
|
@ -1001,20 +1015,21 @@ void MainWindow::computeViewerBBox(CGAL::qglviewer::Vec& min, CGAL::qglviewer::V
|
|||
const double xmax = bbox.xmax();
|
||||
const double ymax = bbox.ymax();
|
||||
const double zmax = bbox.zmax();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
min = CGAL::qglviewer::Vec(xmin, ymin, zmin);
|
||||
max= CGAL::qglviewer::Vec(xmax, ymax, zmax);
|
||||
|
||||
|
||||
CGAL::qglviewer::Vec bbox_center((xmin+xmax)/2, (ymin+ymax)/2, (zmin+zmax)/2);
|
||||
|
||||
double bbox_diag = CGAL::approximate_sqrt(
|
||||
CGAL::square(xmax - xmin)
|
||||
+ CGAL::square(ymax - ymin)
|
||||
+ CGAL::square(zmax - zmin));
|
||||
|
||||
CGAL::qglviewer::Vec offset(0,0,0);
|
||||
|
||||
|
||||
double l_dist = (std::max)((std::abs)(bbox_center.x - viewer->offset().x),
|
||||
(std::max)((std::abs)(bbox_center.y - viewer->offset().y),
|
||||
(std::abs)(bbox_center.z - viewer->offset().z)));
|
||||
|
|
@ -1240,7 +1255,7 @@ void MainWindow::open(QString filename)
|
|||
settings.setValue("OFF open directory",
|
||||
fileinfo.absoluteDir().absolutePath());
|
||||
loadItem(fileinfo, findLoader(load_pair.first), ok);
|
||||
|
||||
|
||||
if(!ok)
|
||||
return;
|
||||
this->addToRecentFiles(fileinfo.absoluteFilePath());
|
||||
|
|
@ -1427,7 +1442,7 @@ void MainWindow::selectionChanged()
|
|||
{
|
||||
if(vi == NULL)
|
||||
continue;
|
||||
|
||||
|
||||
if(item != NULL && item->manipulatable()) {
|
||||
vi->setManipulatedFrame(item->manipulatedFrame());
|
||||
} else {
|
||||
|
|
@ -1562,7 +1577,7 @@ void MainWindow::showSceneContextMenu(const QPoint& p) {
|
|||
menu_actions["line width"] = action->menu()->actions().last();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Q_FOREACH(Scene::Item_id index, scene->selectionIndices())
|
||||
{
|
||||
|
|
@ -1595,7 +1610,7 @@ void MainWindow::showSceneContextMenu(const QPoint& p) {
|
|||
)->value());
|
||||
slider->setOrientation(Qt::Horizontal);
|
||||
sliderAction->setDefaultWidget(slider);
|
||||
|
||||
|
||||
connect(slider, &QSlider::valueChanged, [this, slider]()
|
||||
{
|
||||
Q_FOREACH(Scene::Item_id id, scene->selectionIndices())
|
||||
|
|
@ -1630,7 +1645,7 @@ void MainWindow::showSceneContextMenu(const QPoint& p) {
|
|||
)->value());
|
||||
slider->setOrientation(Qt::Horizontal);
|
||||
sliderAction->setDefaultWidget(slider);
|
||||
|
||||
|
||||
connect(slider, &QSlider::valueChanged, [this, slider]()
|
||||
{
|
||||
Q_FOREACH(Scene::Item_id id, scene->selectionIndices())
|
||||
|
|
@ -1665,7 +1680,7 @@ void MainWindow::showSceneContextMenu(const QPoint& p) {
|
|||
)->value());
|
||||
slider->setOrientation(Qt::Horizontal);
|
||||
sliderAction->setDefaultWidget(slider);
|
||||
|
||||
|
||||
connect(slider, &QSlider::valueChanged, [this, slider]()
|
||||
{
|
||||
Q_FOREACH(Scene::Item_id id, scene->selectionIndices())
|
||||
|
|
@ -1708,7 +1723,7 @@ void MainWindow::showSceneContextMenu(const QPoint& p) {
|
|||
)->value());
|
||||
slider->setOrientation(Qt::Horizontal);
|
||||
sliderAction->setDefaultWidget(slider);
|
||||
|
||||
|
||||
connect(slider, &QSlider::valueChanged, [this, slider]()
|
||||
{
|
||||
Q_FOREACH(Scene::Item_id id, scene->selectionIndices())
|
||||
|
|
@ -1832,6 +1847,7 @@ void MainWindow::readSettings()
|
|||
this->default_point_size = settings.value("points_size").toInt();
|
||||
this->default_normal_length = settings.value("normals_length").toInt();
|
||||
this->default_lines_width = settings.value("lines_width").toInt();
|
||||
setProperty("ws_url", settings.value("ws_server_url").toString());
|
||||
}
|
||||
|
||||
void MainWindow::writeSettings()
|
||||
|
|
@ -1906,14 +1922,7 @@ void MainWindow::throw_exception() {
|
|||
void MainWindow::on_actionLoadScript_triggered()
|
||||
{
|
||||
#if defined(QT_SCRIPT_LIB)
|
||||
QString filename = QFileDialog::getOpenFileName(
|
||||
this,
|
||||
tr("Select a script to run..."),
|
||||
".",
|
||||
"QTScripts (*.js);;All Files (*)");
|
||||
if(filename.isEmpty())
|
||||
return;
|
||||
loadScript(QFileInfo(filename));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -1943,7 +1952,7 @@ void MainWindow::on_actionLoad_triggered()
|
|||
filters << filter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QString directory = settings.value("OFF open directory",
|
||||
QDir::current().dirName()).toString();
|
||||
|
||||
|
|
@ -1971,7 +1980,7 @@ void MainWindow::on_actionLoad_triggered()
|
|||
static_cast<unsigned>(nb_files),
|
||||
std::back_inserter(colors_));
|
||||
std::size_t nb_item = -1;
|
||||
|
||||
|
||||
Q_FOREACH(const QString& filename, dialog.selectedFiles()) {
|
||||
|
||||
CGAL::Three::Scene_item* item = NULL;
|
||||
|
|
@ -2216,7 +2225,6 @@ void MainWindow::on_actionPreferences_triggered()
|
|||
QDialog dialog(this);
|
||||
Ui::PreferencesDialog prefdiag;
|
||||
prefdiag.setupUi(&dialog);
|
||||
|
||||
float lineWidth[2];
|
||||
if(!viewer->isOpenGL_4_3())
|
||||
viewer->glGetFloatv(GL_LINE_WIDTH_RANGE, lineWidth);
|
||||
|
|
@ -2227,22 +2235,22 @@ void MainWindow::on_actionPreferences_triggered()
|
|||
}
|
||||
prefdiag.linesHorizontalSlider->setMinimum(lineWidth[0]);
|
||||
prefdiag.linesHorizontalSlider->setMaximum(lineWidth[1]);
|
||||
|
||||
|
||||
prefdiag.offset_updateCheckBox->setChecked(
|
||||
settings.value("offset_update", false).toBool());
|
||||
connect(prefdiag.offset_updateCheckBox, SIGNAL(toggled(bool)),
|
||||
scene, SLOT(enableVisibilityRecentering(bool)));
|
||||
|
||||
|
||||
prefdiag.antialiasingCheckBox->setChecked(settings.value("antialiasing", false).toBool());
|
||||
connect(prefdiag.antialiasingCheckBox, SIGNAL(toggled(bool)),
|
||||
viewer, SLOT(setAntiAliasing(bool)));
|
||||
|
||||
|
||||
prefdiag.quick_cameraCheckBox->setChecked(
|
||||
settings.value("quick_camera_mode", true).toBool());
|
||||
connect(prefdiag.quick_cameraCheckBox, SIGNAL(toggled(bool)),
|
||||
viewer, SLOT(setFastDrawing(bool)));
|
||||
prefdiag.max_itemsSpinBox->setValue(viewer->textRenderer()->getMax_textItems());
|
||||
|
||||
|
||||
connect(prefdiag.max_itemsSpinBox,static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
|
||||
this, [this](int i){
|
||||
setMaxTextItemsDisplayed(i);
|
||||
|
|
@ -2273,20 +2281,20 @@ void MainWindow::on_actionPreferences_triggered()
|
|||
});
|
||||
connect(prefdiag.background_colorPushButton, &QPushButton::clicked,
|
||||
this, &MainWindow::setBackgroundColor);
|
||||
|
||||
|
||||
connect(prefdiag.default_save_asPushButton, &QPushButton::clicked,
|
||||
this, &MainWindow::setDefaultSaveDir);
|
||||
|
||||
|
||||
connect(prefdiag.lightingPushButton, &QPushButton::clicked,
|
||||
this, &MainWindow::setLighting_triggered);
|
||||
|
||||
|
||||
prefdiag.surface_meshComboBox->setCurrentText(CGAL::Three::Three::modeName(
|
||||
CGAL::Three::Three::s_defaultSMRM));
|
||||
connect(prefdiag.surface_meshComboBox, &QComboBox::currentTextChanged,
|
||||
this, [this](const QString& text){
|
||||
this->s_defaultSMRM = CGAL::Three::Three::modeFromName(text);
|
||||
});
|
||||
|
||||
|
||||
prefdiag.point_setComboBox->setCurrentText(CGAL::Three::Three::modeName(
|
||||
CGAL::Three::Three::s_defaultPSRM));
|
||||
connect(prefdiag.point_setComboBox, &QComboBox::currentTextChanged,
|
||||
|
|
@ -2360,6 +2368,70 @@ void MainWindow::on_actionPreferences_triggered()
|
|||
});
|
||||
dialog.exec();
|
||||
});
|
||||
connect(prefdiag.sshButton, &QPushButton::clicked,
|
||||
this, [this](){
|
||||
QDialog dialog(this);
|
||||
Ui::SSHDialog sshdiag;
|
||||
sshdiag.setupUi(&dialog);
|
||||
|
||||
#ifdef CGAL_USE_SSH
|
||||
sshdiag.userBox->setEnabled(true);
|
||||
sshdiag.serverBox->setEnabled(true);
|
||||
sshdiag.pkBox->setEnabled(true);
|
||||
sshdiag.privkBox->setEnabled(true);
|
||||
sshdiag.userEdit->setText(settings.value("ssh_user", QString()).toString());
|
||||
sshdiag.serverEdit->setText(settings.value("ssh_server", QString()).toString());
|
||||
sshdiag.publicEdit->setText(settings.value("ssh_public_key", QString()).toString());
|
||||
sshdiag.privkEdit->setText(settings.value("ssh_priv_key", QString()).toString());
|
||||
connect(sshdiag.pubButton, &QPushButton::clicked,
|
||||
this, [this, sshdiag](){
|
||||
QFileDialog diag(this,
|
||||
"Public Key",
|
||||
"",
|
||||
"All Files (*)");
|
||||
diag.setFilter(QDir::Hidden|QDir::Files|QDir::Dirs|QDir::NoDotAndDotDot);
|
||||
if(!diag.exec())
|
||||
return;
|
||||
sshdiag.publicEdit->setText(diag.selectedFiles().front());
|
||||
});
|
||||
connect(sshdiag.privButton, &QPushButton::clicked,
|
||||
this, [this, sshdiag](){
|
||||
QFileDialog diag(this,
|
||||
"Private Key",
|
||||
"",
|
||||
"All Files (*)");
|
||||
diag.setFilter(QDir::Hidden|QDir::Files|QDir::Dirs|QDir::NoDotAndDotDot);
|
||||
if(!diag.exec())
|
||||
return;
|
||||
sshdiag.privkEdit->setText(diag.selectedFiles().front());
|
||||
});
|
||||
#else
|
||||
sshdiag.userBox->setEnabled(false);
|
||||
sshdiag.serverBox->setEnabled(false);
|
||||
sshdiag.pkBox->setEnabled(false);
|
||||
sshdiag.privkBox->setEnabled(false);
|
||||
#endif
|
||||
sshdiag.wsEdit->setText(settings.value("ws_server_url", QString()).toString());
|
||||
|
||||
dialog.exec();
|
||||
if ( dialog.result() )
|
||||
{
|
||||
#ifdef CGAL_USE_SSH
|
||||
settings.setValue("ssh_user",
|
||||
sshdiag.userEdit->text());
|
||||
settings.setValue("ssh_server",
|
||||
sshdiag.serverEdit->text());
|
||||
settings.setValue("ssh_public_key",
|
||||
sshdiag.publicEdit->text());
|
||||
settings.setValue("ssh_priv_key",
|
||||
sshdiag.privkEdit->text());
|
||||
#endif
|
||||
settings.setValue("ws_server_url",
|
||||
sshdiag.wsEdit->text());
|
||||
setProperty("ws_url", sshdiag.wsEdit->text());
|
||||
}
|
||||
});
|
||||
|
||||
dialog.exec();
|
||||
|
||||
if ( dialog.result() )
|
||||
|
|
@ -2372,7 +2444,7 @@ void MainWindow::on_actionPreferences_triggered()
|
|||
if (item->checkState(0)==Qt::Unchecked)
|
||||
plugin_blacklist.insert(item->text(1));
|
||||
}
|
||||
|
||||
|
||||
//write settings
|
||||
settings.setValue("antialiasing",
|
||||
prefdiag.antialiasingCheckBox->isChecked());
|
||||
|
|
@ -2392,7 +2464,7 @@ void MainWindow::on_actionPreferences_triggered()
|
|||
settings.setValue("points_size", this->default_point_size);
|
||||
settings.setValue("normals_length", this->default_normal_length);
|
||||
settings.setValue("lines_width", this->default_lines_width);
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -2412,7 +2484,7 @@ void MainWindow::setBackgroundColor()
|
|||
v->update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::setLighting_triggered()
|
||||
|
|
@ -2574,7 +2646,7 @@ QString MainWindow::get_item_stats()
|
|||
Q_FOREACH(int id, scene->selectionIndices())
|
||||
{
|
||||
Scene_item* item = scene->item(id);
|
||||
QString classname = item->property("classname").toString();
|
||||
QString classname = item->property("classname").toString();
|
||||
if(classname.isEmpty())
|
||||
classname = item->metaObject()->className();
|
||||
if(!classnames.contains(classname))
|
||||
|
|
@ -2801,53 +2873,183 @@ void MainWindow::propagate_action()
|
|||
}
|
||||
}
|
||||
|
||||
QString make_fullpath(const QString& filename, bool duplicate = false)
|
||||
{
|
||||
QString fullpath = QString("%1/%2").arg(QDir::tempPath()).arg(filename);
|
||||
QString tmp_fullpath = fullpath;
|
||||
if(duplicate)
|
||||
{
|
||||
int i=0;
|
||||
while(QFileInfo(tmp_fullpath).exists())
|
||||
{
|
||||
QString basename = QFileInfo(tmp_fullpath).baseName();
|
||||
QString dir = QFileInfo(tmp_fullpath).dir().path();
|
||||
QString suffix= QFileInfo(fullpath).completeSuffix();
|
||||
tmp_fullpath=QString("%1/%2%3.%4").arg(dir).arg(basename).arg(++i).arg(suffix);
|
||||
}
|
||||
}
|
||||
return tmp_fullpath;
|
||||
}
|
||||
/*
|
||||
The two following functions allow to create files from string and strings from files.
|
||||
This is used as a workaround of the absence of stream management in our IO system.
|
||||
The whole to/from Base64 is used to avoid problems with binary formats. Everything is written
|
||||
as a base64 binary string, and converted back to what it was.
|
||||
*/
|
||||
QByteArray file_to_string(const char* filename)
|
||||
{
|
||||
std::ifstream f(filename, std::ifstream::binary);
|
||||
// get size of file
|
||||
f.seekg (0,f.end);
|
||||
long size = f.tellg();
|
||||
f.seekg (0);
|
||||
std::ostringstream ss;
|
||||
// allocate memory for file content
|
||||
char* buffer = new char[size];
|
||||
|
||||
// read content of infile
|
||||
f.read(buffer,size);
|
||||
|
||||
// write to outfile
|
||||
ss.write(buffer,size);
|
||||
// release dynamically-allocated memory
|
||||
delete[] buffer;
|
||||
//ss.write( << f.rdbuf(); // reading data
|
||||
f.close();
|
||||
std::string st = ss.str();
|
||||
QByteArray ba(st.c_str(), static_cast<int>(st.size()));
|
||||
return ba;
|
||||
}
|
||||
|
||||
QString MainWindow::write_string_to_file(const QString& str, const QString &filename)
|
||||
{
|
||||
QString fullpath = make_fullpath(filename);
|
||||
std::ofstream f(fullpath.toStdString().c_str(), std::ofstream::binary);
|
||||
QByteArray compressed_item(str.toStdString().c_str());
|
||||
QByteArray item = qUncompress(QByteArray::fromBase64(compressed_item));
|
||||
QByteArray bb = item;
|
||||
f.write(bb.constData(),bb.toStdString().size());
|
||||
f.close();
|
||||
return fullpath;
|
||||
}
|
||||
|
||||
void MainWindow::on_actionSa_ve_Scene_as_Script_triggered()
|
||||
{
|
||||
QString filename =
|
||||
QFileDialog::getSaveFileName(this,
|
||||
"Save the Scene as a Script File",
|
||||
last_saved_dir,
|
||||
"Qt Script files (*.js)");
|
||||
std::ofstream os(filename.toUtf8());
|
||||
if(scene->numberOfEntries() == 0)
|
||||
return;
|
||||
bool do_upload = false;
|
||||
#ifdef CGAL_USE_SSH
|
||||
QString user = settings.value("ssh_user", QString()).toString();
|
||||
QString pass;
|
||||
if(!user.isEmpty())
|
||||
{
|
||||
QMessageBox::StandardButton doyou =
|
||||
QMessageBox::question(this, tr("Upload ?"), tr("Do you wish to upload the scene"
|
||||
" using the SSH preferences ?"));
|
||||
bool ok;
|
||||
do_upload = (doyou == QMessageBox::Yes);
|
||||
if(do_upload)
|
||||
{
|
||||
pass = QInputDialog::getText(this, "SSH Password",
|
||||
"Enter ssh key password:",
|
||||
QLineEdit::Password,
|
||||
tr(""),
|
||||
&ok);
|
||||
if(!ok)
|
||||
return;
|
||||
pass = pass.trimmed();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
QString filename;
|
||||
|
||||
if(do_upload){
|
||||
filename = QString("%1/save_scene.js").arg(QDir::tempPath());
|
||||
}else{
|
||||
filename = QFileDialog::getSaveFileName(this,
|
||||
"Save the Scene as a Script File",
|
||||
last_saved_dir,
|
||||
"Qt Script files (*.js)");
|
||||
}
|
||||
std::ofstream os(filename.toUtf8(), std::ofstream::binary);
|
||||
if(!os)
|
||||
return;
|
||||
std::vector<QString> names;
|
||||
std::vector<QString> loaders;
|
||||
CGAL::Three::Three::CursorScopeGuard cs(Qt::WaitCursor);
|
||||
std::vector<std::pair<QString, QString> > names;
|
||||
std::vector<std::pair<QString, QString> > loaders;
|
||||
std::vector<QColor> colors;
|
||||
std::vector<int> rendering_modes;
|
||||
QStringList not_saved;
|
||||
for(int i = 0; i < scene->numberOfEntries(); ++i)
|
||||
{
|
||||
Scene_item* item = scene->item(i);
|
||||
QString loader = item->property("loader_name").toString();
|
||||
QString source = item->property("source filename").toString();
|
||||
QString loader;// = item->property("loader_name").toString();
|
||||
QString ext;
|
||||
for(Polyhedron_demo_io_plugin_interface* iop : io_plugins)
|
||||
{
|
||||
if(iop->isDefaultLoader(item))
|
||||
{
|
||||
QString sf = iop->saveNameFilters().split(";;").first();
|
||||
//OFF Files (*.off)
|
||||
QRegularExpression re("\\(\\*\\.(.*)\\)");
|
||||
QRegularExpressionMatch rem = re.match(sf);
|
||||
if(!rem.hasMatch())
|
||||
continue;
|
||||
ext = rem.captured(1);
|
||||
QList<Scene_item*>to_save;
|
||||
to_save.append(item);
|
||||
QString savename(tr("%1.%2").arg(item->name()).arg(ext));
|
||||
QString fullpath = make_fullpath(savename, true);
|
||||
savename = QFileInfo(fullpath).fileName();
|
||||
iop->save(QFileInfo(fullpath), to_save);
|
||||
names.push_back(std::make_pair(savename, item->name()));
|
||||
loader=iop->name();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(loader.isEmpty())
|
||||
{
|
||||
not_saved.push_back(item->name());
|
||||
QMessageBox::warning(this, "", tr("No plugin found for %1. Not saved.").arg(item->name()));
|
||||
continue;
|
||||
}
|
||||
names.push_back(source);
|
||||
loaders.push_back(loader);
|
||||
loaders.push_back(std::make_pair(loader, ext));
|
||||
colors.push_back(item->color());
|
||||
rendering_modes.push_back(item->renderingMode());
|
||||
}
|
||||
if(loaders.empty())
|
||||
return;
|
||||
//path
|
||||
os << "var camera = \""<<viewer->dumpCameraCoordinates().toStdString()<<"\";\n";
|
||||
os << "var items = [";
|
||||
for(std::size_t i = 0; i< names.size() -1; ++i)
|
||||
{
|
||||
os << "\'" << names[i].toStdString() << "\', ";
|
||||
QString fullpath = make_fullpath(names[i].first);
|
||||
|
||||
QByteArray item = file_to_string(fullpath.toStdString().c_str());
|
||||
os<<"[\'";
|
||||
os<<qCompress(item, 9).toBase64().toStdString().c_str();
|
||||
os << "\', \'"<<names[i].second.toStdString().c_str()<<"\']," ;
|
||||
//delete temp file
|
||||
QFile tmp_file(fullpath);
|
||||
tmp_file.remove();
|
||||
}
|
||||
os<<"\'"<<names.back().toStdString()<<"\'];\n";
|
||||
|
||||
QString fullpath = make_fullpath(names.back().first);
|
||||
QByteArray item = file_to_string(fullpath.toStdString().c_str());
|
||||
os<<"[\'";
|
||||
os<<qCompress(item, 9).toBase64().toStdString().c_str();
|
||||
os << "\', \'"<<names.back().second.toStdString().c_str()<<"\']];\n";
|
||||
//delete temp file
|
||||
QFile tmp_file(fullpath);
|
||||
tmp_file.remove();
|
||||
//plugin
|
||||
os << "var loaders = [";
|
||||
for(std::size_t i = 0; i< names.size() -1; ++i)
|
||||
{
|
||||
os << "\'" << loaders[i].toStdString() << "\', ";
|
||||
os << "[\'" << loaders[i].first.toStdString() << "\', \'"<<loaders[i].second.toStdString()<< "\'],";
|
||||
}
|
||||
os<<"\'"<<loaders.back().toStdString()<<"\'];\n";
|
||||
|
||||
os << "[\'" << loaders.back().first.toStdString() << "\', \'"<<loaders.back().second.toStdString()<< "\']];\n";
|
||||
|
||||
//color
|
||||
os << "var colors = [";
|
||||
for(std::size_t i = 0; i< names.size() -1; ++i)
|
||||
|
|
@ -2855,7 +3057,7 @@ void MainWindow::on_actionSa_ve_Scene_as_Script_triggered()
|
|||
os << "[" << colors[i].red() <<", "<< colors[i].green() <<", "<< colors[i].blue() <<"], ";
|
||||
}
|
||||
os<<"[" << colors.back().red() <<", "<< colors.back().green() <<", "<< colors.back().blue() <<"]];\n";
|
||||
|
||||
|
||||
//rendering mode
|
||||
os << "var rendering_modes = [";
|
||||
for(std::size_t i = 0; i< names.size() -1; ++i)
|
||||
|
|
@ -2863,10 +3065,13 @@ void MainWindow::on_actionSa_ve_Scene_as_Script_triggered()
|
|||
os << rendering_modes[i] << ", ";
|
||||
}
|
||||
os << rendering_modes.back()<<"];\n";
|
||||
os <<"var initial_scene_size = scene.numberOfEntries;\n";
|
||||
os << "items.forEach(function(item, index, array){\n";
|
||||
os << " main_window.open(item, loaders[index]);\n";
|
||||
os << " var it = scene.item(initial_scene_size+index);\n";
|
||||
os<<" var path=items[index][1];\n";
|
||||
os<<" path+='.';\n";
|
||||
os<<" path+=loaders[index][1];\n";
|
||||
os<<" var fullpath = main_window.write_string_to_file(item[0], path);\n";
|
||||
os<<" main_window.open(fullpath,loaders[index][0]);\n";
|
||||
os << " var it = scene.item(scene.numberOfEntries-1);\n";
|
||||
os << " var r = colors[index][0];\n";
|
||||
os << " var g = colors[index][1];\n";
|
||||
os << " var b = colors[index][2];\n";
|
||||
|
|
@ -2880,6 +3085,60 @@ void MainWindow::on_actionSa_ve_Scene_as_Script_triggered()
|
|||
"Items Not Saved",
|
||||
QString("The following items could not be saved: %1").arg(
|
||||
not_saved.join(", ")));
|
||||
#ifdef CGAL_USE_SSH
|
||||
using namespace CGAL::ssh_internal;
|
||||
if(do_upload)
|
||||
{
|
||||
QString server = settings.value("ssh_server", QString()).toString();
|
||||
QString pk = settings.value("ssh_public_key", QString()).toString();
|
||||
QString privK = settings.value("ssh_priv_key", QString()).toString();
|
||||
user = user.trimmed();
|
||||
server = server.trimmed();
|
||||
pk = pk.trimmed();
|
||||
privK=privK.trimmed();
|
||||
if(user.isEmpty()){
|
||||
return;
|
||||
}
|
||||
QString path;
|
||||
path = QInputDialog::getText(this,
|
||||
"",
|
||||
tr("Enter the destination path for your file."));
|
||||
if(path.isEmpty())
|
||||
return;
|
||||
try{
|
||||
ssh_session session;
|
||||
bool res = establish_ssh_session(session,
|
||||
user.toStdString().c_str(),
|
||||
server.toStdString().c_str(),
|
||||
pk.toStdString().c_str(),
|
||||
privK.toStdString().c_str(),
|
||||
pass.toStdString().c_str());
|
||||
if(!res)
|
||||
{
|
||||
QMessageBox::warning(this,
|
||||
"Error",
|
||||
"The SSH session could not be started.");
|
||||
return;
|
||||
}
|
||||
res = push_file(session,path.toStdString().c_str(), filename.toStdString().c_str());
|
||||
if(!res)
|
||||
{
|
||||
QMessageBox::warning(this,
|
||||
"Error",
|
||||
"The file could not be uploaded. Check your console for more information.");
|
||||
close_connection(session);
|
||||
return;
|
||||
}
|
||||
close_connection(session);
|
||||
QFile tmp_file(filename);
|
||||
tmp_file.remove();
|
||||
} catch( ssh::SshException e )
|
||||
{
|
||||
std::cout << "Error during connection : ";
|
||||
std::cout << e.getError() << std::endl;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
void MainWindow::setTransparencyPasses(int val)
|
||||
{
|
||||
|
|
@ -2907,7 +3166,7 @@ void MainWindow::toggleFullScreen()
|
|||
dock->show();
|
||||
}
|
||||
visibleDockWidgets.clear();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2930,7 +3189,7 @@ void MainWindow::setupViewer(Viewer* viewer, SubViewer* subviewer)
|
|||
viewer, SLOT(update()));
|
||||
connect(scene, SIGNAL(updated()),
|
||||
viewer, SLOT(update()));
|
||||
|
||||
|
||||
QAction* action = subviewer->findChild<QAction*>("actionRecenter");
|
||||
connect(action, SIGNAL(triggered()),
|
||||
viewer, SLOT(update()));
|
||||
|
|
@ -2992,16 +3251,43 @@ void MainWindow::setupViewer(Viewer* viewer, SubViewer* subviewer)
|
|||
this, SLOT(selectSceneItem(int)));
|
||||
connect(viewer, SIGNAL(selectedPoint(double, double, double)),
|
||||
this, SLOT(showSelectedPoint(double, double, double)));
|
||||
|
||||
|
||||
connect(viewer, SIGNAL(selectionRay(double, double, double,
|
||||
double, double, double)),
|
||||
scene, SIGNAL(selectionRay(double, double, double,
|
||||
double, double, double)));
|
||||
|
||||
|
||||
connect(viewer, &Viewer::sendMessage,
|
||||
this, [](QString s){
|
||||
information(s);
|
||||
});
|
||||
|
||||
#ifdef CGAL_USE_WEBSOCKETS
|
||||
action= subviewer->viewer->findChild<QAction*>("actionShareCamera");
|
||||
connect(action, &QAction::toggled,
|
||||
this, [this, viewer](bool b)
|
||||
{
|
||||
if(!viewer){
|
||||
return;
|
||||
}
|
||||
QString session;
|
||||
if(b){
|
||||
bool ok;
|
||||
session = QInputDialog::getText(
|
||||
this,"Session",
|
||||
"Please enter the session name.\n"
|
||||
"Only the machines that enter the same session name will be connected.\n"
|
||||
"Several sessions can run simultaneously on a same server. ",
|
||||
QLineEdit::Normal, QString(), &ok);
|
||||
if(session.isEmpty() || !ok)
|
||||
{
|
||||
viewer->setShareCam(false, session);
|
||||
return;
|
||||
}
|
||||
}
|
||||
viewer->setShareCam(b, session);
|
||||
});
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -3021,7 +3307,7 @@ void MainWindow::on_actionAdd_Viewer_triggered()
|
|||
scene->removeViewer(viewer2);
|
||||
viewerDestroyed(viewer2);
|
||||
});
|
||||
|
||||
|
||||
setupViewer(viewer2, subviewer);
|
||||
viewer2->camera()->interpolateToFitScene();
|
||||
subviewer->show();
|
||||
|
|
@ -3154,11 +3440,20 @@ SubViewer::SubViewer(QWidget *parent, MainWindow* mw, Viewer* mainviewer)
|
|||
QAction* actionTotalPass = new QAction("Set Transparency Pass &Number...",this);
|
||||
actionTotalPass->setObjectName("actionTotalPass");
|
||||
viewMenu->addAction(actionTotalPass);
|
||||
#ifdef CGAL_USE_WEBSOCKETS
|
||||
QAction* actionShareCamera= new QAction("Join &WS Server",viewer);
|
||||
actionShareCamera->setObjectName("actionShareCamera");
|
||||
actionShareCamera->setCheckable(true);
|
||||
actionShareCamera->setChecked(false);
|
||||
viewMenu->addAction(actionShareCamera);
|
||||
#endif
|
||||
|
||||
QAction* actionBackFrontShading = new QAction("Activate Back/Front shading.",this);
|
||||
actionBackFrontShading->setObjectName("actionBackFrontShading");
|
||||
actionBackFrontShading->setCheckable(true);
|
||||
actionBackFrontShading->setChecked(false);
|
||||
viewMenu->addAction(actionBackFrontShading);
|
||||
|
||||
if(mainviewer)
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
setWindowIcon(QIcon(":/cgal/icons/resources/menu.png"));
|
||||
|
|
@ -3206,7 +3501,7 @@ void SubViewer::color()
|
|||
|
||||
void SubViewer::closeEvent(QCloseEvent *closeEvent)
|
||||
{
|
||||
|
||||
|
||||
if(is_main)
|
||||
{
|
||||
QMessageBox::information(mw, "", "This is the main viewer. It cannot be closed.");
|
||||
|
|
@ -3227,9 +3522,9 @@ void SubViewer::changeEvent(QEvent *event)
|
|||
{
|
||||
menu->addAction(action);
|
||||
}
|
||||
setWindowFlags(
|
||||
setWindowFlags(
|
||||
Qt::SubWindow
|
||||
| Qt::CustomizeWindowHint
|
||||
| Qt::CustomizeWindowHint
|
||||
| Qt::WindowMaximizeButtonHint
|
||||
//| Qt::WindowSystemMenuHint
|
||||
| Qt::WindowTitleHint
|
||||
|
|
@ -3245,9 +3540,9 @@ void SubViewer::changeEvent(QEvent *event)
|
|||
{
|
||||
menu->removeAction(action);
|
||||
}
|
||||
setWindowFlags(
|
||||
setWindowFlags(
|
||||
Qt::SubWindow
|
||||
| Qt::CustomizeWindowHint
|
||||
| Qt::CustomizeWindowHint
|
||||
| Qt::WindowMaximizeButtonHint
|
||||
| Qt::WindowSystemMenuHint
|
||||
| Qt::WindowTitleHint
|
||||
|
|
@ -3269,7 +3564,7 @@ void MainWindow::invalidate_bbox(bool do_recenter)
|
|||
|
||||
void MainWindow::on_action_Save_triggered()
|
||||
{
|
||||
if(QMessageBox::question(this, "Save", "Are you sure you want to override these files ?")
|
||||
if(QMessageBox::question(this, "Save", "Are you sure you want to override these files ?")
|
||||
== QMessageBox::No)
|
||||
return;
|
||||
QList<Scene_item*> to_save;
|
||||
|
|
@ -3285,3 +3580,187 @@ void MainWindow::on_action_Save_triggered()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_actionLoad_a_Scene_from_a_Script_File_triggered()
|
||||
{
|
||||
bool do_download = false;
|
||||
QString filename;
|
||||
|
||||
#ifdef CGAL_USE_SSH
|
||||
QString user = settings.value("ssh_user", QString()).toString();
|
||||
QString pass;
|
||||
if(!user.isEmpty())
|
||||
{
|
||||
QMessageBox::StandardButton doyou =
|
||||
QMessageBox::question(this, tr("Download ?"), tr("Do you wish to download the scene"
|
||||
" using the SSH preferences ?"));
|
||||
bool ok;
|
||||
do_download= (doyou == QMessageBox::Yes);
|
||||
if(do_download)
|
||||
{
|
||||
pass = QInputDialog::getText(this, "SSH Password",
|
||||
"Enter ssh key password:",
|
||||
QLineEdit::Password,
|
||||
tr(""),
|
||||
&ok);
|
||||
if(!ok)
|
||||
return;
|
||||
pass = pass.trimmed();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if(do_download)
|
||||
{
|
||||
#ifdef CGAL_USE_SSH
|
||||
using namespace CGAL::ssh_internal;
|
||||
QString server = settings.value("ssh_server", QString()).toString();
|
||||
QString pk = settings.value("ssh_public_key", QString()).toString();
|
||||
QString privK = settings.value("ssh_priv_key", QString()).toString();
|
||||
user = user.trimmed();
|
||||
server = server.trimmed();
|
||||
pk = pk.trimmed();
|
||||
privK=privK.trimmed();
|
||||
QString path;
|
||||
path = QInputDialog::getText(this,
|
||||
"",
|
||||
tr("Enter the remote path for your file."));
|
||||
if(path.isEmpty())
|
||||
return;
|
||||
try{
|
||||
ssh_session session;
|
||||
bool res = establish_ssh_session(session,
|
||||
user.toStdString().c_str(),
|
||||
server.toStdString().c_str(),
|
||||
pk.toStdString().c_str(),
|
||||
privK.toStdString().c_str(),
|
||||
pass.toStdString().c_str());
|
||||
if(!res)
|
||||
{
|
||||
QMessageBox::warning(this,
|
||||
"Error",
|
||||
"The SSH session could not be started.");
|
||||
return;
|
||||
}
|
||||
filename = QString("%1/load_scene.js").arg(QDir::tempPath());
|
||||
path = tr("/tmp/%2").arg(path);
|
||||
res = pull_file(session,path.toStdString().c_str(), filename.toStdString().c_str());
|
||||
if(!res)
|
||||
{
|
||||
QMessageBox::warning(this,
|
||||
"Error",
|
||||
"The file could not be fetched. Check your console for more info.");
|
||||
close_connection(session);
|
||||
return;
|
||||
}
|
||||
close_connection(session);
|
||||
} catch( ssh::SshException e )
|
||||
{
|
||||
std::cout << "Error during connection : ";
|
||||
std::cout << e.getError() << std::endl;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
filename = QFileDialog::getOpenFileName(
|
||||
this,
|
||||
tr("Select a Whole Scene file..."),
|
||||
".",
|
||||
"Whole Scene files (*.js)");
|
||||
if(filename.isEmpty())
|
||||
return;
|
||||
}
|
||||
loadScript(QFileInfo(filename));
|
||||
if(do_download){
|
||||
QFile tmp_file(filename);
|
||||
tmp_file.remove();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CGAL_USE_WEBSOCKETS
|
||||
void MainWindow::on_action_Start_a_Session_triggered()
|
||||
{
|
||||
QAction * action= findChild<QAction*>("action_Start_a_Session");
|
||||
static EchoServer *server =nullptr;
|
||||
if(action->isChecked()){
|
||||
server = new EchoServer(1234);
|
||||
QObject::connect(server, &EchoServer::closed, server,&EchoServer::deleteLater);
|
||||
}
|
||||
else
|
||||
{
|
||||
server->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
EchoServer::EchoServer(quint16 port) :
|
||||
QObject(CGAL::Three::Three::mainWindow()),
|
||||
m_pWebSocketServer(new QWebSocketServer(QStringLiteral("Echo Server"),
|
||||
QWebSocketServer::NonSecureMode, this))
|
||||
{
|
||||
if (m_pWebSocketServer->listen(QHostAddress::Any, port)) {
|
||||
connect(m_pWebSocketServer, &QWebSocketServer::newConnection,
|
||||
this, &EchoServer::onNewConnection);
|
||||
connect(m_pWebSocketServer, &QWebSocketServer::closed, this, &EchoServer::closed);
|
||||
}
|
||||
QHostAddress local_host("0.0.0.0");
|
||||
|
||||
//to avoid printing 127.0.0.1. Not realy sure it won't ever print the external ipv4 though.
|
||||
const QHostAddress &localhost = QHostAddress(QHostAddress::LocalHost);
|
||||
for (const QHostAddress &address: QNetworkInterface::allAddresses()) {
|
||||
if (address.protocol() == QAbstractSocket::IPv4Protocol && address != localhost)
|
||||
{
|
||||
local_host= address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
QMessageBox mb(QMessageBox::NoIcon, "WS Server",
|
||||
tr("WebSockets Server started.\nEnter the following address in\nyour Network Preferences to be able to join it :\n"
|
||||
"ws://%1:%2").arg(local_host.toString()).arg(port), QMessageBox::Ok, CGAL::Three::Three::mainWindow());
|
||||
mb.setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
mb.exec();
|
||||
}
|
||||
|
||||
EchoServer::~EchoServer()
|
||||
{
|
||||
m_pWebSocketServer->close();
|
||||
qDeleteAll(m_clients.begin(), m_clients.end());
|
||||
}
|
||||
|
||||
void EchoServer::onNewConnection()
|
||||
{
|
||||
QWebSocket *pSocket = m_pWebSocketServer->nextPendingConnection();
|
||||
|
||||
connect(pSocket, &QWebSocket::textMessageReceived, this, &EchoServer::processTextMessage);
|
||||
connect(pSocket, &QWebSocket::binaryMessageReceived, this, &EchoServer::processBinaryMessage);
|
||||
connect(pSocket, &QWebSocket::disconnected, this, &EchoServer::socketDisconnected);
|
||||
|
||||
m_clients << pSocket;
|
||||
}
|
||||
|
||||
void EchoServer::processTextMessage(QString message)
|
||||
{
|
||||
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
|
||||
for(auto *client : m_clients) {
|
||||
if(client != pClient)
|
||||
client->sendTextMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
void EchoServer::processBinaryMessage(QByteArray message)
|
||||
{
|
||||
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
|
||||
if (pClient) {
|
||||
pClient->sendBinaryMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
void EchoServer::socketDisconnected()
|
||||
{
|
||||
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
|
||||
if (pClient) {
|
||||
m_clients.removeAll(pClient);
|
||||
pClient->deleteLater();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -138,6 +138,8 @@ public Q_SLOTS:
|
|||
This slot is for use by scripts.*/
|
||||
bool open(QString filename, QString loader_name);
|
||||
|
||||
QString write_string_to_file(const QString &str, const QString& filename);
|
||||
|
||||
/*! Reloads an item. Expects to be called by a QAction with the
|
||||
index of the item to be reloaded as data attached to the action.
|
||||
The index must identify a valid `Scene_item`.*/
|
||||
|
|
@ -352,6 +354,10 @@ protected Q_SLOTS:
|
|||
void save(QString filename, QList<CGAL::Three::Scene_item*>& to_save);
|
||||
//!Calls the function saveSnapShot of the viewer.
|
||||
void on_actionSaveSnapshot_triggered();
|
||||
#ifdef CGAL_USE_WEBSOCKETS
|
||||
//!Starts a new WS server if none is already exist. Else, does nothing.
|
||||
void on_action_Start_a_Session_triggered();
|
||||
#endif
|
||||
//!Opens a Dialog to choose a color and make it the background color.
|
||||
void setBackgroundColor();
|
||||
//!Opens a Dialog to change the lighting settings
|
||||
|
|
@ -447,6 +453,7 @@ public:
|
|||
#endif
|
||||
public Q_SLOTS:
|
||||
void on_actionSa_ve_Scene_as_Script_triggered();
|
||||
void on_actionLoad_a_Scene_from_a_Script_File_triggered();
|
||||
void toggleFullScreen();
|
||||
void setDefaultSaveDir();
|
||||
void invalidate_bbox(bool do_recenter);
|
||||
|
|
@ -494,4 +501,30 @@ protected:
|
|||
private:
|
||||
bool is_main;
|
||||
};
|
||||
#ifdef CGAL_USE_WEBSOCKETS
|
||||
QT_FORWARD_DECLARE_CLASS(QWebSocketServer)
|
||||
QT_FORWARD_DECLARE_CLASS(QWebSocket)
|
||||
|
||||
class EchoServer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit EchoServer(quint16 port);
|
||||
~EchoServer();
|
||||
|
||||
|
||||
Q_SIGNALS:
|
||||
void closed();
|
||||
|
||||
private Q_SLOTS:
|
||||
void onNewConnection();
|
||||
void processTextMessage(QString message);
|
||||
void processBinaryMessage(QByteArray message);
|
||||
void socketDisconnected();
|
||||
|
||||
private:
|
||||
QWebSocketServer *m_pWebSocketServer;
|
||||
QList<QWebSocket *> m_clients;
|
||||
};
|
||||
#endif
|
||||
#endif // ifndef MAINWINDOW_H
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@
|
|||
<addaction name="action_Save"/>
|
||||
<addaction name="actionSaveAs"/>
|
||||
<addaction name="actionSa_ve_Scene_as_Script"/>
|
||||
<addaction name="actionLoad_a_Scene_from_a_Script_File"/>
|
||||
<addaction name="actionSaveSnapshot"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionLoadScript"/>
|
||||
|
|
@ -96,6 +97,7 @@
|
|||
<addaction name="action_Rearrange_Viewers"/>
|
||||
<addaction name="menuDockWindows"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Start_a_Session"/>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
<addaction name="menuEdit"/>
|
||||
|
|
@ -463,6 +465,22 @@
|
|||
<string>&Save</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionLoad_a_Scene_from_a_Script_File">
|
||||
<property name="text">
|
||||
<string>Load a Scene &from a Script File...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Start_a_Session">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Start a Session</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Start a WebSocket Server to Share your Camera with Others on your Network.</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="Polyhedron_3.qrc"/>
|
||||
|
|
|
|||
|
|
@ -860,6 +860,12 @@ public:
|
|||
return ok;
|
||||
}
|
||||
|
||||
bool isDefaultLoader(const Scene_item* item) const Q_DECL_OVERRIDE{
|
||||
if(qobject_cast<const Scene_edges_item*>(item))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
using Polyhedron_demo_io_plugin_interface::init;
|
||||
void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface,
|
||||
Messages_interface* m) override;
|
||||
|
|
|
|||
|
|
@ -78,61 +78,6 @@ class Io_3mf_plugin:
|
|||
std::vector<std::string> names;
|
||||
QList<Scene_item*> result;
|
||||
std::vector<std::vector<CGAL::Color> > all_colors;
|
||||
int nb_polylines =
|
||||
CGAL::read_polylines_from_3mf(fileinfo.filePath().toUtf8().toStdString(),
|
||||
all_points, all_colors, names);
|
||||
if(nb_polylines < 0 )
|
||||
{
|
||||
ok = false;
|
||||
std::cerr << "Error in reading of meshes."<<std::endl;
|
||||
return result;
|
||||
}
|
||||
|
||||
for(int i=0; i< nb_polylines; ++i)
|
||||
{
|
||||
Scene_polylines_item* pol_item = new Scene_polylines_item();
|
||||
PolylineRange& polylines = pol_item->polylines;
|
||||
polylines.push_back(all_points[i]);
|
||||
pol_item->setName(names[i].data());
|
||||
pol_item->invalidateOpenGLBuffers();
|
||||
CGAL::Color c = all_colors[i].front();
|
||||
pol_item->setColor(QColor(c.red(), c.green(), c.blue()));
|
||||
pol_item->setProperty("already_colord", true);
|
||||
result << pol_item;
|
||||
if(add_to_scene)
|
||||
CGAL::Three::Three::scene()->addItem(pol_item);
|
||||
}
|
||||
all_points.clear();
|
||||
all_colors.clear();
|
||||
names.clear();
|
||||
int nb_point_sets =
|
||||
CGAL::read_point_clouds_from_3mf(fileinfo.filePath().toUtf8().toStdString(),
|
||||
all_points, all_colors, names);
|
||||
if(nb_point_sets < 0 )
|
||||
{
|
||||
ok = false;
|
||||
std::cerr << "Error in reading of meshes."<<std::endl;
|
||||
return result;
|
||||
}
|
||||
for(int i=0; i< nb_point_sets; ++i)
|
||||
{
|
||||
Scene_points_with_normal_item* pts_item = new Scene_points_with_normal_item();
|
||||
for(std::size_t j = 0; j < all_points[i].size(); ++j)
|
||||
{
|
||||
pts_item->point_set()->insert(all_points[i][j]);
|
||||
}
|
||||
pts_item->setName(names[i].data());
|
||||
pts_item->invalidateOpenGLBuffers();
|
||||
CGAL::Color c = all_colors[i].front();
|
||||
pts_item->setColor(QColor(c.red(), c.green(), c.blue()));
|
||||
pts_item->setProperty("already_colord", true);
|
||||
result << pts_item;
|
||||
if(add_to_scene)
|
||||
CGAL::Three::Three::scene()->addItem(pts_item);
|
||||
}
|
||||
all_points.clear();
|
||||
names.clear();
|
||||
all_colors.clear();
|
||||
int nb_meshes =
|
||||
CGAL::read_triangle_soups_from_3mf(fileinfo.filePath().toUtf8().toStdString(),
|
||||
all_points, all_polygons, all_colors, names);
|
||||
|
|
|
|||
|
|
@ -15,13 +15,18 @@ class Polyhedron_demo_io_nef_plugin :
|
|||
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0" FILE "nef_io_plugin.json")
|
||||
|
||||
public:
|
||||
QString nameFilters() const;
|
||||
QString name() const { return "io_nef_plugin"; }
|
||||
bool canLoad(QFileInfo) const;
|
||||
QList<Scene_item*> load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true);
|
||||
QString nameFilters() const override;
|
||||
QString name() const override { return "io_nef_plugin"; }
|
||||
bool canLoad(QFileInfo) const override;
|
||||
QList<Scene_item*> load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true) override;
|
||||
|
||||
bool canSave(const CGAL::Three::Scene_item*);
|
||||
bool save(QFileInfo fileinfo,QList<CGAL::Three::Scene_item*>& items);
|
||||
bool canSave(const CGAL::Three::Scene_item*) override;
|
||||
bool save(QFileInfo fileinfo,QList<CGAL::Three::Scene_item*>& items) override;
|
||||
bool isDefaultLoader(const Scene_item* item) const override{
|
||||
if(qobject_cast<const Scene_nef_polyhedron_item*>(item))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
QString Polyhedron_demo_io_nef_plugin::nameFilters() const {
|
||||
|
|
|
|||
|
|
@ -29,28 +29,28 @@ class Polyhedron_demo_off_plugin :
|
|||
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.IOPluginInterface/1.90" FILE "off_io_plugin.json")
|
||||
|
||||
public:
|
||||
bool isDefaultLoader(const Scene_item *item) const
|
||||
bool isDefaultLoader(const Scene_item *item) const override
|
||||
{
|
||||
if(qobject_cast<const Scene_surface_mesh_item*>(item)
|
||||
|| qobject_cast<const Scene_polygon_soup_item*>(item))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
bool isDefaultLoader(const QString& name) const
|
||||
bool isDefaultLoader(const QString& name) const override
|
||||
{
|
||||
if(name == QString("off"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
QString name() const { return "off_plugin"; }
|
||||
QString nameFilters() const { return "OFF files (*.off);;Wavefront OBJ (*.obj)"; }
|
||||
bool canLoad(QFileInfo fileinfo) const;
|
||||
QList<Scene_item*> load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true);
|
||||
QString name() const override{ return "off_plugin"; }
|
||||
QString nameFilters() const override { return "OFF files (*.off);;Wavefront OBJ (*.obj)"; }
|
||||
bool canLoad(QFileInfo fileinfo) const override;
|
||||
QList<Scene_item*> load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true) override;
|
||||
CGAL::Three::Scene_item* load_off(QFileInfo fileinfo);
|
||||
CGAL::Three::Scene_item* load_obj(QFileInfo fileinfo);
|
||||
|
||||
bool canSave(const CGAL::Three::Scene_item*);
|
||||
bool save(QFileInfo fileinfo,QList<CGAL::Three::Scene_item*>& );
|
||||
bool canSave(const CGAL::Three::Scene_item*) override;
|
||||
bool save(QFileInfo fileinfo,QList<CGAL::Three::Scene_item*>& ) override;
|
||||
};
|
||||
|
||||
bool Polyhedron_demo_off_plugin::canLoad(QFileInfo) const {
|
||||
|
|
|
|||
|
|
@ -23,19 +23,19 @@ class Polyhedron_demo_ply_plugin :
|
|||
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.IOPluginInterface/1.90" FILE "ply_io_plugin.json")
|
||||
|
||||
public:
|
||||
bool isDefaultLoader(const CGAL::Three::Scene_item *item) const
|
||||
bool isDefaultLoader(const CGAL::Three::Scene_item *item) const override
|
||||
{
|
||||
if(qobject_cast<const Scene_points_with_normal_item*>(item))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
QString name() const { return "ply_plugin"; }
|
||||
QString nameFilters() const { return "PLY files (*.ply)"; }
|
||||
bool canLoad(QFileInfo fileinfo) const;
|
||||
QList<Scene_item*> load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true);
|
||||
QString name() const override{ return "ply_plugin"; }
|
||||
QString nameFilters() const override{ return "PLY files (*.ply)"; }
|
||||
bool canLoad(QFileInfo fileinfo) const override;
|
||||
QList<Scene_item*> load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true)override;
|
||||
|
||||
bool canSave(const CGAL::Three::Scene_item*);
|
||||
bool save(QFileInfo fileinfo,QList<CGAL::Three::Scene_item*>&);
|
||||
bool canSave(const CGAL::Three::Scene_item*)override;
|
||||
bool save(QFileInfo fileinfo,QList<CGAL::Three::Scene_item*>&)override;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,12 @@ public:
|
|||
return QList<QAction*>()<<actionSplit_polylines
|
||||
<<actionJoin_polylines;
|
||||
}
|
||||
|
||||
bool isDefaultLoader(const Scene_item* item) const override{
|
||||
if(qobject_cast<const Scene_polylines_item*>(item))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
protected Q_SLOTS:
|
||||
//!Splits the selected Scene_polylines_item in multiple items all containing a single polyline.
|
||||
void split();
|
||||
|
|
|
|||
|
|
@ -290,10 +290,11 @@ public:
|
|||
|
||||
|
||||
QString nameFilters() const {
|
||||
return "VTK PolyData files (*.vtk);; VTK XML PolyData (*.vtp);; VTK XML UnstructuredGrid (*.vtu)"; }
|
||||
return "VTK XML UnstructuredGrid (*.vtu);;VTK PolyData files (*.vtk);; VTK XML PolyData (*.vtp)"; }
|
||||
QString name() const { return "vtk_plugin"; }
|
||||
bool canSave(const CGAL::Three::Scene_item* item)
|
||||
{
|
||||
|
||||
return (qobject_cast<const Scene_facegraph_item*>(item)
|
||||
|| qobject_cast<const Scene_c3t3_item*>(item));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,24 +17,24 @@ class LCC_io_plugin :
|
|||
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.IOPluginInterface/1.90" FILE "lcc_io_plugin.json")
|
||||
|
||||
public:
|
||||
bool isDefaultLoader(const CGAL::Three::Scene_item *item) const
|
||||
bool isDefaultLoader(const CGAL::Three::Scene_item *item) const override
|
||||
{
|
||||
if(qobject_cast<const Scene_lcc_item*>(item))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
QString name() const { return "lcc_plugin"; }
|
||||
QString nameFilters() const { return
|
||||
QString name() const override{ return "lcc_plugin"; }
|
||||
QString nameFilters() const override{ return
|
||||
"OFF files (*.off);;"
|
||||
"3-map files (*.3map)"; }
|
||||
|
||||
QString saveNameFilters() const {
|
||||
QString saveNameFilters() const override{
|
||||
return
|
||||
"3-map files (*.3map)";
|
||||
}
|
||||
|
||||
bool canLoad(QFileInfo) const { return true; }
|
||||
QList<CGAL::Three::Scene_item*> load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true){
|
||||
bool canLoad(QFileInfo) const override{ return true; }
|
||||
QList<CGAL::Three::Scene_item*> load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true) override{
|
||||
// Open file
|
||||
std::ifstream ifs(fileinfo.filePath().toUtf8());
|
||||
if(!ifs) {
|
||||
|
|
@ -67,8 +67,8 @@ public:
|
|||
}
|
||||
|
||||
|
||||
bool canSave(const CGAL::Three::Scene_item*){return false;}
|
||||
bool save(QFileInfo, QList<CGAL::Three::Scene_item*>& ){
|
||||
bool canSave(const CGAL::Three::Scene_item*)override{return false;}
|
||||
bool save(QFileInfo, QList<CGAL::Three::Scene_item*>& )override{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,15 +18,20 @@ class Polyhedron_demo_c3t3_binary_io_plugin :
|
|||
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.IOPluginInterface/1.90" FILE "c3t3_io_plugin.json")
|
||||
|
||||
public:
|
||||
QString name() const { return "C3t3_io_plugin"; }
|
||||
QString nameFilters() const { return "binary files (*.cgal);;ascii (*.mesh);;maya (*.ma)"; }
|
||||
QString saveNameFilters() const { return "binary files (*.cgal);;ascii (*.mesh);;maya (*.ma);;avizo (*.am);;OFF files (*.off)"; }
|
||||
QString loadNameFilters() const { return "binary files (*.cgal);;ascii (*.mesh)"; }
|
||||
bool canLoad(QFileInfo) const;
|
||||
QList<Scene_item*> load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true);
|
||||
QString name() const override { return "C3t3_io_plugin"; }
|
||||
QString nameFilters() const override { return "binary files (*.cgal);;ascii (*.mesh);;maya (*.ma)"; }
|
||||
QString saveNameFilters() const override { return "binary files (*.cgal);;ascii (*.mesh);;maya (*.ma);;avizo (*.am);;OFF files (*.off)"; }
|
||||
QString loadNameFilters() const override { return "binary files (*.cgal);;ascii (*.mesh)"; }
|
||||
bool canLoad(QFileInfo) const override;
|
||||
QList<Scene_item*> load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true) override;
|
||||
|
||||
bool canSave(const CGAL::Three::Scene_item*);
|
||||
bool save(QFileInfo fileinfo,QList<CGAL::Three::Scene_item*>& );
|
||||
bool canSave(const CGAL::Three::Scene_item*) override;
|
||||
bool save(QFileInfo fileinfo,QList<CGAL::Three::Scene_item*>& ) override;
|
||||
bool isDefaultLoader(const Scene_item* item) const override{
|
||||
if(qobject_cast<const Scene_c3t3_item*>(item))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
bool try_load_other_binary_format(std::istream& in, C3t3& c3t3);
|
||||
|
|
|
|||
|
|
@ -296,6 +296,11 @@ public:
|
|||
items.pop_front();
|
||||
return ok;
|
||||
}
|
||||
bool isDefaultLoader(const Scene_item* item) const override{
|
||||
if(qobject_cast<const Scene_image_item*>(item))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
QString name() const override{ return "segmented images"; }
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -127,6 +127,12 @@ public:
|
|||
return res;
|
||||
}
|
||||
|
||||
bool isDefaultLoader(const Scene_item* item) const override{
|
||||
if(qobject_cast<const Scene_polyhedron_selection_item*>(item))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool applicable(QAction* action) const override {
|
||||
if(action == actionSelfIntersection)
|
||||
return qobject_cast<Scene_face_graph_item*>(scene->item(scene->mainSelectionIndex()));
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>631</width>
|
||||
<height>526</height>
|
||||
<height>628</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
|
|
@ -58,9 +58,9 @@
|
|||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>-96</y>
|
||||
<y>0</y>
|
||||
<width>275</width>
|
||||
<height>528</height>
|
||||
<height>534</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
|
|
@ -285,6 +285,16 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="sshButton">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Network Settings</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="backFrontColor_pushButton">
|
||||
<property name="text">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,151 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SSHDialog</class>
|
||||
<widget class="QDialog" name="SSHDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>415</width>
|
||||
<height>441</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>SSH Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>SSH Preferences</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="userBox">
|
||||
<property name="title">
|
||||
<string>User</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="userEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="serverBox">
|
||||
<property name="title">
|
||||
<string>Server</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="serverEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="pkBox">
|
||||
<property name="title">
|
||||
<string>Public Key </string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="publicEdit"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pubButton">
|
||||
<property name="text">
|
||||
<string>Browse...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="privkBox">
|
||||
<property name="title">
|
||||
<string>Private Key</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="privkEdit"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="privButton">
|
||||
<property name="text">
|
||||
<string>Browse...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Camera Synchronization Server</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="wsEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>SSHDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>SSHDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
|
@ -0,0 +1,286 @@
|
|||
// Copyright (c) 2020 GeometryFactory (France). All rights reserved.
|
||||
//
|
||||
// This file is part of CGAL (www.cgal.org)
|
||||
//
|
||||
// $URL$
|
||||
// $Id$
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial
|
||||
//
|
||||
//
|
||||
// Author(s) : Maxime Gimeno
|
||||
|
||||
#ifdef CGAL_USE_SSH
|
||||
|
||||
#include <CGAL/Three/Three.h>
|
||||
|
||||
#include "CGAL/Use_ssh.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
bool test_result(int res)
|
||||
{
|
||||
switch(res){
|
||||
|
||||
case SSH_AUTH_ERROR:
|
||||
{
|
||||
std::cerr<<"Auth failed with error: "<<std::endl;
|
||||
return false;
|
||||
}
|
||||
case SSH_AUTH_DENIED:
|
||||
{
|
||||
std::cerr<<"The server doesn't accept that public key as an authentication token. Try another key or another method."<<std::endl;
|
||||
return false;
|
||||
}
|
||||
case SSH_AUTH_PARTIAL :
|
||||
{
|
||||
std::cerr<<"You've been partially authenticated, you still have to use another method."<<std::endl;
|
||||
return false;
|
||||
}
|
||||
case SSH_OK:
|
||||
return true;
|
||||
case SSH_EOF:
|
||||
std::cerr<<"key doesn't exist."<<std::endl;
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
namespace CGAL{
|
||||
namespace ssh_internal{
|
||||
|
||||
bool establish_ssh_session(ssh_session &session,
|
||||
const char* user,
|
||||
const char* server,
|
||||
const char* pub_key_path,
|
||||
const char* priv_key_path,
|
||||
const char* priv_key_password)
|
||||
{
|
||||
int port = 22;
|
||||
|
||||
//Can use SSH_LOG_PROTOCOL here for verbose output
|
||||
int verbosity = SSH_LOG_NOLOG;
|
||||
int res;
|
||||
//retry 4 times max each time the connection asks to be retried.
|
||||
for(int k = 0; k < 4; ++k)
|
||||
{
|
||||
session = ssh_new();
|
||||
ssh_options_set( session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity );
|
||||
ssh_options_set( session, SSH_OPTIONS_PORT, &port );
|
||||
ssh_options_set( session, SSH_OPTIONS_USER, user );
|
||||
ssh_options_set( session, SSH_OPTIONS_HOST, server);
|
||||
|
||||
ssh_connect(session);
|
||||
#if LIBSSH_VERSION_MAJOR <1 && LIBSSH_VERSION_MINOR < 8
|
||||
if( ssh_is_server_known(session) != SSH_SERVER_KNOWN_OK )
|
||||
#else
|
||||
if( ssh_session_is_known_server(session) != SSH_KNOWN_HOSTS_OK )
|
||||
#endif
|
||||
{
|
||||
if(QMessageBox::warning(CGAL::Three::Three::mainWindow(), QString("Unknown Server"),
|
||||
QString ("The server you are trying to join is not known.\n"
|
||||
"Do you wish to add it to the known servers list and continue?"),
|
||||
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#if LIBSSH_VERSION_MAJOR <1 && LIBSSH_VERSION_MINOR < 8
|
||||
if( ssh_write_knownhost(session) != SSH_OK )
|
||||
#else
|
||||
if( ssh_session_update_known_hosts(session) != SSH_OK )
|
||||
#endif
|
||||
{
|
||||
std::cerr << "writeKnownHost failed" << std::endl;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ssh_connect(session);
|
||||
}
|
||||
}
|
||||
ssh_key pubkey = ssh_key_new();
|
||||
ssh_pki_import_pubkey_file(pub_key_path, &pubkey);
|
||||
res = ssh_userauth_try_publickey(session, NULL, pubkey);
|
||||
if(res == SSH_AUTH_AGAIN)
|
||||
ssh_disconnect(session);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if(!test_result(res))
|
||||
{
|
||||
ssh_disconnect(session);
|
||||
return false;
|
||||
}
|
||||
|
||||
ssh_key privkey = ssh_key_new();
|
||||
res = ssh_pki_import_privkey_file(priv_key_path, priv_key_password, NULL, NULL, &privkey);
|
||||
if (!test_result(res))
|
||||
{
|
||||
ssh_disconnect(session);
|
||||
return false;
|
||||
}
|
||||
res = ssh_userauth_publickey(session, NULL, privkey);
|
||||
if(!test_result(res))
|
||||
{
|
||||
ssh_disconnect(session);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void close_connection(ssh_session &session)
|
||||
{
|
||||
ssh_disconnect(session);
|
||||
}
|
||||
|
||||
bool push_file(ssh_session &session,
|
||||
const char* dest_path,
|
||||
const char* filepath)
|
||||
{
|
||||
//copy a file
|
||||
ssh_scp scp = ssh_scp_new(
|
||||
session, SSH_SCP_WRITE | SSH_SCP_RECURSIVE, "/tmp");
|
||||
if (scp == NULL)
|
||||
{
|
||||
std::cerr<<"Error allocating scp session: %s\n"
|
||||
<< ssh_get_error(session)<<std::endl;
|
||||
return false;
|
||||
}
|
||||
int res = ssh_scp_init(scp);
|
||||
if(res != SSH_OK)
|
||||
{
|
||||
std::cerr<< "Error initializing scp session: %s\n"
|
||||
<< ssh_get_error(session)<<std::endl;
|
||||
ssh_scp_free(scp);
|
||||
ssh_disconnect(session);
|
||||
return false;
|
||||
}
|
||||
//read a file into a buffer
|
||||
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
|
||||
if(!file)
|
||||
{
|
||||
std::cerr<<"File not found."<<std::endl;
|
||||
ssh_scp_free(scp);
|
||||
ssh_disconnect(session);
|
||||
return false;
|
||||
}
|
||||
std::streamsize size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<char> buffer(size);
|
||||
if (!file.read(buffer.data(), size))
|
||||
{
|
||||
std::cerr<<"error while reading file."<<std::endl;
|
||||
ssh_disconnect(session);
|
||||
return false;
|
||||
}
|
||||
//push a file to /tmp
|
||||
res = ssh_scp_push_directory(scp, ".", 0707);
|
||||
if (res != SSH_OK)
|
||||
{
|
||||
std::cerr<<"Can't create remote directory: %s\n"
|
||||
<<ssh_get_error(session)<<std::endl;
|
||||
ssh_disconnect(session);
|
||||
return false;
|
||||
}
|
||||
res = ssh_scp_push_file
|
||||
(scp, dest_path, size, 0707);
|
||||
if (res != SSH_OK)
|
||||
{
|
||||
std::cerr<< "Can't open remote file: %s\n"
|
||||
<< ssh_get_error(session)<<std::endl;
|
||||
ssh_disconnect(session);
|
||||
return false;
|
||||
}
|
||||
res = ssh_scp_write(scp, buffer.data(), size);
|
||||
//some versions of libssh don't copy everything without this.
|
||||
//This is the case for the official version on Ubuntu 18.04
|
||||
std::chrono::duration<int, std::micro> timespan(size);
|
||||
std::this_thread::sleep_for(timespan);
|
||||
if (res != SSH_OK)
|
||||
{
|
||||
std::cerr<< "Can't write to remote file: %s\n"
|
||||
<< ssh_get_error(session)<<std::endl;
|
||||
ssh_disconnect(session);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pull_file(ssh_session &session,
|
||||
const char* from_path,
|
||||
const char* to_path)
|
||||
{
|
||||
int rc;
|
||||
std::size_t size;
|
||||
std::size_t processed = 0;
|
||||
std::vector<char> buffer;
|
||||
|
||||
ssh_scp scp = ssh_scp_new(
|
||||
session, SSH_SCP_READ | SSH_SCP_RECURSIVE, from_path);
|
||||
if (scp == NULL)
|
||||
{
|
||||
std::cerr<<"Error allocating scp session: %s\n"
|
||||
<< ssh_get_error(session)<<std::endl;
|
||||
return false;
|
||||
}
|
||||
int res = ssh_scp_init(scp);
|
||||
if(res != SSH_OK)
|
||||
{
|
||||
std::cerr<< "Error initializing scp session: %s\n"
|
||||
<< ssh_get_error(session)<<std::endl;
|
||||
ssh_scp_free(scp);
|
||||
ssh_disconnect(session);
|
||||
return false;
|
||||
}
|
||||
rc = ssh_scp_pull_request(scp);
|
||||
if (rc != SSH_SCP_REQUEST_NEWFILE)
|
||||
{
|
||||
std::cerr<< "Error receiving information about file: %s\n"
|
||||
<< ssh_get_error(session)<<std::endl;
|
||||
ssh_scp_free(scp);
|
||||
ssh_disconnect(session);
|
||||
return false;
|
||||
}
|
||||
size = ssh_scp_request_get_size64(scp);
|
||||
buffer.resize(size);
|
||||
if(ssh_scp_accept_request(scp) != SSH_OK)
|
||||
{
|
||||
std::cerr<< "Could not accept request."<<std::endl;
|
||||
ssh_scp_free(scp);
|
||||
ssh_disconnect(session);
|
||||
return false;
|
||||
}
|
||||
|
||||
do{
|
||||
rc = ssh_scp_read(scp, buffer.data() + processed, size-processed);
|
||||
if (rc == SSH_ERROR)
|
||||
{
|
||||
std::cerr<< "Error receiving file data: %s\n"<< ssh_get_error(session)<<std::endl;
|
||||
//free(buffer);
|
||||
ssh_scp_free(scp);
|
||||
ssh_disconnect(session);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
processed += rc;
|
||||
}while(processed != size);
|
||||
std::ofstream file(to_path, std::ios::binary |std::ios::trunc);
|
||||
if(!file.write(buffer.data(), size))
|
||||
{
|
||||
std::cerr<<"Error while writing file."<<std::endl;
|
||||
}
|
||||
file.close();
|
||||
ssh_scp_free(scp);
|
||||
return true;
|
||||
}
|
||||
|
||||
}}
|
||||
#endif
|
||||
|
|
@ -16,6 +16,11 @@
|
|||
#include <QApplication>
|
||||
#include <QOpenGLDebugLogger>
|
||||
#include <QStyleFactory>
|
||||
#include <QAction>
|
||||
#include <QRegularExpressionMatch>
|
||||
#ifdef CGAL_USE_WEBSOCKETS
|
||||
#include <QtWebSockets/QWebSocket>
|
||||
#endif
|
||||
|
||||
#include <CGAL/Three/Three.h>
|
||||
|
||||
|
|
@ -40,6 +45,7 @@ public:
|
|||
bool inDrawWithNames;
|
||||
bool clipping;
|
||||
bool projection_is_ortho;
|
||||
bool cam_sharing;
|
||||
GLfloat gl_point_size;
|
||||
QVector4D clipbox[6];
|
||||
QPainter *painter;
|
||||
|
|
@ -111,6 +117,12 @@ public:
|
|||
{
|
||||
return shader_programs;
|
||||
}
|
||||
#ifdef CGAL_USE_WEBSOCKETS
|
||||
QWebSocket m_webSocket;
|
||||
#endif
|
||||
bool is_connected;
|
||||
QString session;
|
||||
QUrl m_url;
|
||||
};
|
||||
|
||||
class LightingDialog :
|
||||
|
|
@ -268,6 +280,7 @@ void Viewer::doBindings()
|
|||
d->spec_power = viewer_settings.value("spec_power", 51.8).toFloat();
|
||||
d->scene = 0;
|
||||
d->projection_is_ortho = false;
|
||||
d->cam_sharing = false;
|
||||
d->twosides = false;
|
||||
this->setProperty("draw_two_sides", false);
|
||||
this->setProperty("back_front_shading", false);
|
||||
|
|
@ -278,6 +291,7 @@ void Viewer::doBindings()
|
|||
d->shader_programs.resize(NB_OF_PROGRAMS);
|
||||
d->textRenderer = new TextRenderer();
|
||||
d->is_2d_selection_mode = false;
|
||||
d->is_connected = false;
|
||||
|
||||
connect( d->textRenderer, SIGNAL(sendMessage(QString,int)),
|
||||
this, SLOT(printMessage(QString,int)) );
|
||||
|
|
@ -623,7 +637,7 @@ void Viewer::mousePressEvent(QMouseEvent* event)
|
|||
d->showDistance(event->pos());
|
||||
event->accept();
|
||||
}
|
||||
else {
|
||||
else{
|
||||
makeCurrent();
|
||||
CGAL::QGLViewer::mousePressEvent(event);
|
||||
}
|
||||
|
|
@ -1931,6 +1945,79 @@ bool Viewer::isClipping() const
|
|||
{
|
||||
return d->clipping;
|
||||
}
|
||||
#ifdef CGAL_USE_WEBSOCKETS
|
||||
void Viewer::setShareCam(bool b, QString session)
|
||||
{
|
||||
static bool init = false;
|
||||
if(b)
|
||||
{
|
||||
d->cam_sharing = b;
|
||||
d->session = session;
|
||||
QString ws_url
|
||||
= CGAL::Three::Three::mainWindow()->property("ws_url").toString();
|
||||
if(ws_url.isEmpty())
|
||||
{
|
||||
QMessageBox::warning(this, "Error", "No Server configured. Please go to Edit->Preferences->Network Settings and fill the \"Camera Synchronization Server\" Field.");
|
||||
}
|
||||
else{
|
||||
if(!init)
|
||||
{
|
||||
connect(&d->m_webSocket, &QWebSocket::connected, this, &Viewer::onSocketConnected);
|
||||
connect(&d->m_webSocket, &QWebSocket::disconnected, this,[this]()
|
||||
{
|
||||
d->is_connected = false;
|
||||
Viewer::socketClosed();
|
||||
});
|
||||
init = true;
|
||||
}
|
||||
d->m_webSocket.open(QUrl(ws_url));
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
QTimer::singleShot(1000, this, [this](){
|
||||
QApplication::restoreOverrideCursor();
|
||||
if(!d->is_connected){
|
||||
QMessageBox::warning(CGAL::Three::Three::mainWindow(),
|
||||
"Connection failure",
|
||||
"The requested server was not found.");
|
||||
setShareCam(false, "");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QAction* action = findChild<QAction*>("actionShareCamera");
|
||||
action->setChecked(false);
|
||||
d->m_webSocket.close();
|
||||
}
|
||||
}
|
||||
|
||||
void Viewer::onSocketConnected()
|
||||
{
|
||||
connect(&d->m_webSocket, &QWebSocket::textMessageReceived,
|
||||
this, &Viewer::onTextMessageSocketReceived);
|
||||
connect(camera()->frame(), &CGAL::qglviewer::ManipulatedCameraFrame::manipulated,
|
||||
this, [this](){
|
||||
if(d->cam_sharing){
|
||||
QString cam_state = QString("[%1] %2").arg(d->session).arg(dumpCameraCoordinates());
|
||||
//send to server
|
||||
d->m_webSocket.sendTextMessage(cam_state);
|
||||
}
|
||||
});
|
||||
d->is_connected = true;
|
||||
}
|
||||
|
||||
void Viewer::onTextMessageSocketReceived(QString message)
|
||||
{
|
||||
QString session;
|
||||
QString position;
|
||||
QRegularExpression re("\\[(.*)\\] (.*)");
|
||||
QRegularExpressionMatch match = re.match(message);
|
||||
session = match.captured(1);
|
||||
position = match.captured(2);
|
||||
if(session != d->session){
|
||||
return;
|
||||
}
|
||||
moveCameraToCoordinates(position, 0.05f);
|
||||
}
|
||||
#endif
|
||||
#include "Viewer.moc"
|
||||
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ public:
|
|||
Q_SIGNALS:
|
||||
void sendMessage(QString);
|
||||
void doneInitGL(CGAL::Three::Viewer_interface*);
|
||||
void socketClosed();
|
||||
public Q_SLOTS:
|
||||
//! Sets the antialiasing to true or false.
|
||||
void setAntiAliasing(bool b) Q_DECL_OVERRIDE;
|
||||
|
|
@ -131,6 +132,11 @@ public Q_SLOTS:
|
|||
void setBackFrontColors();
|
||||
|
||||
void messageLogged(QOpenGLDebugMessage);
|
||||
#ifdef CGAL_USE_WEBSOCKETS
|
||||
void setShareCam(bool, QString);
|
||||
void onSocketConnected();
|
||||
void onTextMessageSocketReceived(QString message);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *)Q_DECL_OVERRIDE;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef USE_SSH_H
|
||||
#define USE_SSH_H
|
||||
#include <libssh/libsshpp.hpp>
|
||||
namespace CGAL{
|
||||
namespace ssh_internal{
|
||||
//should be used inside a try/catch(ssh::SshException e)
|
||||
|
||||
//give an unitialized session.
|
||||
bool establish_ssh_session(ssh_session& session,
|
||||
const char *user,
|
||||
const char *server,
|
||||
const char *pub_key_path,
|
||||
const char *priv_key_path,
|
||||
const char *priv_key_password);
|
||||
void close_connection(ssh_session& session);
|
||||
|
||||
bool push_file(ssh_session& session,
|
||||
const char* dest_path,
|
||||
const char* filepath);
|
||||
bool pull_file(ssh_session &session,
|
||||
const char *from_path,
|
||||
const char *to_path);
|
||||
|
||||
}}
|
||||
#endif
|
||||
|
|
@ -65,12 +65,6 @@ bool extract_soups (NMR::PLib3MFModelMeshObject *pMeshObject,
|
|||
pBuffer.resize(nNeededChars + 1);
|
||||
hResult = NMR::lib3mf_object_getnameutf8(pMeshObject, &pBuffer[0], nNeededChars + 1, NULL);
|
||||
pBuffer[nNeededChars] = 0;
|
||||
std::string temp(&pBuffer[0]);
|
||||
if(temp.find("_cgal_pc") != std::string::npos
|
||||
|| temp.find("_cgal_pl")!= std::string::npos) //ignore point clouds and polylines
|
||||
{
|
||||
return false;
|
||||
}
|
||||
name = std::string(&pBuffer[0]);
|
||||
}
|
||||
else
|
||||
|
|
@ -120,151 +114,6 @@ bool extract_soups (NMR::PLib3MFModelMeshObject *pMeshObject,
|
|||
return true;
|
||||
}
|
||||
|
||||
template<typename PointRange,
|
||||
typename PolygonRange,
|
||||
typename ColorRange>
|
||||
bool extract_polylines (NMR::PLib3MFModelMeshObject *pMeshObject,
|
||||
const NMR::MODELTRANSFORM& ,
|
||||
PointRange& points,
|
||||
PolygonRange&,
|
||||
ColorRange& colors,
|
||||
std::string& name) {
|
||||
typedef typename PointRange::value_type Point_3;
|
||||
|
||||
HRESULT hResult;
|
||||
DWORD nNeededChars;
|
||||
std::vector<char> pBuffer;
|
||||
// Retrieve Mesh Name Length
|
||||
hResult = NMR::lib3mf_object_getnameutf8(pMeshObject, NULL, 0, &nNeededChars);
|
||||
if (hResult != LIB3MF_OK)
|
||||
{
|
||||
points.resize(0);
|
||||
std::cerr<<"Error during name extraction.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retrieve Mesh Name
|
||||
if (nNeededChars > 0) {
|
||||
pBuffer.resize(nNeededChars + 1);
|
||||
hResult = NMR::lib3mf_object_getnameutf8(pMeshObject, &pBuffer[0], nNeededChars + 1, NULL);
|
||||
pBuffer[nNeededChars] = 0;
|
||||
std::string temp(&pBuffer[0]);
|
||||
if(temp.find("_cgal_pl")== std::string::npos) //ignore not polylines
|
||||
{
|
||||
points.resize(0);
|
||||
return false;
|
||||
}
|
||||
name = std::string(&pBuffer[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
points.resize(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
NMR::PLib3MFPropertyHandler * pPropertyHandler;
|
||||
hResult = NMR::lib3mf_meshobject_createpropertyhandler(pMeshObject, &pPropertyHandler);
|
||||
if (hResult != LIB3MF_OK) {
|
||||
DWORD nErrorMessage;
|
||||
LPCSTR pszErrorMessage;
|
||||
std::cerr << "could not create property handler: " << std::hex << hResult << std::endl;
|
||||
NMR::lib3mf_getlasterror(pMeshObject, &nErrorMessage, &pszErrorMessage);
|
||||
std::cerr << "error #" << std::hex << nErrorMessage << ": " << pszErrorMessage << std::endl;
|
||||
NMR::lib3mf_release(pMeshObject);
|
||||
return false;
|
||||
}
|
||||
|
||||
points.resize(points.size()-3);//ignore dummy_vertices
|
||||
for(DWORD vid = 0; vid < points.size(); ++vid)
|
||||
{
|
||||
NMR::MODELMESHVERTEX pVertex;
|
||||
NMR::lib3mf_meshobject_getvertex(pMeshObject, vid+3, &pVertex);
|
||||
points[vid] =
|
||||
Point_3(pVertex.m_fPosition[0],
|
||||
pVertex.m_fPosition[1],
|
||||
pVertex.m_fPosition[2]);
|
||||
}
|
||||
|
||||
NMR::MODELMESH_TRIANGLECOLOR_SRGB pColor;
|
||||
NMR::lib3mf_propertyhandler_getcolor(pPropertyHandler, 0, &pColor);//get color of the dummy triangle
|
||||
NMR::MODELMESHCOLOR_SRGB mColor = pColor.m_Colors[0];
|
||||
colors[0]=CGAL::Color(mColor.m_Red, mColor.m_Green,
|
||||
mColor.m_Blue, mColor.m_Alpha);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename PointRange,
|
||||
typename PolygonRange,
|
||||
typename ColorRange>
|
||||
bool extract_point_clouds (NMR::PLib3MFModelMeshObject *pMeshObject,
|
||||
const NMR::MODELTRANSFORM&,
|
||||
PointRange& points,
|
||||
PolygonRange&,
|
||||
ColorRange& colors,
|
||||
std::string& name) {
|
||||
typedef typename PointRange::value_type Point_3;
|
||||
|
||||
HRESULT hResult;
|
||||
DWORD nNeededChars;
|
||||
std::vector<char> pBuffer;
|
||||
// Retrieve Mesh Name Length
|
||||
hResult = NMR::lib3mf_object_getnameutf8(pMeshObject, NULL, 0, &nNeededChars);
|
||||
if (hResult != LIB3MF_OK)
|
||||
{
|
||||
std::cerr<<"Error during name extraction.";
|
||||
points.resize(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retrieve Mesh Name
|
||||
if (nNeededChars > 0) {
|
||||
pBuffer.resize(nNeededChars + 1);
|
||||
hResult = NMR::lib3mf_object_getnameutf8(pMeshObject, &pBuffer[0], nNeededChars + 1, NULL);
|
||||
pBuffer[nNeededChars] = 0;
|
||||
std::string temp(&pBuffer[0]);
|
||||
if(temp.find("_cgal_pc")== std::string::npos) //ignore not point_cloud
|
||||
{
|
||||
points.resize(0);
|
||||
return false;
|
||||
}
|
||||
name = std::string(&pBuffer[0]);
|
||||
}
|
||||
else{
|
||||
points.resize(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
NMR::PLib3MFPropertyHandler * pPropertyHandler;
|
||||
hResult = NMR::lib3mf_meshobject_createpropertyhandler(pMeshObject, &pPropertyHandler);
|
||||
if (hResult != LIB3MF_OK) {
|
||||
DWORD nErrorMessage;
|
||||
LPCSTR pszErrorMessage;
|
||||
std::cerr << "could not create property handler: " << std::hex << hResult << std::endl;
|
||||
NMR::lib3mf_getlasterror(pMeshObject, &nErrorMessage, &pszErrorMessage);
|
||||
std::cerr << "error #" << std::hex << nErrorMessage << ": " << pszErrorMessage << std::endl;
|
||||
NMR::lib3mf_release(pMeshObject);
|
||||
return -1;
|
||||
}
|
||||
|
||||
points.resize(points.size()-3);//ignore dummy_vertices
|
||||
for(DWORD vid = 0; vid < points.size(); ++vid)
|
||||
{
|
||||
NMR::MODELMESHVERTEX pVertex;
|
||||
NMR::lib3mf_meshobject_getvertex(pMeshObject, vid+3, &pVertex);
|
||||
points[vid] =
|
||||
Point_3(pVertex.m_fPosition[0],
|
||||
pVertex.m_fPosition[1],
|
||||
pVertex.m_fPosition[2]);
|
||||
}
|
||||
|
||||
NMR::MODELMESH_TRIANGLECOLOR_SRGB pColor;
|
||||
NMR::lib3mf_propertyhandler_getcolor(pPropertyHandler, 0, &pColor);//get color of the dummy triangle
|
||||
NMR::MODELMESHCOLOR_SRGB mColor = pColor.m_Colors[0];
|
||||
colors[0]=CGAL::Color(mColor.m_Red, mColor.m_Green,
|
||||
mColor.m_Blue, mColor.m_Alpha);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template<typename PointRanges, typename PolygonRanges, typename ColorRanges,
|
||||
typename PointRange, typename PolygonRange, typename ColorRange>
|
||||
|
|
@ -607,43 +456,6 @@ int read_triangle_soups_from_3mf(const std::string& file_name, PointRanges& all_
|
|||
all_colors, names, extract_soups<PointRange, PolygonRange, ColorRange>);
|
||||
}
|
||||
|
||||
|
||||
template<typename PointRanges, typename ColorRanges>
|
||||
int read_polylines_from_3mf(const std::string& file_name,
|
||||
PointRanges& all_points,
|
||||
ColorRanges& all_colors,
|
||||
std::vector<std::string>& names
|
||||
)
|
||||
{
|
||||
typedef typename PointRanges::value_type PointRange;
|
||||
typedef std::vector<std::size_t> Polygon;
|
||||
typedef std::vector<Polygon> PolygonRange;
|
||||
typedef std::vector<CGAL::Color> ColorRange;
|
||||
std::vector<PolygonRange> all_polygons;
|
||||
return read_from_3mf<PointRanges,std::vector<PolygonRange>,
|
||||
std::vector<ColorRange>, PointRange, PolygonRange, ColorRange>
|
||||
(file_name, all_points, all_polygons, all_colors, names,
|
||||
extract_polylines<PointRange, PolygonRange, ColorRange>);
|
||||
}
|
||||
|
||||
|
||||
template<typename PointRanges, typename ColorRanges>
|
||||
int read_point_clouds_from_3mf(const std::string& file_name,
|
||||
PointRanges& all_points,
|
||||
ColorRanges& all_colors,
|
||||
std::vector<std::string>& names
|
||||
)
|
||||
{
|
||||
typedef typename PointRanges::value_type PointRange;
|
||||
typedef std::vector<std::size_t> Polygon;
|
||||
typedef std::vector<Polygon> PolygonRange;
|
||||
typedef std::vector<CGAL::Color> ColorRange;
|
||||
std::vector<PolygonRange> all_polygons;
|
||||
return read_from_3mf<PointRanges,std::vector<PolygonRange>,
|
||||
std::vector<ColorRange>, PointRange, PolygonRange, ColorRange>
|
||||
(file_name, all_points, all_polygons, all_colors, names,
|
||||
extract_point_clouds<PointRange, PolygonRange, ColorRange>);
|
||||
}
|
||||
}//end CGAL
|
||||
|
||||
#endif // CGAL_IO_READ_3MF_H
|
||||
|
|
|
|||
|
|
@ -51,33 +51,6 @@ int main(int argc, char** argv)
|
|||
ofs << mesh;
|
||||
ofs.close();
|
||||
}
|
||||
int nb_polylines =
|
||||
CGAL::read_polylines_from_3mf(file_name, all_points, all_colors, names);
|
||||
|
||||
if(nb_polylines == 0)
|
||||
std::cout<<"No polyline found."<<std::endl;
|
||||
else
|
||||
{
|
||||
std::cout<<nb_polylines<<" polylines found, of ";
|
||||
for(int i = 0; i< nb_polylines-1; ++i){
|
||||
std::cout<<all_points[i].size()<<", ";
|
||||
}
|
||||
std::cout<<all_points.back().size()<<" points."<<std::endl;
|
||||
}
|
||||
all_points.clear();
|
||||
all_colors.clear();
|
||||
int nb_point_sets =
|
||||
CGAL::read_point_clouds_from_3mf(file_name, all_points, all_colors, names);
|
||||
if(nb_point_sets == 0)
|
||||
std::cout<<"No point cloud found."<<std::endl;
|
||||
else
|
||||
{
|
||||
std::cout<<nb_point_sets<<" point clouds found, of ";
|
||||
for(int i = 0; i< nb_point_sets-1; ++i){
|
||||
std::cout<<all_points[i].size()<<", ";
|
||||
}
|
||||
std::cout<<all_points.back().size()<<" points."<<std::endl;
|
||||
}
|
||||
|
||||
// testing writing functions
|
||||
Mesh sphere, tube;
|
||||
|
|
@ -147,31 +120,6 @@ int main(int argc, char** argv)
|
|||
meshes[1] = tube;
|
||||
CGAL::write_triangle_meshes_to_3mf("meshes.3mf", meshes, names);
|
||||
|
||||
|
||||
//testing of point clouds
|
||||
|
||||
HRESULT hResult;
|
||||
NMR::PLib3MFModel * pModel;
|
||||
hResult = NMR::lib3mf_createmodel(&pModel);
|
||||
NMR::PLib3MFModelMeshObject* pMeshObject;
|
||||
if (hResult != LIB3MF_OK) {
|
||||
std::cout << "could not create model: " << std::hex << hResult << std::endl;
|
||||
return 1;
|
||||
}
|
||||
for(std::size_t i=0; i< names.size(); ++i)
|
||||
{
|
||||
CGAL::write_mesh_to_model(all_points[i], all_polygons[i],
|
||||
all_colors[i], names[i], &pMeshObject, pModel);
|
||||
}
|
||||
CGAL::Color color(255,0,0);
|
||||
CGAL::write_point_cloud_to_model(all_points.front(),
|
||||
color, names.front(), &pMeshObject, pModel);
|
||||
CGAL::export_model_to_file("micro.3mf", pModel);
|
||||
//testing of polylines
|
||||
CGAL::write_polyline_to_model(all_points.back(),
|
||||
color, names.back(), &pMeshObject, pModel);
|
||||
CGAL::export_model_to_file("micro.3mf", pModel);
|
||||
|
||||
std::cout<<"OK."<<std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue