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:
Sebastien Loriot 2020-03-16 16:35:45 +01:00 committed by GitHub
commit 0c8e9cb0c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1306 additions and 414 deletions

View File

@ -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()

View File

@ -64,7 +64,7 @@ include(${CGAL_USE_FILE})
find_package(Qt5 find_package(Qt5
QUIET QUIET
COMPONENTS OpenGL Script COMPONENTS OpenGL Script
OPTIONAL_COMPONENTS ScriptTools) OPTIONAL_COMPONENTS ScriptTools WebSockets)
if(Qt5_FOUND) if(Qt5_FOUND)
@ -95,6 +95,11 @@ if( POLYHEDRON_DEMO_ACTIVATE_CONCURRENCY )
endif() endif()
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) # Activate concurrency ? (turned OFF by default)
option(CGAL_ACTIVATE_CONCURRENT_MESH_3 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( statisticsUI_FILES Statistics_on_item_dialog.ui)
qt5_wrap_ui( FileLoaderDialogUI_files FileLoaderDialog.ui ) qt5_wrap_ui( FileLoaderDialogUI_files FileLoaderDialog.ui )
qt5_wrap_ui( Show_point_dialogUI_FILES Show_point_dialog.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( Show_point_dialogUI_FILES Show_point_dialog.ui )
qt5_wrap_ui( ViewerUI_FILES LightingDialog.ui) qt5_wrap_ui( ViewerUI_FILES LightingDialog.ui)
qt5_generate_moc( "File_loader_dialog.h" "${CMAKE_CURRENT_BINARY_DIR}/File_loader_dialog_moc.cpp" ) 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 target_link_libraries(demo_framework
PUBLIC Qt5::OpenGL Qt5::Widgets Qt5::Gui Qt5::Script 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) cgal_add_compilation_test(demo_framework)
# Let's define `three_EXPORT` during the compilation of `demo_framework`, # Let's define `three_EXPORT` during the compilation of `demo_framework`,
# in addition of `demo_framework_EXPORT` (defined automatically by # in addition of `demo_framework_EXPORT` (defined automatically by
@ -329,11 +339,20 @@ if(CGAL_Qt5_FOUND AND Qt5_FOUND)
MainWindow.cpp MainWindow.cpp
Polyhedron_demo.cpp Polyhedron_demo.cpp
File_loader_dialog_moc.cpp File_loader_dialog_moc.cpp
Use_ssh.cpp
${CGAL_Qt5_RESOURCE_FILES} ${CGAL_Qt5_MOC_FILES} ${CGAL_Qt5_RESOURCE_FILES} ${CGAL_Qt5_MOC_FILES}
${FileLoaderDialogUI_files} ${MainWindowUI_files} ${PreferencesUI_FILES} ${FileLoaderDialogUI_files} ${MainWindowUI_files} ${PreferencesUI_FILES}
${statisticsUI_FILES} ${SubViewerUI_files}) ${statisticsUI_FILES} ${SubViewerUI_files})
target_link_libraries(polyhedron_demo PUBLIC target_link_libraries(polyhedron_demo PUBLIC
demo_framework point_dialog Qt5::Gui Qt5::OpenGL Qt5::Widgets Qt5::Script) 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 ) add_executable ( Polyhedron_3 Polyhedron_3.cpp )
target_link_libraries( Polyhedron_3 PRIVATE polyhedron_demo ) target_link_libraries( Polyhedron_3 PRIVATE polyhedron_demo )
add_to_cached_list( CGAL_EXECUTABLE_TARGETS Polyhedron_3 ) add_to_cached_list( CGAL_EXECUTABLE_TARGETS Polyhedron_3 )

View File

