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

View File

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

View File

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

View File

@ -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>&amp;Save</string>
</property>
</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>
<resources>
<include location="Polyhedron_3.qrc"/>

View File

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

View File

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

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")
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 {

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")
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 {

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")
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;
};

View File

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

View File

@ -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));
}

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")
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;
}

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")
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);

View File

@ -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"; }

View File

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

View File

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

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

View File

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

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);
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

View File

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