| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | #include <Inventor/events/SoMouseButtonEvent.h>
|
| | #include <QInputDialog>
|
| | #include <QMessageBox>
|
| | #include <algorithm>
|
| |
|
| |
|
| | #include <App/Application.h>
|
| | #include <App/Document.h>
|
| | #include <Base/Exception.h>
|
| | #include <Base/Interpreter.h>
|
| | #include <Base/Tools.h>
|
| | #include <Base/UnitsApi.h>
|
| | #include <Gui/Application.h>
|
| | #include <Gui/Command.h>
|
| | #include <Gui/Document.h>
|
| | #include <Gui/FileDialog.h>
|
| | #include <Gui/MainWindow.h>
|
| | #include <Gui/Selection/Selection.h>
|
| | #include <Gui/View3DInventor.h>
|
| | #include <Gui/View3DInventorViewer.h>
|
| | #include <Gui/ViewProviderDocumentObject.h>
|
| | #include <Gui/WaitCursor.h>
|
| |
|
| | #include "../App/PointsFeature.h"
|
| | #include "../App/Properties.h"
|
| | #include "../App/Structured.h"
|
| | #include "../App/Tools.h"
|
| |
|
| | #include "DlgPointsReadImp.h"
|
| | #include "ViewProvider.h"
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | DEF_STD_CMD_A(CmdPointsImport)
|
| |
|
| | CmdPointsImport::CmdPointsImport()
|
| | : Command("Points_Import")
|
| | {
|
| | sAppModule = "Points";
|
| | sGroup = QT_TR_NOOP("Points");
|
| | sMenuText = QT_TR_NOOP("Import Points…");
|
| | sToolTipText = QT_TR_NOOP("Imports a point cloud");
|
| | sWhatsThis = "Points_Import";
|
| | sStatusTip = sToolTipText;
|
| | sPixmap = "Points_Import_Point_cloud";
|
| | }
|
| |
|
| | void CmdPointsImport::activated(int iMsg)
|
| | {
|
| | Q_UNUSED(iMsg);
|
| |
|
| | QString fn = Gui::FileDialog::getOpenFileName(
|
| | Gui::getMainWindow(),
|
| | QString(),
|
| | QString(),
|
| | QStringLiteral("%1 (*.asc *.pcd *.ply *.e57);;%2 (*.*)")
|
| | .arg(QObject::tr("Point formats"), QObject::tr("All Files"))
|
| | );
|
| | if (fn.isEmpty()) {
|
| | return;
|
| | }
|
| |
|
| | if (!fn.isEmpty()) {
|
| | fn = Base::Tools::escapeEncodeFilename(fn);
|
| | App::Document* doc = getActiveDocument();
|
| | openCommand(QT_TRANSLATE_NOOP("Command", "Import points"));
|
| | addModule(Command::App, "Points");
|
| | doCommand(Command::Doc, "Points.insert(\"%s\", \"%s\")", fn.toUtf8().data(), doc->getName());
|
| | commitCommand();
|
| |
|
| | updateActive();
|
| |
|
| | |
| | |
| | |
| | |
| |
|
| | if (auto pcFtr = dynamic_cast<Points::Feature*>(doc->getActiveObject())) {
|
| | auto points = pcFtr->Points.getValue();
|
| | auto bbox = points.getBoundBox();
|
| | auto center = bbox.GetCenter();
|
| |
|
| | if (!bbox.IsInBox(Base::Vector3d(0, 0, 0))) {
|
| | QMessageBox msgBox(Gui::getMainWindow());
|
| | msgBox.setIcon(QMessageBox::Question);
|
| | msgBox.setWindowTitle(QObject::tr("Points not at Origin"));
|
| | msgBox.setText(
|
| | QObject::tr(
|
| | "The bounding box of the imported points does not contain the origin. "
|
| | "Translate it to the origin?"
|
| | )
|
| | );
|
| | msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
| | msgBox.setDefaultButton(QMessageBox::Yes);
|
| | auto ret = msgBox.exec();
|
| |
|
| | if (ret == QMessageBox::Yes) {
|
| | Points::PointKernel* kernel = pcFtr->Points.startEditing();
|
| | kernel->moveGeometry(-center);
|
| | pcFtr->Points.finishEditing();
|
| | }
|
| | }
|
| | }
|
| | }
|
| | }
|
| |
|
| | bool CmdPointsImport::isActive()
|
| | {
|
| | if (getActiveGuiDocument()) {
|
| | return true;
|
| | }
|
| | else {
|
| | return false;
|
| | }
|
| | }
|
| |
|
| | DEF_STD_CMD_A(CmdPointsExport)
|
| |
|
| | CmdPointsExport::CmdPointsExport()
|
| | : Command("Points_Export")
|
| | {
|
| | sAppModule = "Points";
|
| | sGroup = QT_TR_NOOP("Points");
|
| | sMenuText = QT_TR_NOOP("Export Points…");
|
| | sToolTipText = QT_TR_NOOP("Exports a point cloud");
|
| | sWhatsThis = "Points_Export";
|
| | sStatusTip = QT_TR_NOOP("Exports a point cloud");
|
| | sPixmap = "Points_Export_Point_cloud";
|
| | }
|
| |
|
| | void CmdPointsExport::activated(int iMsg)
|
| | {
|
| | Q_UNUSED(iMsg);
|
| |
|
| | addModule(Command::App, "Points");
|
| | std::vector<App::DocumentObject*> points = getSelection().getObjectsOfType(
|
| | Points::Feature::getClassTypeId()
|
| | );
|
| | for (auto point : points) {
|
| | QString fn = Gui::FileDialog::getSaveFileName(
|
| | Gui::getMainWindow(),
|
| | QString(),
|
| | QString(),
|
| | QStringLiteral("%1 (*.asc *.pcd *.ply);;%2 (*.*)")
|
| | .arg(QObject::tr("Point formats"), QObject::tr("All Files"))
|
| | );
|
| | if (fn.isEmpty()) {
|
| | break;
|
| | }
|
| |
|
| | if (!fn.isEmpty()) {
|
| | fn = Base::Tools::escapeEncodeFilename(fn);
|
| | doCommand(
|
| | Command::Doc,
|
| | "Points.export([App.ActiveDocument.%s], \"%s\")",
|
| | point->getNameInDocument(),
|
| | fn.toUtf8().data()
|
| | );
|
| | }
|
| | }
|
| | }
|
| |
|
| | bool CmdPointsExport::isActive()
|
| | {
|
| | return getSelection().countObjectsOfType<Points::Feature>() > 0;
|
| | }
|
| |
|
| | DEF_STD_CMD_A(CmdPointsConvert)
|
| |
|
| | CmdPointsConvert::CmdPointsConvert()
|
| | : Command("Points_Convert")
|
| | {
|
| | sAppModule = "Points";
|
| | sGroup = QT_TR_NOOP("Points");
|
| | sMenuText = QT_TR_NOOP("Convert to Points");
|
| | sToolTipText = QT_TR_NOOP("Converts to points");
|
| | sWhatsThis = "Points_Convert";
|
| | sStatusTip = sToolTipText;
|
| | sPixmap = "Points_Convert";
|
| | }
|
| |
|
| | void CmdPointsConvert::activated(int iMsg)
|
| | {
|
| | Q_UNUSED(iMsg);
|
| | double STD_OCC_TOLERANCE = 1e-6;
|
| |
|
| | int decimals = Base::UnitsApi::getDecimals();
|
| | double tolerance_from_decimals = pow(10., -decimals);
|
| |
|
| | double minimal_tolerance = tolerance_from_decimals < STD_OCC_TOLERANCE ? STD_OCC_TOLERANCE
|
| | : tolerance_from_decimals;
|
| |
|
| | bool ok;
|
| | double tol = QInputDialog::getDouble(
|
| | Gui::getMainWindow(),
|
| | QObject::tr("Distance"),
|
| | QObject::tr("Enter maximum distance:"),
|
| | 0.1,
|
| | minimal_tolerance,
|
| | 10.0,
|
| | decimals,
|
| | &ok,
|
| | Qt::MSWindowsFixedSizeDialogHint
|
| | );
|
| | if (!ok) {
|
| | return;
|
| | }
|
| |
|
| | Gui::WaitCursor wc;
|
| | openCommand(QT_TRANSLATE_NOOP("Command", "Convert to points"));
|
| | std::vector<App::GeoFeature*> geoObject = getSelection().getObjectsOfType<App::GeoFeature>();
|
| |
|
| | auto run_python = [](const std::vector<App::GeoFeature*>& geoObject, double tol) -> bool {
|
| | Py::List list;
|
| | for (auto it : geoObject) {
|
| | const App::PropertyComplexGeoData* prop = it->getPropertyOfGeometry();
|
| | if (prop) {
|
| | list.append(Py::asObject(it->getPyObject()));
|
| | }
|
| | }
|
| |
|
| | if (list.size() > 0) {
|
| | PyObject* module = PyImport_ImportModule("pointscommands.commands");
|
| | if (!module) {
|
| | throw Py::Exception();
|
| | }
|
| |
|
| | Py::Module commands(module, true);
|
| | commands.callMemberFunction("make_points_from_geometry", Py::TupleN(list, Py::Float(tol)));
|
| | return true;
|
| | }
|
| |
|
| | return false;
|
| | };
|
| |
|
| | Base::PyGILStateLocker lock;
|
| | try {
|
| | if (run_python(geoObject, tol)) {
|
| | commitCommand();
|
| | }
|
| | else {
|
| | abortCommand();
|
| | }
|
| | }
|
| | catch (const Py::Exception&) {
|
| | abortCommand();
|
| | Base::PyException e;
|
| | e.reportException();
|
| | }
|
| | }
|
| |
|
| | bool CmdPointsConvert::isActive()
|
| | {
|
| | return getSelection().countObjectsOfType<App::GeoFeature>() > 0;
|
| | }
|
| |
|
| | DEF_STD_CMD_A(CmdPointsPolyCut)
|
| |
|
| | CmdPointsPolyCut::CmdPointsPolyCut()
|
| | : Command("Points_PolyCut")
|
| | {
|
| | sAppModule = "Points";
|
| | sGroup = QT_TR_NOOP("Points");
|
| | sMenuText = QT_TR_NOOP("Cut Point Cloud");
|
| | sToolTipText = QT_TR_NOOP("Cuts a point cloud with a selected polygon");
|
| | sWhatsThis = "Points_PolyCut";
|
| | sStatusTip = sToolTipText;
|
| | sPixmap = "PolygonPick";
|
| | }
|
| |
|
| | void CmdPointsPolyCut::activated(int iMsg)
|
| | {
|
| | Q_UNUSED(iMsg);
|
| |
|
| | std::vector<App::DocumentObject*> docObj = Gui::Selection().getObjectsOfType(
|
| | Points::Feature::getClassTypeId()
|
| | );
|
| | for (std::vector<App::DocumentObject*>::iterator it = docObj.begin(); it != docObj.end(); ++it) {
|
| | if (it == docObj.begin()) {
|
| | Gui::Document* doc = getActiveGuiDocument();
|
| | Gui::MDIView* view = doc->getActiveView();
|
| | if (view->isDerivedFrom<Gui::View3DInventor>()) {
|
| | Gui::View3DInventorViewer* viewer = ((Gui::View3DInventor*)view)->getViewer();
|
| | viewer->setEditing(true);
|
| | viewer->startSelection(Gui::View3DInventorViewer::Lasso);
|
| | viewer->addEventCallback(
|
| | SoMouseButtonEvent::getClassTypeId(),
|
| | PointsGui::ViewProviderPoints::clipPointsCallback
|
| | );
|
| | }
|
| | else {
|
| | return;
|
| | }
|
| | }
|
| |
|
| | Gui::ViewProvider* pVP = getActiveGuiDocument()->getViewProvider(*it);
|
| | pVP->startEditing(Gui::ViewProvider::Cutting);
|
| | }
|
| | }
|
| |
|
| | bool CmdPointsPolyCut::isActive()
|
| | {
|
| |
|
| | return getSelection().countObjectsOfType<Points::Feature>() > 0;
|
| | }
|
| |
|
| | DEF_STD_CMD_A(CmdPointsMerge)
|
| |
|
| | CmdPointsMerge::CmdPointsMerge()
|
| | : Command("Points_Merge")
|
| | {
|
| | sAppModule = "Points";
|
| | sGroup = QT_TR_NOOP("Points");
|
| | sMenuText = QT_TR_NOOP("Merge Point Clouds");
|
| | sToolTipText = QT_TR_NOOP("Merges several point clouds into one");
|
| | sWhatsThis = "Points_Merge";
|
| | sStatusTip = sToolTipText;
|
| | sPixmap = "Points_Merge";
|
| | }
|
| |
|
| | void CmdPointsMerge::activated(int iMsg)
|
| | {
|
| | Q_UNUSED(iMsg);
|
| |
|
| | App::Document* doc = App::GetApplication().getActiveDocument();
|
| | doc->openTransaction("Merge point clouds");
|
| | Points::Feature* pts = doc->addObject<Points::Feature>("Merged Points");
|
| | Points::PointKernel* kernel = pts->Points.startEditing();
|
| |
|
| | std::vector<App::DocumentObject*> docObj = Gui::Selection().getObjectsOfType(
|
| | Points::Feature::getClassTypeId()
|
| | );
|
| | for (auto it : docObj) {
|
| | const Points::PointKernel& k = static_cast<Points::Feature*>(it)->Points.getValue();
|
| | std::size_t numPts = kernel->size();
|
| | kernel->resize(numPts + k.size());
|
| | for (std::size_t i = 0; i < k.size(); ++i) {
|
| | kernel->setPoint(i + numPts, k.getPoint(i));
|
| | }
|
| | }
|
| |
|
| | pts->Points.finishEditing();
|
| |
|
| |
|
| | std::string displayMode = "Points";
|
| | if (Points::copyProperty<App::PropertyColorList>(pts, docObj, "Color")) {
|
| | displayMode = "Color";
|
| | }
|
| | if (Points::copyProperty<Points::PropertyNormalList>(pts, docObj, "Normal")) {
|
| | displayMode = "Shaded";
|
| | }
|
| | if (Points::copyProperty<Points::PropertyGreyValueList>(pts, docObj, "Intensity")) {
|
| | displayMode = "Intensity";
|
| | }
|
| |
|
| | if (auto vp = dynamic_cast<Gui::ViewProviderDocumentObject*>(
|
| | Gui::Application::Instance->getViewProvider(pts)
|
| | )) {
|
| | vp->DisplayMode.setValue(displayMode.c_str());
|
| | }
|
| |
|
| | doc->commitTransaction();
|
| | updateActive();
|
| | }
|
| |
|
| | bool CmdPointsMerge::isActive()
|
| | {
|
| | return getSelection().countObjectsOfType<Points::Feature>() > 1;
|
| | }
|
| |
|
| | DEF_STD_CMD_A(CmdPointsStructure)
|
| |
|
| | CmdPointsStructure::CmdPointsStructure()
|
| | : Command("Points_Structure")
|
| | {
|
| | sAppModule = "Points";
|
| | sGroup = QT_TR_NOOP("Points");
|
| | sMenuText = QT_TR_NOOP("Structured Point Cloud");
|
| | sToolTipText = QT_TR_NOOP("Converts points to a structured point cloud");
|
| | sWhatsThis = "Points_Structure";
|
| | sStatusTip = sToolTipText;
|
| | sPixmap = "Points_Structure";
|
| | }
|
| |
|
| | void CmdPointsStructure::activated(int iMsg)
|
| | {
|
| | Q_UNUSED(iMsg);
|
| |
|
| | App::Document* doc = App::GetApplication().getActiveDocument();
|
| | doc->openTransaction("Structure point cloud");
|
| |
|
| | std::vector<App::DocumentObject*> docObj = Gui::Selection().getObjectsOfType(
|
| | Points::Feature::getClassTypeId()
|
| | );
|
| | for (auto it : docObj) {
|
| | std::string name = it->Label.getValue();
|
| | name += " (Structured)";
|
| | Points::Structured* output = doc->addObject<Points::Structured>(name.c_str());
|
| | output->Label.setValue(name);
|
| |
|
| |
|
| | if (it->isDerivedFrom<Points::Structured>()) {
|
| | Points::Structured* input = static_cast<Points::Structured*>(it);
|
| |
|
| | Points::PointKernel* kernel = output->Points.startEditing();
|
| | const Points::PointKernel& k = input->Points.getValue();
|
| |
|
| | kernel->resize(k.size());
|
| | for (std::size_t i = 0; i < k.size(); ++i) {
|
| | kernel->setPoint(i, k.getPoint(i));
|
| | }
|
| | output->Points.finishEditing();
|
| | output->Width.setValue(input->Width.getValue());
|
| | output->Height.setValue(input->Height.getValue());
|
| | }
|
| |
|
| | else {
|
| | Points::Feature* input = static_cast<Points::Feature*>(it);
|
| |
|
| | Points::PointKernel* kernel = output->Points.startEditing();
|
| | const Points::PointKernel& k = input->Points.getValue();
|
| |
|
| | Base::BoundBox3d bbox = input->Points.getBoundingBox();
|
| | double width = bbox.LengthX();
|
| | double height = bbox.LengthY();
|
| |
|
| |
|
| | std::set<double> countX, countY;
|
| | for (std::size_t i = 0; i < k.size(); ++i) {
|
| | Base::Vector3d pnt = k.getPoint(i);
|
| | countX.insert(pnt.x);
|
| | countY.insert(pnt.y);
|
| | }
|
| |
|
| | long width_l = long(countX.size());
|
| | long height_l = long(countY.size());
|
| |
|
| | double dx = width / (width_l - 1);
|
| | double dy = height / (height_l - 1);
|
| |
|
| |
|
| |
|
| | double nan = std::numeric_limits<double>::quiet_NaN();
|
| | std::vector<Base::Vector3d> sortedPoints(width_l * height_l, Base::Vector3d(nan, nan, nan));
|
| |
|
| | for (std::size_t i = 0; i < k.size(); ++i) {
|
| | Base::Vector3d pnt = k.getPoint(i);
|
| | double xi = (pnt.x - bbox.MinX) / dx;
|
| | double yi = (pnt.y - bbox.MinY) / dy;
|
| |
|
| | double xx = std::fabs(xi - std::round(xi));
|
| | double yy = std::fabs(yi - std::round(yi));
|
| | if (xx < 0.01 && yy < 0.01) {
|
| | xi = std::round(xi);
|
| | yi = std::round(yi);
|
| | long index = long(yi * width_l + xi);
|
| | sortedPoints[index] = pnt;
|
| | }
|
| | }
|
| |
|
| | kernel->resize(sortedPoints.size());
|
| | for (std::size_t index = 0; index < sortedPoints.size(); index++) {
|
| | kernel->setPoint(index, sortedPoints[index]);
|
| | }
|
| |
|
| | output->Points.finishEditing();
|
| | output->Width.setValue(width_l);
|
| | output->Height.setValue(height_l);
|
| | }
|
| | }
|
| |
|
| | doc->commitTransaction();
|
| | updateActive();
|
| | }
|
| |
|
| | bool CmdPointsStructure::isActive()
|
| | {
|
| | return getSelection().countObjectsOfType<Points::Feature>() == 1;
|
| | }
|
| |
|
| | void CreatePointsCommands()
|
| | {
|
| | Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager();
|
| | rcCmdMgr.addCommand(new CmdPointsImport());
|
| | rcCmdMgr.addCommand(new CmdPointsExport());
|
| | rcCmdMgr.addCommand(new CmdPointsConvert());
|
| | rcCmdMgr.addCommand(new CmdPointsPolyCut());
|
| | rcCmdMgr.addCommand(new CmdPointsMerge());
|
| | rcCmdMgr.addCommand(new CmdPointsStructure());
|
| | }
|
| |
|