@ -1,3 +1,6 @@
#ifdef CGAL_USE_SSH
# include "CGAL/Use_ssh.h"
#endif
#include <cmath> #include <cmath>
#include "config.h" #include "config.h"
@ -43,6 +46,7 @@
#include <QWidgetAction> #include <QWidgetAction>
#include <QJsonArray> #include <QJsonArray>
#include <QSequentialIterable> #include <QSequentialIterable>
#include <QDir>
#ifdef QT_SCRIPT_LIB #ifdef QT_SCRIPT_LIB
# include <QScriptValue> # include <QScriptValue>
# ifdef QT_SCRIPTTOOLS_LIB # ifdef QT_SCRIPTTOOLS_LIB
@ -59,6 +63,7 @@
#include "ui_Preferences.h" #include "ui_Preferences.h"
#include "ui_Details.h" #include "ui_Details.h"
#include "ui_Statistics_on_item_dialog.h" #include "ui_Statistics_on_item_dialog.h"
#include "ui_SSH_dialog.h"
#include "Show_point_dialog.h" #include "Show_point_dialog.h"
#include "File_loader_dialog.h" #include "File_loader_dialog.h"
#include "Viewer.h" #include "Viewer.h"
@ -70,6 +75,14 @@
# include <QScriptEngine> # include <QScriptEngine>
# include <QScriptValue> # include <QScriptValue>
#include "Color_map.h" #include "Color_map.h"
#ifdef CGAL_USE_WEBSOCKETS
#include <QWebSocketServer>
#include <QWebSocket>
#include <QNetworkInterface>
#endif
using namespace CGAL::Three; using namespace CGAL::Three;
QScriptValue QScriptValue
myScene_itemToScriptValue(QScriptEngine *engine, 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 // remove the Load Script menu entry, when the demo has not been compiled with QT_SCRIPT_LIB
#if !defined(QT_SCRIPT_LIB) #if !defined(QT_SCRIPT_LIB)
ui->menuBar->removeAction(ui->actionLoadScript); ui->menuBar->removeAction(ui->actionLoadScript);
ui->menuBar->removeAction(ui->on_actionLoad_a_Scene_from_a_Script_File);
#endif #endif
// Save some pointers from ui, for latter use. // Save some pointers from ui, for latter use.
sceneView = ui->sceneView; sceneView = ui->sceneView;
@ -1008,6 +1022,7 @@ void MainWindow::computeViewerBBox(CGAL::qglviewer::Vec& min, CGAL::qglviewer::V
max= CGAL::qglviewer::Vec(xmax, ymax, zmax); max= CGAL::qglviewer::Vec(xmax, ymax, zmax);
CGAL::qglviewer::Vec bbox_center((xmin+xmax)/2, (ymin+ymax)/2, (zmin+zmax)/2); CGAL::qglviewer::Vec bbox_center((xmin+xmax)/2, (ymin+ymax)/2, (zmin+zmax)/2);
double bbox_diag = CGAL::approximate_sqrt( double bbox_diag = CGAL::approximate_sqrt(
CGAL::square(xmax - xmin) CGAL::square(xmax - xmin)
+ CGAL::square(ymax - ymin) + CGAL::square(ymax - ymin)
@ -1832,6 +1847,7 @@ void MainWindow::readSettings()
this->default_point_size = settings.value("points_size").toInt(); this->default_point_size = settings.value("points_size").toInt();
this->default_normal_length = settings.value("normals_length").toInt(); this->default_normal_length = settings.value("normals_length").toInt();
this->default_lines_width = settings.value("lines_width").toInt(); this->default_lines_width = settings.value("lines_width").toInt();
setProperty("ws_url", settings.value("ws_server_url").toString());
} }
void MainWindow::writeSettings() void MainWindow::writeSettings()
@ -1906,14 +1922,7 @@ void MainWindow::throw_exception() {
void MainWindow::on_actionLoadScript_triggered() void MainWindow::on_actionLoadScript_triggered()
{ {
#if defined(QT_SCRIPT_LIB) #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 #endif
} }
@ -2216,7 +2225,6 @@ void MainWindow::on_actionPreferences_triggered()
QDialog dialog(this); QDialog dialog(this);
Ui::PreferencesDialog prefdiag; Ui::PreferencesDialog prefdiag;
prefdiag.setupUi(&dialog); prefdiag.setupUi(&dialog);
float lineWidth[2]; float lineWidth[2];
if(!viewer->isOpenGL_4_3()) if(!viewer->isOpenGL_4_3())
viewer->glGetFloatv(GL_LINE_WIDTH_RANGE, lineWidth); viewer->glGetFloatv(GL_LINE_WIDTH_RANGE, lineWidth);
@ -2360,6 +2368,70 @@ void MainWindow::on_actionPreferences_triggered()
}); });
dialog.exec(); 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(); dialog.exec();
if ( dialog.result() ) if ( dialog.result() )
@ -2801,52 +2873,182 @@ 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() void MainWindow::on_actionSa_ve_Scene_as_Script_triggered()
{ {
QString filename = if(scene->numberOfEntries() == 0)
QFileDialog::getSaveFileName(this, 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", "Save the Scene as a Script File",
last_saved_dir, last_saved_dir,
"Qt Script files (*.js)"); "Qt Script files (*.js)");
std::ofstream os(filename.toUtf8()); }
std::ofstream os(filename.toUtf8(), std::ofstream::binary);
if(!os) if(!os)
return; return;
std::vector<QString> names; CGAL::Three::Three::CursorScopeGuard cs(Qt::WaitCursor);
std::vector<QString> loaders; std::vector<std::pair<QString, QString> > names;
std::vector<std::pair<QString, QString> > loaders;
std::vector<QColor> colors; std::vector<QColor> colors;
std::vector<int> rendering_modes; std::vector<int> rendering_modes;
QStringList not_saved; QStringList not_saved;
for(int i = 0; i < scene->numberOfEntries(); ++i) for(int i = 0; i < scene->numberOfEntries(); ++i)
{ {
Scene_item* item = scene->item(i); Scene_item* item = scene->item(i);
QString loader = item->property("loader_name").toString(); QString loader;// = item->property("loader_name").toString();
QString source = item->property("source filename").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()) if(loader.isEmpty())
{ {
not_saved.push_back(item->name()); QMessageBox::warning(this, "", tr("No plugin found for %1. Not saved.").arg(item->name()));
continue; continue;
} }
names.push_back(source); loaders.push_back(std::make_pair(loader, ext));
loaders.push_back(loader);
colors.push_back(item->color()); colors.push_back(item->color());
rendering_modes.push_back(item->renderingMode()); rendering_modes.push_back(item->renderingMode());
} }
if(loaders.empty())
return;
//path //path
os << "var camera = \""<<viewer->dumpCameraCoordinates().toStdString()<<"\";\n"; os << "var camera = \""<<viewer->dumpCameraCoordinates().toStdString()<<"\";\n";
os << "var items = ["; os << "var items = [";
for(std::size_t i = 0; i< names.size() -1; ++i) for(std::size_t i = 0; i< names.size() -1; ++i)
{ {
os << "\'" << names[i].toStdString() << "\', "; QString fullpath = make_fullpath(names[i].first);
}
os<<"\'"<<names.back().toStdString()<<"\'];\n";
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();
}
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 //plugin
os << "var loaders = ["; os << "var loaders = [";
for(std::size_t i = 0; i< names.size() -1; ++i) 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 //color
os << "var colors = ["; os << "var colors = [";
@ -2863,10 +3065,13 @@ void MainWindow::on_actionSa_ve_Scene_as_Script_triggered()
os << rendering_modes[i] << ", "; os << rendering_modes[i] << ", ";
} }
os << rendering_modes.back()<<"];\n"; os << rendering_modes.back()<<"];\n";
os <<"var initial_scene_size = scene.numberOfEntries;\n";
os << "items.forEach(function(item, index, array){\n"; os << "items.forEach(function(item, index, array){\n";
os << " main_window.open(item, loaders[index]);\n"; os<<" var path=items[index][1];\n";
os << " var it = scene.item(initial_scene_size+index);\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 r = colors[index][0];\n";
os << " var g = colors[index][1];\n"; os << " var g = colors[index][1];\n";
os << " var b = colors[index][2];\n"; os << " var b = colors[index][2];\n";
@ -2880,6 +3085,60 @@ void MainWindow::on_actionSa_ve_Scene_as_Script_triggered()
"Items Not Saved", "Items Not Saved",
QString("The following items could not be saved: %1").arg( QString("The following items could not be saved: %1").arg(
not_saved.join(", "))); 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) void MainWindow::setTransparencyPasses(int val)
{ {
@ -3003,6 +3262,33 @@ void MainWindow::setupViewer(Viewer* viewer, SubViewer* subviewer)
information(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
} }
void MainWindow::on_actionAdd_Viewer_triggered() void MainWindow::on_actionAdd_Viewer_triggered()
@ -3154,11 +3440,20 @@ SubViewer::SubViewer(QWidget *parent, MainWindow* mw, Viewer* mainviewer)
QAction* actionTotalPass = new QAction("Set Transparency Pass &Number...",this); QAction* actionTotalPass = new QAction("Set Transparency Pass &Number...",this);
actionTotalPass->setObjectName("actionTotalPass"); actionTotalPass->setObjectName("actionTotalPass");
viewMenu->addAction(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); QAction* actionBackFrontShading = new QAction("Activate Back/Front shading.",this);
actionBackFrontShading->setObjectName("actionBackFrontShading"); actionBackFrontShading->setObjectName("actionBackFrontShading");
actionBackFrontShading->setCheckable(true); actionBackFrontShading->setCheckable(true);
actionBackFrontShading->setChecked(false); actionBackFrontShading->setChecked(false);
viewMenu->addAction(actionBackFrontShading); viewMenu->addAction(actionBackFrontShading);
if(mainviewer) if(mainviewer)
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
setWindowIcon(QIcon(":/cgal/icons/resources/menu.png")); setWindowIcon(QIcon(":/cgal/icons/resources/menu.png"));
@ -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

View File

@ -138,6 +138,8 @@ public Q_SLOTS:
This slot is for use by scripts.*/ This slot is for use by scripts.*/
bool open(QString filename, QString loader_name); 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 /*! 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. index of the item to be reloaded as data attached to the action.
The index must identify a valid `Scene_item`.*/ 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); void save(QString filename, QList<CGAL::Three::Scene_item*>& to_save);
//!Calls the function saveSnapShot of the viewer. //!Calls the function saveSnapShot of the viewer.
void on_actionSaveSnapshot_triggered(); 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. //!Opens a Dialog to choose a color and make it the background color.
void setBackgroundColor(); void setBackgroundColor();
//!Opens a Dialog to change the lighting settings //!Opens a Dialog to change the lighting settings
@ -447,6 +453,7 @@ public:
#endif #endif
public Q_SLOTS: public Q_SLOTS:
void on_actionSa_ve_Scene_as_Script_triggered(); void on_actionSa_ve_Scene_as_Script_triggered();
void on_actionLoad_a_Scene_from_a_Script_File_triggered();
void toggleFullScreen(); void toggleFullScreen();
void setDefaultSaveDir(); void setDefaultSaveDir();
void invalidate_bbox(bool do_recenter); void invalidate_bbox(bool do_recenter);
@ -494,4 +501,30 @@ protected:
private: private:
bool is_main; 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 #endif // ifndef MAINWINDOW_H

View File

@ -58,6 +58,7 @@
<addaction name="action_Save"/> <addaction name="action_Save"/>
<addaction name="actionSaveAs"/> <addaction name="actionSaveAs"/>
<addaction name="actionSa_ve_Scene_as_Script"/> <addaction name="actionSa_ve_Scene_as_Script"/>
<addaction name="actionLoad_a_Scene_from_a_Script_File"/>
<addaction name="actionSaveSnapshot"/> <addaction name="actionSaveSnapshot"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionLoadScript"/> <addaction name="actionLoadScript"/>
@ -96,6 +97,7 @@
<addaction name="action_Rearrange_Viewers"/> <addaction name="action_Rearrange_Viewers"/>
<addaction name="menuDockWindows"/> <addaction name="menuDockWindows"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="action_Start_a_Session"/>
</widget> </widget>
<addaction name="menuFile"/> <addaction name="menuFile"/>
<addaction name="menuEdit"/> <addaction name="menuEdit"/>
@ -463,6 +465,22 @@
<string>&amp;Save</string> <string>&amp;Save</string>
</property> </property>
</action> </action>
<action name="actionLoad_a_Scene_from_a_Script_File">
<property name="text">
<string>Load a Scene &amp;from a Script File...</string>
</property>
</action>
<action name="action_Start_a_Session">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;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> </widget>
<resources> <resources>
<include location="Polyhedron_3.qrc"/> <include location="Polyhedron_3.qrc"/>

View File

@ -860,6 +860,12 @@ public:
return ok; 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; using Polyhedron_demo_io_plugin_interface::init;
void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface,
Messages_interface* m) override; Messages_interface* m) override;

View File

@ -78,61 +78,6 @@ class Io_3mf_plugin:
std::vector<std::string> names; std::vector<std::string> names;
QList<Scene_item*> result; QList<Scene_item*> result;
std::vector<std::vector<CGAL::Color> > all_colors; 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 = int nb_meshes =
CGAL::read_triangle_soups_from_3mf(fileinfo.filePath().toUtf8().toStdString(), CGAL::read_triangle_soups_from_3mf(fileinfo.filePath().toUtf8().toStdString(),
all_points, all_polygons, all_colors, names); all_points, all_polygons, all_colors, names);

View File

@ -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") Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0" FILE "nef_io_plugin.json")
public: public:
QString nameFilters() const; QString nameFilters() const override;
QString name() const { return "io_nef_plugin"; } QString name() const override { return "io_nef_plugin"; }
bool canLoad(QFileInfo) const; bool canLoad(QFileInfo) const override;
QList<Scene_item*> load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true); QList<Scene_item*> load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true) override;
bool canSave(const CGAL::Three::Scene_item*); bool canSave(const CGAL::Three::Scene_item*) override;
bool save(QFileInfo fileinfo,QList<CGAL::Three::Scene_item*>& items); 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 { QString Polyhedron_demo_io_nef_plugin::nameFilters() const {

View File

@ -29,28 +29,28 @@ class Polyhedron_demo_off_plugin :
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.IOPluginInterface/1.90" FILE "off_io_plugin.json") Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.IOPluginInterface/1.90" FILE "off_io_plugin.json")
public: 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) if(qobject_cast<const Scene_surface_mesh_item*>(item)
|| qobject_cast<const Scene_polygon_soup_item*>(item)) || qobject_cast<const Scene_polygon_soup_item*>(item))
return true; return true;
return false; return false;
} }
bool isDefaultLoader(const QString& name) const bool isDefaultLoader(const QString& name) const override
{ {
if(name == QString("off")) if(name == QString("off"))
return true; return true;
return false; return false;
} }
QString name() const { return "off_plugin"; } QString name() const override{ return "off_plugin"; }
QString nameFilters() const { return "OFF files (*.off);;Wavefront OBJ (*.obj)"; } QString nameFilters() const override { return "OFF files (*.off);;Wavefront OBJ (*.obj)"; }
bool canLoad(QFileInfo fileinfo) const; bool canLoad(QFileInfo fileinfo) const override;
QList<Scene_item*> load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true); 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_off(QFileInfo fileinfo);
CGAL::Three::Scene_item* load_obj(QFileInfo fileinfo); CGAL::Three::Scene_item* load_obj(QFileInfo fileinfo);
bool canSave(const CGAL::Three::Scene_item*); bool canSave(const CGAL::Three::Scene_item*) override;
bool save(QFileInfo fileinfo,QList<CGAL::Three::Scene_item*>& ); bool save(QFileInfo fileinfo,QList<CGAL::Three::Scene_item*>& ) override;
}; };
bool Polyhedron_demo_off_plugin::canLoad(QFileInfo) const { bool Polyhedron_demo_off_plugin::canLoad(QFileInfo) const {

View File

@ -23,19 +23,19 @@ class Polyhedron_demo_ply_plugin :
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.IOPluginInterface/1.90" FILE "ply_io_plugin.json") Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.IOPluginInterface/1.90" FILE "ply_io_plugin.json")
public: 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)) if(qobject_cast<const Scene_points_with_normal_item*>(item))
return true; return true;
return false; return false;
} }
QString name() const { return "ply_plugin"; } QString name() const override{ return "ply_plugin"; }
QString nameFilters() const { return "PLY files (*.ply)"; } QString nameFilters() const override{ return "PLY files (*.ply)"; }
bool canLoad(QFileInfo fileinfo) const; bool canLoad(QFileInfo fileinfo) const override;
QList<Scene_item*> load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true); QList<Scene_item*> load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true)override;
bool canSave(const CGAL::Three::Scene_item*); bool canSave(const CGAL::Three::Scene_item*)override;
bool save(QFileInfo fileinfo,QList<CGAL::Three::Scene_item*>&); bool save(QFileInfo fileinfo,QList<CGAL::Three::Scene_item*>&)override;
}; };

View File

@ -75,6 +75,12 @@ public:
return QList<QAction*>()<<actionSplit_polylines return QList<QAction*>()<<actionSplit_polylines
<<actionJoin_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: protected Q_SLOTS:
//!Splits the selected Scene_polylines_item in multiple items all containing a single polyline. //!Splits the selected Scene_polylines_item in multiple items all containing a single polyline.
void split(); void split();

View File

@ -290,10 +290,11 @@ public:
QString nameFilters() const { 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"; } QString name() const { return "vtk_plugin"; }
bool canSave(const CGAL::Three::Scene_item* item) bool canSave(const CGAL::Three::Scene_item* item)
{ {
return (qobject_cast<const Scene_facegraph_item*>(item) return (qobject_cast<const Scene_facegraph_item*>(item)
|| qobject_cast<const Scene_c3t3_item*>(item)); || qobject_cast<const Scene_c3t3_item*>(item));
} }

View File

@ -17,24 +17,24 @@ class LCC_io_plugin :
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.IOPluginInterface/1.90" FILE "lcc_io_plugin.json") Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.IOPluginInterface/1.90" FILE "lcc_io_plugin.json")
public: 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)) if(qobject_cast<const Scene_lcc_item*>(item))
return true; return true;
return false; return false;
} }
QString name() const { return "lcc_plugin"; } QString name() const override{ return "lcc_plugin"; }
QString nameFilters() const { return QString nameFilters() const override{ return
"OFF files (*.off);;" "OFF files (*.off);;"
"3-map files (*.3map)"; } "3-map files (*.3map)"; }
QString saveNameFilters() const { QString saveNameFilters() const override{
return return
"3-map files (*.3map)"; "3-map files (*.3map)";
} }
bool canLoad(QFileInfo) const { return true; } bool canLoad(QFileInfo) const override{ return true; }
QList<CGAL::Three::Scene_item*> load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true){ QList<CGAL::Three::Scene_item*> load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true) override{
// Open file // Open file
std::ifstream ifs(fileinfo.filePath().toUtf8()); std::ifstream ifs(fileinfo.filePath().toUtf8());
if(!ifs) { if(!ifs) {
@ -67,8 +67,8 @@ public:
} }
bool canSave(const CGAL::Three::Scene_item*){return false;} bool canSave(const CGAL::Three::Scene_item*)override{return false;}
bool save(QFileInfo, QList<CGAL::Three::Scene_item*>& ){ bool save(QFileInfo, QList<CGAL::Three::Scene_item*>& )override{
return false; return false;
} }

View File

@ -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") Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.IOPluginInterface/1.90" FILE "c3t3_io_plugin.json")
public: public:
QString name() const { return "C3t3_io_plugin"; } QString name() const override { return "C3t3_io_plugin"; }
QString nameFilters() const { return "binary files (*.cgal);;ascii (*.mesh);;maya (*.ma)"; } QString nameFilters() const override { 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 saveNameFilters() const override { return "binary files (*.cgal);;ascii (*.mesh);;maya (*.ma);;avizo (*.am);;OFF files (*.off)"; }
QString loadNameFilters() const { return "binary files (*.cgal);;ascii (*.mesh)"; } QString loadNameFilters() const override { return "binary files (*.cgal);;ascii (*.mesh)"; }
bool canLoad(QFileInfo) const; bool canLoad(QFileInfo) const override;
QList<Scene_item*> load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true); QList<Scene_item*> load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true) override;
bool canSave(const CGAL::Three::Scene_item*); bool canSave(const CGAL::Three::Scene_item*) override;
bool save(QFileInfo fileinfo,QList<CGAL::Three::Scene_item*>& ); 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: private:
bool try_load_other_binary_format(std::istream& in, C3t3& c3t3); bool try_load_other_binary_format(std::istream& in, C3t3& c3t3);

View File

@ -296,6 +296,11 @@ public:
items.pop_front(); items.pop_front();
return ok; 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"; } QString name() const override{ return "segmented images"; }

View File

@ -127,6 +127,12 @@ public:
return res; 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 { bool applicable(QAction* action) const override {
if(action == actionSelfIntersection) if(action == actionSelfIntersection)
return qobject_cast<Scene_face_graph_item*>(scene->item(scene->mainSelectionIndex())); return qobject_cast<Scene_face_graph_item*>(scene->item(scene->mainSelectionIndex()));

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>631</width> <width>631</width>
<height>526</height> <height>628</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -58,9 +58,9 @@
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>-96</y> <y>0</y>
<width>275</width> <width>275</width>
<height>528</height> <height>534</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_3">
@ -285,6 +285,16 @@
</property> </property>
</widget> </widget>
</item> </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> <item>
<widget class="QPushButton" name="backFrontColor_pushButton"> <widget class="QPushButton" name="backFrontColor_pushButton">
<property name="text"> <property name="text">

View File

@ -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>

View File

@ -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

View File

@ -16,6 +16,11 @@
#include <QApplication> #include <QApplication>
#include <QOpenGLDebugLogger> #include <QOpenGLDebugLogger>
#include <QStyleFactory> #include <QStyleFactory>
#include <QAction>
#include <QRegularExpressionMatch>
#ifdef CGAL_USE_WEBSOCKETS
#include <QtWebSockets/QWebSocket>
#endif
#include <CGAL/Three/Three.h> #include <CGAL/Three/Three.h>
@ -40,6 +45,7 @@ public:
bool inDrawWithNames; bool inDrawWithNames;
bool clipping; bool clipping;
bool projection_is_ortho; bool projection_is_ortho;
bool cam_sharing;
GLfloat gl_point_size; GLfloat gl_point_size;
QVector4D clipbox[6]; QVector4D clipbox[6];
QPainter *painter; QPainter *painter;
@ -111,6 +117,12 @@ public:
{ {
return shader_programs; return shader_programs;
} }
#ifdef CGAL_USE_WEBSOCKETS
QWebSocket m_webSocket;
#endif
bool is_connected;
QString session;
QUrl m_url;
}; };
class LightingDialog : class LightingDialog :
@ -268,6 +280,7 @@ void Viewer::doBindings()
d->spec_power = viewer_settings.value("spec_power", 51.8).toFloat(); d->spec_power = viewer_settings.value("spec_power", 51.8).toFloat();
d->scene = 0; d->scene = 0;
d->projection_is_ortho = false; d->projection_is_ortho = false;
d->cam_sharing = false;
d->twosides = false; d->twosides = false;
this->setProperty("draw_two_sides", false); this->setProperty("draw_two_sides", false);
this->setProperty("back_front_shading", false); this->setProperty("back_front_shading", false);
@ -278,6 +291,7 @@ void Viewer::doBindings()
d->shader_programs.resize(NB_OF_PROGRAMS); d->shader_programs.resize(NB_OF_PROGRAMS);
d->textRenderer = new TextRenderer(); d->textRenderer = new TextRenderer();
d->is_2d_selection_mode = false; d->is_2d_selection_mode = false;
d->is_connected = false;
connect( d->textRenderer, SIGNAL(sendMessage(QString,int)), connect( d->textRenderer, SIGNAL(sendMessage(QString,int)),
this, SLOT(printMessage(QString,int)) ); this, SLOT(printMessage(QString,int)) );
@ -1931,6 +1945,79 @@ bool Viewer::isClipping() const
{ {
return d->clipping; 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" #include "Viewer.moc"

View File

@ -95,6 +95,7 @@ public:
Q_SIGNALS: Q_SIGNALS:
void sendMessage(QString); void sendMessage(QString);
void doneInitGL(CGAL::Three::Viewer_interface*); void doneInitGL(CGAL::Three::Viewer_interface*);
void socketClosed();
public Q_SLOTS: public Q_SLOTS:
//! Sets the antialiasing to true or false. //! Sets the antialiasing to true or false.
void setAntiAliasing(bool b) Q_DECL_OVERRIDE; void setAntiAliasing(bool b) Q_DECL_OVERRIDE;
@ -131,6 +132,11 @@ public Q_SLOTS:
void setBackFrontColors(); void setBackFrontColors();
void messageLogged(QOpenGLDebugMessage); void messageLogged(QOpenGLDebugMessage);
#ifdef CGAL_USE_WEBSOCKETS
void setShareCam(bool, QString);
void onSocketConnected();
void onTextMessageSocketReceived(QString message);
#endif
protected: protected:
void paintEvent(QPaintEvent *)Q_DECL_OVERRIDE; void paintEvent(QPaintEvent *)Q_DECL_OVERRIDE;

View File

@ -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

View File

@ -65,12 +65,6 @@ bool extract_soups (NMR::PLib3MFModelMeshObject *pMeshObject,
pBuffer.resize(nNeededChars + 1); pBuffer.resize(nNeededChars + 1);
hResult = NMR::lib3mf_object_getnameutf8(pMeshObject, &pBuffer[0], nNeededChars + 1, NULL); hResult = NMR::lib3mf_object_getnameutf8(pMeshObject, &pBuffer[0], nNeededChars + 1, NULL);
pBuffer[nNeededChars] = 0; 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]); name = std::string(&pBuffer[0]);
} }
else else
@ -120,151 +114,6 @@ bool extract_soups (NMR::PLib3MFModelMeshObject *pMeshObject,
return true; 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, template<typename PointRanges, typename PolygonRanges, typename ColorRanges,
typename PointRange, typename PolygonRange, typename ColorRange> 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>); 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 }//end CGAL
#endif // CGAL_IO_READ_3MF_H #endif // CGAL_IO_READ_3MF_H

View File

@ -51,33 +51,6 @@ int main(int argc, char** argv)
ofs << mesh; ofs << mesh;
ofs.close(); 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 // testing writing functions
Mesh sphere, tube; Mesh sphere, tube;
@ -147,31 +120,6 @@ int main(int argc, char** argv)
meshes[1] = tube; meshes[1] = tube;
CGAL::write_triangle_meshes_to_3mf("meshes.3mf", meshes, names); 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; std::cout<<"OK."<<std::endl;
return 0; return 0;
} }