From 0f6d6d4b3735573e4a14df5d40b48c7314ca2f26 Mon Sep 17 00:00:00 2001 From: wmayer Date: Mon, 26 Mar 2012 15:54:13 +0200 Subject: [PATCH] Implement manual alignment utility --- src/Gui/Application.cpp | 1 + src/Gui/CMakeLists.txt | 7 +- src/Gui/CommandDoc.cpp | 60 ++ src/Gui/ManualAlignment.cpp | 1244 ++++++++++++++++++++++++++++ src/Gui/ManualAlignment.h | 257 ++++++ src/Gui/SoAxisCrossKit.cpp | 146 +++- src/Gui/SoAxisCrossKit.h | 29 + src/Gui/SoFCDB.cpp | 1 + src/Gui/SplitView3DInventor.cpp | 102 ++- src/Gui/SplitView3DInventor.h | 23 +- src/Gui/Workbench.cpp | 3 +- src/Mod/Complete/Gui/Workbench.cpp | 3 +- 12 files changed, 1822 insertions(+), 54 deletions(-) create mode 100644 src/Gui/ManualAlignment.cpp create mode 100644 src/Gui/ManualAlignment.h diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index deffe1024f..2dbc4b2c8c 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -1407,6 +1407,7 @@ void Application::initTypes(void) Gui::BaseView ::init(); Gui::MDIView ::init(); Gui::View3DInventor ::init(); + Gui::AbstractSplitView ::init(); Gui::SplitView3DInventor ::init(); // View Provider Gui::ViewProvider ::init(); diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index 133d81a540..b7788cfcd7 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -125,6 +125,7 @@ set(Gui_MOC_HDRS HelpView.h InputVector.h MainWindow.h + ManualAlignment.h MDIView.h NetworkRetriever.h OnlineDocumentation.h @@ -627,7 +628,7 @@ SET(Inventor_CPP_SRCS SoFCInteractiveElement.cpp SoFCOffscreenRenderer.cpp SoFCSelection.cpp - SoFCUnifiedSelection.cpp + SoFCUnifiedSelection.cpp SoFCSelectionAction.cpp SoFCVectorizeSVGAction.cpp SoFCVectorizeU3DAction.cpp @@ -647,7 +648,7 @@ SET(Inventor_SRCS SoFCInteractiveElement.h SoFCOffscreenRenderer.h SoFCSelection.h - SoFCUnifiedSelection.h + SoFCUnifiedSelection.h SoFCSelectionAction.h SoFCVectorizeSVGAction.h SoFCVectorizeU3DAction.h @@ -743,6 +744,7 @@ SET(FreeCADGui_CPP_SRCS Thumbnail.cpp Utilities.cpp WaitCursor.cpp + ManualAlignment.cpp ) SET(FreeCADGui_SRCS Application.h @@ -763,6 +765,7 @@ SET(FreeCADGui_SRCS Thumbnail.h Utilities.h WaitCursor.h + ManualAlignment.h ) SET(FreeCADGui_SRCS diff --git a/src/Gui/CommandDoc.cpp b/src/Gui/CommandDoc.cpp index a0496abab3..52fa870be9 100644 --- a/src/Gui/CommandDoc.cpp +++ b/src/Gui/CommandDoc.cpp @@ -50,11 +50,13 @@ #include "DlgProjectUtility.h" #include "Transform.h" #include "Placement.h" +#include "ManualAlignment.h" #include "WaitCursor.h" #include "ViewProvider.h" #include #include #include "MergeDocuments.h" +#include "NavigationStyle.h" using namespace Gui; @@ -1013,6 +1015,63 @@ bool StdCmdPlacement::isActive(void) return (Gui::Control().activeDialog()==0); } +//=========================================================================== +// Std_Alignment +//=========================================================================== +DEF_STD_CMD_A(StdCmdAlignment); + +StdCmdAlignment::StdCmdAlignment() + : Command("Std_Alignment") +{ + sGroup = QT_TR_NOOP("Edit"); + sMenuText = QT_TR_NOOP("Alignment..."); + sToolTipText = QT_TR_NOOP("Align the selected objects"); + sStatusTip = QT_TR_NOOP("Align the selected objects"); + sWhatsThis = "Std_Alignment"; +} + +void StdCmdAlignment::activated(int iMsg) +{ + std::vector sel = Gui::Selection().getObjectsOfType + (App::GeoFeature::getClassTypeId()); + ManualAlignment* align = ManualAlignment::instance(); + QObject::connect(align, SIGNAL(emitCanceled()), align, SLOT(deleteLater())); + QObject::connect(align, SIGNAL(emitFinished()), align, SLOT(deleteLater())); + + // Get the fixed and moving meshes + FixedGroup fixedGroup; + std::map groupMap; + fixedGroup.addView(sel[0]); + groupMap[0].addView(sel[1]); + + // add the fixed group + align->setFixedGroup(fixedGroup); + + // create the model of movable groups + MovableGroupModel model; + model.addGroups(groupMap); + align->setModel(model); + Base::Type style = Base::Type::fromName("Gui::CADNavigationStyle"); + Gui::Document* doc = Application::Instance->activeDocument(); + if (doc) { + View3DInventor* mdi = qobject_cast(doc->getActiveView()); + if (mdi) { + style = mdi->getViewer()->navigationStyle()->getTypeId(); + } + } + + align->setMinPoints(1); + align->startAlignment(style); + Gui::Selection().clearSelection(); +} + +bool StdCmdAlignment::isActive(void) +{ + if (ManualAlignment::hasInstance()) + return false; + return Gui::Selection().countObjectsOfType(App::GeoFeature::getClassTypeId()) == 2; +} + //=========================================================================== // Std_Edit //=========================================================================== @@ -1085,6 +1144,7 @@ void CreateDocCommands(void) rcCmdMgr.addCommand(new StdCmdRefresh()); rcCmdMgr.addCommand(new StdCmdTransform()); rcCmdMgr.addCommand(new StdCmdPlacement()); + rcCmdMgr.addCommand(new StdCmdAlignment()); rcCmdMgr.addCommand(new StdCmdEdit()); } diff --git a/src/Gui/ManualAlignment.cpp b/src/Gui/ManualAlignment.cpp new file mode 100644 index 0000000000..ae4d8e1788 --- /dev/null +++ b/src/Gui/ManualAlignment.cpp @@ -0,0 +1,1244 @@ +/*************************************************************************** + * Copyright (c) 2012 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#ifndef _PreComp_ +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ManualAlignment.h" +#include "BitmapFactory.h" +#include "SoAxisCrossKit.h" + + +using namespace Gui; + +AlignmentGroup::AlignmentGroup() +{ +} + +AlignmentGroup::~AlignmentGroup() +{ +} + +void AlignmentGroup::addView(App::DocumentObject* pView) +{ + if (pView) { + App::Document* rDoc = pView->getDocument(); + Gui::Document* pDoc = Gui::Application::Instance->getDocument(rDoc); + Gui::ViewProviderDocumentObject* pProvider = static_cast + (pDoc->getViewProvider(pView)); + this->_views.push_back(pProvider); + } +} + +std::vector AlignmentGroup::getViews() const +{ + std::vector views; + + std::vector::const_iterator it; + for (it = this->_views.begin(); it != this->_views.end(); ++it) { + App::DocumentObject* pView = (*it)->getObject(); + views.push_back(pView); + } + + return views; +} + +bool AlignmentGroup::hasView(Gui::ViewProviderDocumentObject* pView) const +{ + std::vector::const_iterator it; + for (it = this->_views.begin(); it != this->_views.end(); ++it) { + if (*it == pView) + return true; + } + + return false; +} + +void AlignmentGroup::removeView(Gui::ViewProviderDocumentObject* pView) +{ + std::vector::iterator it; + for (it = this->_views.begin(); it != this->_views.end(); ++it) { + if (*it == pView) { + this->_views.erase(it); + break; + } + } +} + +void AlignmentGroup::addToViewer(Gui::View3DInventorViewer* viewer) const +{ + std::vector::const_iterator it; + for (it = this->_views.begin(); it != this->_views.end(); ++it) + viewer->addViewProvider(*it); + + viewer->viewAll(); +} + +void AlignmentGroup::removeFromViewer(Gui::View3DInventorViewer* viewer) const +{ + std::vector::const_iterator it; + for (it = this->_views.begin(); it != this->_views.end(); ++it) + viewer->removeViewProvider(*it); +} + +void AlignmentGroup::setRandomColor() +{ + std::vector::iterator it; + for (it = this->_views.begin(); it != this->_views.end(); ++it) { + float r = /*(float)rand()/(float)RAND_MAX*/0.0f; + float g = (float)rand()/(float)RAND_MAX; + float b = (float)rand()/(float)RAND_MAX; + if ((*it)->isDerivedFrom(Gui::ViewProviderGeometryObject::getClassTypeId())) { + SoSearchAction searchAction; + searchAction.setType(SoMaterial::getClassTypeId()); + searchAction.setInterest(SoSearchAction::FIRST); + searchAction.apply((*it)->getRoot()); + SoPath* selectionPath = searchAction.getPath(); + + if (selectionPath) { + SoMaterial* material = static_cast(selectionPath->getTail()); + material->diffuseColor.setValue(r, g, b); + } + } + } +} + +Gui::Document* AlignmentGroup::getDocument() const +{ + if (this->_views.empty()) + return 0; + App::DocumentObject* pView = this->_views[0]->getObject(); + if (pView) { + App::Document* rDoc = pView->getDocument(); + Gui::Document* pDoc = Gui::Application::Instance->getDocument(rDoc); + return pDoc; + } + + return 0; +} + +void AlignmentGroup::addPoint(const Base::Vector3d& pnt) +{ + this->_pickedPoints.push_back(pnt); +} + +void AlignmentGroup::removeLastPoint() +{ + this->_pickedPoints.pop_back(); +} + +int AlignmentGroup::countPoints() const +{ + return this->_pickedPoints.size(); +} + +const std::vector& AlignmentGroup::getPoints() const +{ + return this->_pickedPoints; +} + +void AlignmentGroup::clearPoints() +{ + this->_pickedPoints.clear(); +} + +void AlignmentGroup::setAlignable(bool align) +{ + //std::vector::iterator it; + //for (it = this->_views.begin(); it != this->_views.end(); ++it) { + // if (!align){ + // App::PropertyColor* pColor = (App::PropertyColor*)(*it)->getPropertyByName("ShapeColor"); + // if (pColor) + // pColor->touch(); // resets to color defined by property + // } + //} +} + +void AlignmentGroup::moveTo(AlignmentGroup& that) +{ + std::vector::iterator it; + for (it = this->_views.begin(); it != this->_views.end(); ++it) + that._views.push_back(*it); + + this->_views.clear(); +} + +void AlignmentGroup::clear() +{ + this->_views.clear(); + this->_pickedPoints.clear(); +} + +bool AlignmentGroup::isEmpty() const +{ + return this->_views.empty(); +} + +int AlignmentGroup::count() const +{ + return this->_views.size(); +} + +// ------------------------------------------------------------------ + +MovableGroup::MovableGroup() +{ +} + +MovableGroup::~MovableGroup() +{ +} + +// ------------------------------------------------------------------ + +FixedGroup::FixedGroup() +{ +} + +FixedGroup::~FixedGroup() +{ +} + +// ------------------------------------------------------------------ + +MovableGroupModel::MovableGroupModel() +{ +} + +MovableGroupModel::~MovableGroupModel() +{ +} + +void MovableGroupModel::addGroup(const MovableGroup& grp) +{ + this->_groups.push_back(grp); +} + +void MovableGroupModel::addGroups(const std::map& grps) +{ + for (std::map::const_iterator it = grps.begin(); it != grps.end(); ++it) + this->_groups.push_back(it->second); +} + +void MovableGroupModel::removeActiveGroup() +{ + this->_groups.erase(this->_groups.begin()); +} + +MovableGroup& MovableGroupModel::activeGroup() +{ + // Make sure that the array is not empty + if (this->_groups.empty()) + throw Base::Exception("Empty group"); + return *(this->_groups.begin()); +} + +const MovableGroup& MovableGroupModel::activeGroup() const +{ + // Make sure that the array is not empty + if (this->_groups.empty()) + throw Base::Exception("Empty group"); + return *(this->_groups.begin()); +} + +void MovableGroupModel::continueAlignment() +{ + if (!isEmpty()) + removeActiveGroup(); +} + +void MovableGroupModel::clear() +{ + this->_groups.clear(); +} + +bool MovableGroupModel::isEmpty() const +{ + return this->_groups.empty(); +} + +int MovableGroupModel::count() const +{ + return this->_groups.size(); +} + +// ------------------------------------------------------------------ + +namespace Gui { +class AlignmentView : public Gui::AbstractSplitView +{ +public: + QLabel* myLabel; + + AlignmentView(Gui::Document* pcDocument, QWidget* parent, Qt::WFlags wflags=0) + : AbstractSplitView(pcDocument, parent, wflags) + { + QSplitter* mainSplitter=0; + mainSplitter = new QSplitter(Qt::Horizontal, this); + _viewer.push_back(new View3DInventorViewer(mainSplitter)); + _viewer.push_back(new View3DInventorViewer(mainSplitter)); + + QFrame* vbox = new QFrame(this); + QVBoxLayout* layout = new QVBoxLayout(); + layout->setMargin(0); + layout->setSpacing(0); + vbox->setLayout(layout); + + myLabel = new QLabel(this); + myLabel->setAutoFillBackground(true); + QPalette pal = myLabel->palette(); + pal.setColor(QPalette::Window, Qt::darkGray); + pal.setColor(QPalette::WindowText, Qt::white); + myLabel->setPalette(pal); + mainSplitter->setPalette(pal); + myLabel->setAlignment(Qt::AlignCenter); + myLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + QFont font = myLabel->font(); + font.setPointSize(14); + myLabel->setFont(font); + layout->addWidget(myLabel); + layout->addWidget(mainSplitter); + + vbox->show(); + setCentralWidget(vbox); + + // apply the user settings + setupSettings(); + + static_cast(getViewer(0)->getSceneManager()->getSceneGraph())-> + addChild(setupHeadUpDisplay(tr("Movable object"))); + static_cast(getViewer(1)->getSceneManager()->getSceneGraph())-> + addChild(setupHeadUpDisplay(tr("Fixed object"))); + } + ~AlignmentView() + { + } + bool canClose() + { + return false; + } + SoNode* setupHeadUpDisplay(const QString& text) const + { + SoSeparator* hudRoot = new SoSeparator; + hudRoot->ref(); + + SoOrthographicCamera* hudCam = new SoOrthographicCamera(); + hudCam->viewportMapping = SoCamera::LEAVE_ALONE; + + // Set the position in the window. + // [0, 0] is in the center of the screen. + // + SoTranslation* hudTrans = new SoTranslation; + hudTrans->translation.setValue(-0.95f, -0.95f, 0.0f); + + QFont font = this->font(); + font.setPointSize(24); + QFontMetrics fm(font); + + QColor front; + front.setRgbF(0.8f, 0.8f, 0.8f); + + int w = fm.width(text); + int h = fm.height(); + + QImage image(w,h,QImage::Format_ARGB32_Premultiplied); + image.fill(0x00000000); + QPainter painter(&image); + painter.setRenderHint(QPainter::Antialiasing); + painter.setPen(front); + painter.setFont(font); + painter.drawText(0,0,w,h,Qt::AlignLeft,text); + painter.end(); + SoSFImage sfimage; + Gui::BitmapFactory().convert(image, sfimage); + SoImage* hudImage = new SoImage(); + hudImage->image = sfimage; + + // Assemble the parts... + // + hudRoot->addChild(hudCam); + hudRoot->addChild(hudTrans); + hudRoot->addChild(hudImage); + + return hudRoot; + } +}; +} + +class ManualAlignment::Private { +public: + SoSeparator * picksepLeft; + SoSeparator * picksepRight; + SoNodeSensor* sensorCam1; + SoNodeSensor* sensorCam2; + SbRotation rot_cam1, rot_cam2; + SbVec3f pos_cam1, pos_cam2; + + Private() + : sensorCam1(0), sensorCam2(0) + { + // left view + picksepLeft = new SoSeparator; + picksepLeft->ref(); + // right view + picksepRight = new SoSeparator; + picksepRight->ref(); + } + ~Private() + { + picksepLeft->unref(); + picksepRight->unref(); + delete sensorCam1; + delete sensorCam2; + } + + static + void reorientCamera(SoCamera * cam, const SbRotation & rot) + { + if (cam == NULL) return; + + // Find global coordinates of focal point. + SbVec3f direction; + cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction); + SbVec3f focalpoint = cam->position.getValue() + + cam->focalDistance.getValue() * direction; + + // Set new orientation value by accumulating the new rotation. + cam->orientation = rot * cam->orientation.getValue(); + + // Reposition camera so we are still pointing at the same old focal point. + cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction); + cam->position = focalpoint - cam->focalDistance.getValue() * direction; + } + + static + void copyCameraSettings(SoCamera* cam1, SbRotation& rot_cam1, SbVec3f& pos_cam1, + SoCamera* cam2, SbRotation& rot_cam2, SbVec3f& pos_cam2) + { + // recompute the diff we have applied to the camera's orientation + SbRotation rot = cam1->orientation.getValue(); + SbRotation dif = rot * rot_cam1.inverse(); + rot_cam1 = rot; + + // copy the values + cam2->enableNotify(FALSE); + cam2->nearDistance = cam1->nearDistance; + cam2->farDistance = cam1->farDistance; + cam2->focalDistance = cam1->focalDistance; + reorientCamera(cam2,dif); + rot_cam2 = cam2->orientation.getValue(); + + // reverse engineer the translation part in wc + SbVec3f pos = cam1->position.getValue(); + SbVec3f difpos = pos - pos_cam1; + pos_cam1 = pos; + // the translation in pixel coords + cam1->orientation.getValue().inverse().multVec(difpos,difpos); + // the translation again in wc for the second camera + cam2->orientation.getValue().multVec(difpos,difpos); + cam2->position.setValue(cam2->position.getValue()+difpos); + + if (cam1->getTypeId() == cam2->getTypeId()) { + if (cam1->getTypeId() == SoOrthographicCamera::getClassTypeId()) + static_cast(cam2)->height = + static_cast(cam1)->height; + } + + cam2->enableNotify(TRUE); + } + static + void syncCameraCB(void * data, SoSensor * s) + { + ManualAlignment* self = reinterpret_cast(data); + if (!self->myViewer) + return; // already destroyed + SoCamera* cam1 = self->myViewer->getViewer(0)->getCamera(); + SoCamera* cam2 = self->myViewer->getViewer(1)->getCamera(); + if (!cam1 || !cam2) + return; // missing camera + SoNodeSensor* sensor = static_cast(s); + SoNode* node = sensor->getAttachedNode(); + if (node && node->getTypeId().isDerivedFrom(SoCamera::getClassTypeId())) { + if (node == cam1) { + Private::copyCameraSettings(cam1, self->d->rot_cam1, self->d->pos_cam1, + cam2, self->d->rot_cam2, self->d->pos_cam2); + self->myViewer->getViewer(1)->render(); + } + else if (node == cam2) { + Private::copyCameraSettings(cam2, self->d->rot_cam2, self->d->pos_cam2, + cam1, self->d->rot_cam1, self->d->pos_cam1); + self->myViewer->getViewer(0)->render(); + } + } + } + + static Base::Placement + transformation2x2(const Base::Vector3d& plane1_base, + const Base::Vector3d& plane1_xaxis, + const Base::Vector3d& plane2_base, + const Base::Vector3d& plane2_xaxis) + { + // the transformation is: + // * move from plane1_base to plane2_base + // * rotate from plane1_zaxis to plane2_zaxis around plane2_base as center point + Base::Rotation rot(plane1_xaxis, plane2_xaxis); + + Base::Vector3d pln_base; + rot.multVec(plane1_base,pln_base); + Base::Vector3d dif = plane2_base - pln_base; + return Base::Placement(dif, rot); + } + + static Base::Placement + transformation3x3(const Base::Vector3d& plane1_base, + const Base::Vector3d& plane1_zaxis, + const Base::Vector3d& plane1_xaxis, + const Base::Vector3d& plane2_base, + const Base::Vector3d& plane2_zaxis, + const Base::Vector3d& plane2_xaxis) + { + // the transformation is: + // * move from plane1_base to plane2_base + // * rotate from plane1_zaxis to plane2_zaxis around plane2_base as center point + Base::Rotation rot(plane1_zaxis, plane2_zaxis); + + // first transformation to align the plane normals and base points + Base::Vector3d dif1 = plane1_base; + rot.multVec(dif1,dif1); + dif1 = plane2_base - dif1; + Base::Placement plm1(dif1, rot); + + // second transformation to align the planes' x axes + Base::Vector3d pln_xaxis; + rot.multVec(plane1_xaxis,pln_xaxis); + Base::Rotation rot2(pln_xaxis, plane2_xaxis); + Base::Vector3d dif2 = plane2_base; + rot2.multVec(dif2,dif2); + dif2 = plane2_base - dif2; + Base::Placement plm2(dif2, rot2); + plm2 = plm2 * plm1; + return plm2; + } +}; + +/* TRANSLATOR Gui::ManualAlignment */ + +ManualAlignment* ManualAlignment::_instance = 0; + +/** + * Construction. + */ +ManualAlignment::ManualAlignment() + : myViewer(0), myDocument(0), myPickPoints(3), d(new Private) +{ + // connect with the application's signal for deletion of documents + this->connectApplicationDeletedDocument = Gui::Application::Instance->signalDeleteDocument + .connect(boost::bind(&ManualAlignment::slotDeletedDocument, this, _1)); + + // setup sensor connection + d->sensorCam1 = new SoNodeSensor(Private::syncCameraCB, this); + d->sensorCam2 = new SoNodeSensor(Private::syncCameraCB, this); +} + +/** + * Destruction. + */ +ManualAlignment::~ManualAlignment() +{ + this->connectDocumentDeletedObject.disconnect(); + this->connectApplicationDeletedDocument.disconnect(); + closeViewer(); + delete d; + _instance = 0; +} + +/** + * Creates the one and only instance of this class. + */ +ManualAlignment* ManualAlignment::instance() +{ + // not initialized? + if (!_instance) + _instance = new ManualAlignment(); + return _instance; +} + +/** + * Destructs the one and only instance of this class. + */ +void ManualAlignment::destruct() +{ + if (_instance) { + ManualAlignment* tmp = _instance; + _instance = 0; + delete tmp; + } +} + +/** + * Checks whether the one instance exists. + */ +bool ManualAlignment::hasInstance() +{ + return _instance != 0; +} + +void ManualAlignment::setMinPoints(int minPoints) +{ + if ((minPoints > 0) && (minPoints <= 3)) + myPickPoints = minPoints; +} + +void ManualAlignment::setFixedGroup(const FixedGroup& fixed) +{ + this->myFixedGroup = fixed; + this->myDocument = fixed.getDocument(); +} + +void ManualAlignment::setModel(const MovableGroupModel& model) +{ + this->myAlignModel = model; +} + +void ManualAlignment::clearAll() +{ + myFixedGroup.clear(); + myAlignModel.clear(); + myDocument = 0; +} + +void ManualAlignment::setViewingDirections(const Base::Vector3d& view1, const Base::Vector3d& up1, + const Base::Vector3d& view2, const Base::Vector3d& up2) +{ + if (myViewer.isNull()) + return; + + { + SbRotation rot; + rot.setValue(SbVec3f(0.0f, 0.0f, 1.0f), SbVec3f(-view1.x,-view1.y,-view1.z)); + + SbRotation rot2; + SbVec3f up(0.0f, 1.0f, 0.0f); + rot.multVec(up, up); + rot2.setValue(up, SbVec3f(up1.x, up1.y, up1.z)); + myViewer->getViewer(0)->getCamera()->orientation.setValue(rot * rot2); + myViewer->getViewer(0)->viewAll(); + } + + { + SbRotation rot; + rot.setValue(SbVec3f(0.0f, 0.0f, 1.0f), SbVec3f(-view2.x,-view2.y,-view2.z)); + + SbRotation rot2; + SbVec3f up(0.0f, 1.0f, 0.0f); + rot.multVec(up, up); + rot2.setValue(up, SbVec3f(up2.x, up2.y, up2.z)); + myViewer->getViewer(1)->getCamera()->orientation.setValue(rot * rot2); + myViewer->getViewer(1)->viewAll(); + } +} + +/** + * Performs the alignment for the specified aligned and non-aligned views specified by setModel() and setFixedGroup(). + */ +void ManualAlignment::startAlignment(Base::Type mousemodel) +{ + // allow only one alignment at a time + if (!myViewer.isNull()) { + QMessageBox::warning(qApp->activeWindow(), tr("Manual alignment"), tr("The alignment is already in progress.")); + return; + } + + myTransform = Base::Placement(); + + if (myFixedGroup.isEmpty()) + return; + if (myAlignModel.isEmpty()) + return; + + // create a splitted window for picking the points + myViewer = new AlignmentView(myDocument,Gui::getMainWindow()); + myViewer->setWindowTitle(tr("Alignment[*]")); + myViewer->setWindowIcon(QApplication::windowIcon()); + myViewer->resize(400, 300); + Gui::getMainWindow()->addWindow(myViewer); + myViewer->showMaximized(); + int n = this->myPickPoints; + QString msg = n == 1 + ? tr("Please, select at least one point in the left and the right view") + : tr("Please, select at least %1 points in the left and the right view").arg(n); + myViewer->myLabel->setText(msg); + + connect(myViewer, SIGNAL(destroyed()), this, SLOT(reset())); + + // show all aligned views in the 2nd view + myFixedGroup.addToViewer(myViewer->getViewer(1)); + myFixedGroup.setAlignable(true); + + // set picked points root + SoNode* node1 = myViewer->getViewer(0)->getSceneGraph(); + if (node1->getTypeId().isDerivedFrom(SoGroup::getClassTypeId())){ + ((SoGroup*)node1)->addChild(d->picksepLeft); + } + SoNode* node2 = myViewer->getViewer(1)->getSceneGraph(); + if (node2->getTypeId().isDerivedFrom(SoGroup::getClassTypeId())){ + ((SoGroup*)node2)->addChild(d->picksepRight); + } + + myViewer->getViewer(0)->setEditing(true); + myViewer->getViewer(0)->addEventCallback(SoMouseButtonEvent::getClassTypeId(), + ManualAlignment::probePickedCallback); + myViewer->getViewer(1)->setEditing(true); + myViewer->getViewer(1)->addEventCallback(SoMouseButtonEvent::getClassTypeId(), + ManualAlignment::probePickedCallback); + // apply the mouse model + myViewer->getViewer(0)->setNavigationType(mousemodel); + myViewer->getViewer(1)->setNavigationType(mousemodel); + + // Connect to the document's signal as we want to be notified when something happens + if (this->connectDocumentDeletedObject.connected()) + this->connectDocumentDeletedObject.disconnect(); + this->connectDocumentDeletedObject = myDocument->signalDeletedObject.connect(boost::bind + (&ManualAlignment::slotDeletedObject, this, _1)); + + continueAlignment(); +} + +/** + * If still one view needs to be aligned then it is shown in the first window. If all views are aligned the process will be terminated. + */ +void ManualAlignment::continueAlignment() +{ + myFixedGroup.clearPoints(); + d->picksepLeft->removeAllChildren(); + d->picksepRight->removeAllChildren(); + + if (!myAlignModel.isEmpty()) { + AlignmentGroup& grp = myAlignModel.activeGroup(); + grp.clearPoints(); + grp.addToViewer(myViewer->getViewer(0)); + grp.setAlignable(true); + + Gui::getMainWindow()->statusBar()->showMessage(tr("Please pick points in the left and right view")); + + myViewer->getViewer(0)->setEditingCursor(QCursor(Qt::PointingHandCursor)); + myViewer->getViewer(1)->setEditingCursor(QCursor(Qt::PointingHandCursor)); + } + else { + finish(); + } +} + +void ManualAlignment::closeViewer() +{ + if (!myViewer) + return; + // Close the viewer +#if !defined(NO_USE_QT_MDI_AREA) + if (myViewer->parentWidget()) + myViewer->parentWidget()->deleteLater(); +#else + myViewer->deleteLater(); +#endif + myViewer = 0; +} + +/** + * Make all views unpickable and resets internal data. + */ +void ManualAlignment::reset() +{ + if (!myAlignModel.isEmpty()) { + myAlignModel.activeGroup().setAlignable(false); + myAlignModel.activeGroup().clear(); + myAlignModel.clear(); + } + + myFixedGroup.setAlignable(false); + myFixedGroup.clear(); + + d->picksepLeft->removeAllChildren(); + d->picksepRight->removeAllChildren(); + + if (myDocument) { + this->connectDocumentDeletedObject.disconnect(); + myDocument = 0; + } +} + +/** + * Terminates the process and closes the windows. + */ +void ManualAlignment::finish() +{ + if (myViewer.isNull()) + return; + + if (myDocument) + myDocument->getDocument()->recompute(); + closeViewer(); + reset(); + + Gui::getMainWindow()->statusBar()->showMessage(tr("The alignment has finished")); + + // If an event receiver has been defined send the manual alignment finished event to it + emitFinished(); +} + +/** + * Cancels the process and clöses the windows without performing an alignment. + */ +void ManualAlignment::cancel() +{ + if (myViewer.isNull()) + return; + + closeViewer(); + myTransform = Base::Placement(); + reset(); + + Gui::getMainWindow()->statusBar()->showMessage(tr("The alignment has been canceled")); + + // If an event receiver has been defined send the manual alignment cancelled event to it + emitCanceled(); +} + +void ManualAlignment::align() +{ + // Now we can start the actual alignment + if (myAlignModel.activeGroup().countPoints() < myPickPoints) { + QMessageBox::warning(myViewer, tr("Manual alignment"), + tr("Too few points picked in the left view." + " At least %1 points are needed.").arg(myPickPoints)); + } + else if (myFixedGroup.countPoints() < myPickPoints) { + QMessageBox::warning(myViewer, tr("Manual alignment"), + tr("Too few points picked in the right view." + " At least %1 points are needed.").arg(myPickPoints)); + } + else if (myAlignModel.activeGroup().countPoints() != myFixedGroup.countPoints()) { + QMessageBox::warning(myViewer, tr("Manual alignment"), + tr("Different number of points picked in left and right view.\n" + "On the left view %1 points are picked,\n" + "on the right view %2 points are picked.") + .arg(myAlignModel.activeGroup().countPoints()) + .arg(myFixedGroup.countPoints())); + } + else { + // do not allow to pick further points + myAlignModel.activeGroup().removeFromViewer(myViewer->getViewer(0)); + myAlignModel.activeGroup().setAlignable(false); + std::vector pViews = myAlignModel.activeGroup().getViews(); + Gui::getMainWindow()->statusBar()->showMessage(tr("Try to align group of views")); + + // Compute alignment + bool ok = computeAlignment(myAlignModel.activeGroup().getPoints(), myFixedGroup.getPoints()); + if (ok) { + // Align views + for (std::vector::iterator it = pViews.begin(); it != pViews.end(); ++it) + alignObject(*it); + + // the alignment was successful so show it in the right view now + //myAlignModel.activeGroup().setRandomColor(); + myAlignModel.activeGroup().setAlignable(true); + myAlignModel.activeGroup().addToViewer(myViewer->getViewer(1)); + myAlignModel.activeGroup().moveTo(myFixedGroup); + myAlignModel.continueAlignment(); + } + else { + // Inform user that alignment failed + int ret = QMessageBox::critical(myViewer, tr("Manual alignment"), + tr("The alignment failed.\nHow do you want to proceed?"), + tr("Retry"), tr("Ignore"), tr("Abort")); + if ( ret == 1 ) { + myAlignModel.continueAlignment(); + } + else if ( ret == 2 ) { + finish(); + return; + } + } + + continueAlignment(); + } +} + +void ManualAlignment::showInstructions() +{ + // Now we can start the actual alignment + if (myAlignModel.activeGroup().countPoints() < myPickPoints) { + Gui::getMainWindow()->statusBar()->showMessage( + tr("Too few points picked in the left view." + " At least %1 points are needed.").arg(myPickPoints)); + } + else if (myFixedGroup.countPoints() < myPickPoints) { + Gui::getMainWindow()->statusBar()->showMessage( + tr("Too few points picked in the right view." + " At least %1 points are needed.").arg(myPickPoints)); + } + else if (myAlignModel.activeGroup().countPoints() != myFixedGroup.countPoints()) { + Gui::getMainWindow()->statusBar()->showMessage( + tr("Different number of points picked in left and right view. " + "On the left view %1 points are picked, " + "on the right view %2 points are picked.") + .arg(myAlignModel.activeGroup().countPoints()) + .arg(myFixedGroup.countPoints())); + } +} + +bool ManualAlignment::canAlign() const +{ + if (myAlignModel.activeGroup().countPoints() == myFixedGroup.countPoints()) { + if (myFixedGroup.countPoints() >= myPickPoints) + return true; + } + + return false; +} + +/** + * This method computes the alignment. For the calculation of the alignment the picked points of both views + * are taken. If the alignment fails false is returned, true otherwise. + */ +bool ManualAlignment::computeAlignment(const std::vector& movPts, + const std::vector& fixPts) +{ + assert((int)movPts.size() >= myPickPoints); + assert((int)fixPts.size() >= myPickPoints); + assert((int)movPts.size() == (int)fixPts.size()); + myTransform = Base::Placement(); + + if (movPts.size() == 1) { + // 1 point partial solution: Simple translation only + myTransform.setPosition(fixPts[0] - movPts[0]); + } + else if (movPts.size() == 2) { + const Base::Vector3d& p1 = movPts[0]; + const Base::Vector3d& p2 = movPts[1]; + Base::Vector3d d1 = p2-p1; + d1.Normalize(); + + const Base::Vector3d& q1 = fixPts[0]; + const Base::Vector3d& q2 = fixPts[1]; + Base::Vector3d d2 = q2-q1; + d2.Normalize(); + + myTransform = Private::transformation2x2(p1, d1, q1, d2); + } + else if (movPts.size() >= 3) { + const Base::Vector3d& p1 = movPts[0]; + const Base::Vector3d& p2 = movPts[1]; + const Base::Vector3d& p3 = movPts[2]; + Base::Vector3d d1 = p2-p1; + d1.Normalize(); + Base::Vector3d n1 = (p2-p1) % (p3-p1); + n1.Normalize(); + + const Base::Vector3d& q1 = fixPts[0]; + const Base::Vector3d& q2 = fixPts[1]; + const Base::Vector3d& q3 = fixPts[2]; + Base::Vector3d d2 = q2-q1; + d2.Normalize(); + Base::Vector3d n2 = (q2-q1) % (q3-q1); + n2.Normalize(); + + myTransform = Private::transformation3x3(p1, d1, n1, q1, d2, n2); + } + + return true; +} + +/** + * This method performs the actual alignment of view \a pView. + */ +void ManualAlignment::alignObject(App::DocumentObject *obj) +{ + if (obj->getTypeId().isDerivedFrom(App::GeoFeature::getClassTypeId())) { + App::GeoFeature* geom = static_cast(obj); + Base::Placement plm = geom->Placement.getValue(); + plm = this->myTransform * plm; + geom->Placement.setValue(plm); + } +} + +/** + * Creates a point element as visible feedback for the user. + */ +SoNode* ManualAlignment::pickedPointsSubGraph(const SbVec3f& p, const SbVec3f& n, int id) +{ + static const float color_table [10][3] = { + {1.0f,0.0f,0.0f}, // red + {0.0f,1.0f,0.0f}, // green + {0.0f,0.0f,1.0f}, // blue + {1.0f,1.0f,0.0f}, // yellow + {0.0f,1.0f,1.0f}, // cyan + {0.7f,0.0f,0.0f}, + {0.0f,0.7f,0.0f}, + {0.7f,0.7f,0.0f}, + {0.7f,0.0f,0.5f}, + {1.0f,0.7f,0.0f} + }; + + int index = (id-1) % 10; + + SoRegPoint* probe = new SoRegPoint(); + probe->base.setValue(p); + probe->normal.setValue(n); + probe->color.setValue(color_table[index][0],color_table[index][1],color_table[index][2]); + SbString s; + probe->text.setValue(s.sprintf("RegPoint_%d", id)); + return probe; +} + +/** + * Handle if the current document is about to being closed. + */ +void ManualAlignment::slotDeletedDocument(const Gui::Document& Doc) +{ + if (&Doc == this->myDocument) + reset(); +} + +/** + * Handle if the a view provider is about to being destroyed. + */ +void ManualAlignment::slotDeletedObject(const Gui::ViewProvider& Obj) +{ + // remove the view provider either from the left or the right view + if (Obj.getTypeId().isDerivedFrom(Gui::ViewProviderDocumentObject::getClassTypeId())) { + // remove the view provider immediately from the split window + bool found = false; + Gui::ViewProviderDocumentObject* vp = const_cast + (static_cast(&Obj)); + if (myAlignModel.activeGroup().hasView(vp)) { + myViewer->getViewer(0)->removeViewProvider(vp); + found = true; + } + if (myFixedGroup.hasView(vp)) { + myViewer->getViewer(1)->removeViewProvider(vp); + found = true; + } + + if (found) + cancel(); + } +} + +void ManualAlignment::onAlign() +{ + align(); +} + +void ManualAlignment::onRemoveLastPointMoveable() +{ + int nPoints = myAlignModel.activeGroup().countPoints(); + if (nPoints > 0) { + myAlignModel.activeGroup().removeLastPoint(); + d->picksepLeft->removeChild(nPoints-1); + } +} + +void ManualAlignment::onRemoveLastPointFixed() +{ + int nPoints = myFixedGroup.countPoints(); + if (nPoints > 0) { + myFixedGroup.removeLastPoint(); + d->picksepRight->removeChild(nPoints-1); + } +} + +void ManualAlignment::onClear() +{ + myAlignModel.activeGroup().clear(); + myFixedGroup.clear(); + + d->picksepLeft->removeAllChildren(); + d->picksepRight->removeAllChildren(); +} + +void ManualAlignment::onCancel() +{ + cancel(); +} + +void ManualAlignment::probePickedCallback(void * ud, SoEventCallback * n) +{ + Gui::View3DInventorViewer* view = reinterpret_cast(n->getUserData()); + const SoEvent* ev = n->getEvent(); + if (ev->getTypeId() == SoMouseButtonEvent::getClassTypeId()) { + // set as handled + n->getAction()->setHandled(); + n->setHandled(); + + const SoMouseButtonEvent * mbe = static_cast(ev); + if (mbe->getButton() == SoMouseButtonEvent::BUTTON1 && mbe->getState() == SoButtonEvent::DOWN) { + // if we are in 'align' mode then handle the click event + ManualAlignment* self = ManualAlignment::instance(); + // Get the closest point to the camera of the whole scene. + // This point doesn't need to be part of this view provider. + Gui::WaitCursor wc; + const SoPickedPoint * point = n->getPickedPoint(); + if (point) { + Gui::ViewProvider* vp = static_cast(view->getViewProviderByPath(point->getPath())); + if (vp && vp->getTypeId().isDerivedFrom(Gui::ViewProviderDocumentObject::getClassTypeId())) { + Gui::ViewProviderDocumentObject* that = static_cast(vp); + self->applyPickedProbe(that, point); + + const SbVec3f& vec = point->getPoint(); + Gui::getMainWindow()->statusBar()->showMessage( + tr("Point picked at (%1,%2,%3)") + .arg(vec[0]).arg(vec[1]).arg(vec[2])); + } + } + else { + Gui::getMainWindow()->statusBar()->showMessage( + tr("No point was picked")); + } + } + else if (mbe->getButton() == SoMouseButtonEvent::BUTTON2 && mbe->getState() == SoButtonEvent::UP) { + ManualAlignment* self = ManualAlignment::instance(); + if (self->myAlignModel.isEmpty() || self->myFixedGroup.isEmpty()) + return; + self->showInstructions(); + int nPoints; + if (view == self->myViewer->getViewer(0)) + nPoints = self->myAlignModel.activeGroup().countPoints(); + else + nPoints = self->myFixedGroup.countPoints(); + QMenu menu; + QAction* fi = menu.addAction(QLatin1String("&Align")); + QAction* rem = menu.addAction(QLatin1String("&Remove last point")); + //QAction* cl = menu.addAction("C&lear"); + QAction* ca = menu.addAction(QLatin1String("&Cancel")); + fi->setEnabled(self->canAlign()); + rem->setEnabled(nPoints > 0); + menu.addSeparator(); + QAction* sync = menu.addAction(QLatin1String("&Synchronize views")); + sync->setCheckable(true); + if (self->d->sensorCam1->getAttachedNode()) + sync->setChecked(true); + QAction* id = menu.exec(QCursor::pos()); + if (id == fi) { + // call align->align(); + QTimer::singleShot(300, self, SLOT(onAlign())); + } + else if ((id == rem) && (view == self->myViewer->getViewer(0))) { + QTimer::singleShot(300, self, SLOT(onRemoveLastPointMoveable())); + } + else if ((id == rem) && (view == self->myViewer->getViewer(1))) { + QTimer::singleShot(300, self, SLOT(onRemoveLastPointFixed())); + } + //else if (id == cl) { + // // call align->clear(); + // QTimer::singleShot(300, self, SLOT(onClear())); + //} + else if (id == ca) { + // call align->cancel(); + QTimer::singleShot(300, self, SLOT(onCancel())); + } + else if (id == sync) { + // setup sensor connection + if (sync->isChecked()) { + SoCamera* cam1 = self->myViewer->getViewer(0)->getCamera(); + SoCamera* cam2 = self->myViewer->getViewer(1)->getCamera(); + if (cam1 && cam2) { + self->d->sensorCam1->attach(cam1); + self->d->rot_cam1 = cam1->orientation.getValue(); + self->d->pos_cam1 = cam1->position.getValue(); + self->d->sensorCam2->attach(cam2); + self->d->rot_cam2 = cam2->orientation.getValue(); + self->d->pos_cam2 = cam2->position.getValue(); + } + } + else { + self->d->sensorCam1->detach(); + self->d->sensorCam2->detach(); + } + } + } + } +} + +/** + * This method stores the picked point \a pnt from the view provider \a prov. If enough points in both windows have been picked + * the alignment gets invoked. + */ +void ManualAlignment::applyPickedProbe(Gui::ViewProviderDocumentObject* prov, const SoPickedPoint* pnt) +{ + const SbVec3f& vec = pnt->getPoint(); + const SbVec3f& nor = pnt->getNormal(); + + // add to the list for the non-aligned view in the left view + if (myAlignModel.activeGroup().hasView(prov)) { + myAlignModel.activeGroup().addPoint(Base::Vector3d(vec[0],vec[1],vec[2])); + // Adds a point marker for the picked point. + d->picksepLeft->addChild(pickedPointsSubGraph(vec, nor, myAlignModel.activeGroup().countPoints())); + } + else if (myFixedGroup.hasView(prov)) { + myFixedGroup.addPoint(Base::Vector3d(vec[0],vec[1],vec[2])); + // Adds a point marker for the picked point. + d->picksepRight->addChild(pickedPointsSubGraph(vec, nor, myFixedGroup.countPoints())); + } +} + +#include "moc_ManualAlignment.cpp" + diff --git a/src/Gui/ManualAlignment.h b/src/Gui/ManualAlignment.h new file mode 100644 index 0000000000..78a93391fe --- /dev/null +++ b/src/Gui/ManualAlignment.h @@ -0,0 +1,257 @@ +/*************************************************************************** + * Copyright (c) 2012 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef GUI_MANUALALIGNMENT_H +#define GUI_MANUALALIGNMENT_H + +#include +#include +#include +#include +#include +#include + +class SbVec3f; +class SoPickedPoint; +class SoEventCallback; + +namespace Gui { +class Document; +class AlignmentView; +class View3DInventorViewer; + +/** + * The AlignemntGroup class is the base for fixed and movable groups. + * @author Werner Mayer + */ +class GuiExport AlignmentGroup +{ +protected: + AlignmentGroup(); + ~AlignmentGroup(); + +public: + /** + * Add a mesh to the group. + */ + void addView(App::DocumentObject*); + std::vector getViews() const; + /** + * Checks for the view provider of one of the added views. + */ + bool hasView(Gui::ViewProviderDocumentObject*) const; + /** + * Remove a previously added view by its view provider. + */ + void removeView(Gui::ViewProviderDocumentObject*); + /** + * Add the group and therefore all its added view providers to the Inventor tree. + */ + void addToViewer(Gui::View3DInventorViewer*) const; + /** + * Remove all the view providers from the Inventor tree. + */ + void removeFromViewer(Gui::View3DInventorViewer*) const; + void setRandomColor(); + /** + * Returns the document of the added views. + */ + Gui::Document* getDocument() const; + /** + * Add a point to an array of picked points. + */ + void addPoint(const Base::Vector3d&); + /** + * Remove last point from array of picked points. + */ + void removeLastPoint(); + /** + * Count the number of picked points. + */ + int countPoints() const; + /** + * Return an array of picked points. + */ + const std::vector& getPoints() const; + /** + * Clear all picked points. + */ + void clearPoints(); + /** + * Set or unset the alignable mode for the added views. If a view is not alignable it also not pickable. + */ + void setAlignable(bool); + void moveTo(AlignmentGroup&); + /** + * Clear the list of added views. + */ + void clear(); + /** + * Checks whether the list of added views is empty or not. + */ + bool isEmpty() const; + /** + * Return the number of added views. + */ + int count() const; + +protected: + std::vector _pickedPoints; + std::vector _views; +}; + +/** + * The FixedGroup class can be used for a fixed group of views. + * @author Werner Mayer + */ +class GuiExport MovableGroup : public AlignmentGroup +{ +public: + MovableGroup(); + ~MovableGroup(); +}; + +/** + * The FixedGroup class can be used for a fixed group of views. + * @author Werner Mayer + */ +class GuiExport FixedGroup : public AlignmentGroup +{ +public: + FixedGroup(); + ~FixedGroup(); +}; + +/** + * The MovableGroupModel class keeps an array of movable groups. + * @author Werner Mayer + */ +class GuiExport MovableGroupModel +{ +public: + MovableGroupModel(); + ~MovableGroupModel(); + + void addGroup(const MovableGroup&); + void addGroups(const std::map&); + MovableGroup& activeGroup(); + const MovableGroup& activeGroup() const; + void continueAlignment(); + void clear(); + bool isEmpty() const; + int count() const; + +protected: + void removeActiveGroup(); + +private: + std::vector _groups; +}; + +/** + * @author Werner Mayer + */ +class GuiExport ManualAlignment : public QObject +{ + Q_OBJECT + +protected: + ManualAlignment(); + ~ManualAlignment(); + +public: + static ManualAlignment* instance(); + static void destruct(); + static bool hasInstance(); + + void setMinPoints(int minPoints); + void setFixedGroup(const FixedGroup&); + void setModel(const MovableGroupModel&); + void clearAll(); + + void setViewingDirections(const Base::Vector3d& view1, const Base::Vector3d& up1, + const Base::Vector3d& view2, const Base::Vector3d& up2); + void startAlignment(Base::Type mousemodel); + void finish(); + void align(); + bool canAlign() const; + void cancel(); + + const Base::Placement & getTransform() const + { return myTransform; } + void alignObject(App::DocumentObject*); + + // Observer stuff + /// Checks if the given object is about to be removed + void slotDeletedDocument(const Gui::Document& Doc); + /// Checks if the given document is about to be closed + void slotDeletedObject(const Gui::ViewProvider& Obj); + +protected: + bool computeAlignment(const std::vector& unnavPts, const std::vector& navigPts); + void continueAlignment(); + void showInstructions(); + /** @name Probe picking */ + //@{ + static void probePickedCallback(void * ud, SoEventCallback * n); + void applyPickedProbe(Gui::ViewProviderDocumentObject*, const SoPickedPoint* pnt); + //@} + +protected Q_SLOTS: + void reset(); + void onAlign(); + void onRemoveLastPointMoveable(); + void onRemoveLastPointFixed(); + void onClear(); + void onCancel(); + +Q_SIGNALS: + void emitCanceled(); + void emitFinished(); + +private: + SoNode* pickedPointsSubGraph(const SbVec3f& p, const SbVec3f& n, int id); + void closeViewer(); + + static ManualAlignment* _instance; + + typedef boost::BOOST_SIGNALS_NAMESPACE::connection Connection; + Connection connectApplicationDeletedDocument; + Connection connectDocumentDeletedObject; + + FixedGroup myFixedGroup; + MovableGroupModel myAlignModel; + QPointer myViewer; + Gui::Document* myDocument; + int myPickPoints; + Base::Placement myTransform; + + class Private; + Private* d; +}; + +} // namespace Gui + + +#endif // GUI_MANUALALIGNMENT_H + diff --git a/src/Gui/SoAxisCrossKit.cpp b/src/Gui/SoAxisCrossKit.cpp index 6eed7457de..d05f856702 100644 --- a/src/Gui/SoAxisCrossKit.cpp +++ b/src/Gui/SoAxisCrossKit.cpp @@ -24,20 +24,34 @@ #include "PreCompiled.h" #ifndef _PreComp_ +# ifdef FC_OS_WIN32 +# include +# endif +# ifdef FC_OS_MACOSX +# include +# else +# include +# endif # include # include +# include +# include +# include +# include +# include +# include # include # include # include # include # include # include +# include # include # include +# include +# include # include -# include -# include -# include #endif @@ -217,3 +231,129 @@ SoAxisCrossKit::createAxes() set("zAxis.pickStyle", "style UNPICKABLE"); set("zHead.pickStyle", "style UNPICKABLE"); } + +// -------------------------------------------------------------- + +SO_NODE_SOURCE(SoRegPoint); + +void SoRegPoint::initClass() +{ + SO_NODE_INIT_CLASS(SoRegPoint, SoShape, "Shape"); +} + +SoRegPoint::SoRegPoint() +{ + SO_NODE_CONSTRUCTOR(SoRegPoint); + + SO_NODE_ADD_FIELD(base, (SbVec3f(0,0,0))); + SO_NODE_ADD_FIELD(normal, (SbVec3f(1,1,1))); + SO_NODE_ADD_FIELD(length, (3.0)); + SO_NODE_ADD_FIELD(color, (1.0f, 0.447059f, 0.337255f)); + SO_NODE_ADD_FIELD(text, ("")); + + root = new SoSeparator(); + root->ref(); + + // translation + SoTranslation* move = new SoTranslation(); + move->translation.setValue(base.getValue() + normal.getValue() * length.getValue()); + root->addChild(move); + + // sub-group + SoBaseColor* col = new SoBaseColor(); + col->rgb.setValue(this->color.getValue()); + + SoFontStyle* font = new SoFontStyle; + font->size = 14; + + SoSeparator* sub = new SoSeparator(); + sub->addChild(col); + sub->addChild(font); + sub->addChild(new SoText2()); + root->addChild(sub); +} + +SoRegPoint::~SoRegPoint() +{ + root->unref(); +} + +/** + * Renders the probe with text label and a bullet at the base point. + */ +void SoRegPoint::GLRender(SoGLRenderAction *action) +{ + if (shouldGLRender(action)) + { + SoState* state = action->getState(); + state->push(); + SoMaterialBundle mb(action); + SoTextureCoordinateBundle tb(action, TRUE, FALSE); + SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR); + mb.sendFirst(); // make sure we have the correct material + + SbVec3f p1 = base.getValue(); + SbVec3f p2 = p1 + normal.getValue() * length.getValue(); + + glLineWidth(1.0f); + glColor3fv(color.getValue().getValue()); + glBegin(GL_LINE_STRIP); + glVertex3d(p1[0], p1[1], p1[2]); + glVertex3d(p2[0], p2[1], p2[2]); + glEnd(); + glPointSize(5.0f); + glBegin(GL_POINTS); + glVertex3fv(p1.getValue()); + glEnd(); + glPointSize(2.0f); + glBegin(GL_POINTS); + glVertex3fv(p2.getValue()); + glEnd(); + + root->GLRender(action); + state->pop(); + } +} + +void SoRegPoint::generatePrimitives(SoAction* action) +{ +} + +/** + * Sets the bounding box of the probe to \a box and its center to \a center. + */ +void SoRegPoint::computeBBox(SoAction *action, SbBox3f &box, SbVec3f ¢er) +{ + root->doAction(action); + if (action->getTypeId().isDerivedFrom(SoGetBoundingBoxAction::getClassTypeId())) + static_cast(action)->resetCenter(); + + SbVec3f p1 = base.getValue(); + SbVec3f p2 = p1 + normal.getValue() * length.getValue(); + + box.extendBy(p1); + box.extendBy(p2); + + center = box.getCenter(); +} + +void SoRegPoint::notify(SoNotList * node) +{ + SoField * f = node->getLastField(); + if (f == &this->base || f == &this->normal || f == &this->length) { + SoTranslation* move = static_cast(root->getChild(0)); + move->translation.setValue(base.getValue() + normal.getValue() * length.getValue()); + } + else if (f == &this->color) { + SoSeparator* sub = static_cast(root->getChild(1)); + SoBaseColor* col = static_cast(sub->getChild(0)); + col->rgb = this->color.getValue(); + } + else if (f == &this->text) { + SoSeparator* sub = static_cast(root->getChild(1)); + SoText2* label = static_cast(sub->getChild(2)); + label->string = this->text.getValue(); + } + + SoShape::notify(node); +} diff --git a/src/Gui/SoAxisCrossKit.h b/src/Gui/SoAxisCrossKit.h index bb1a24934e..488c51ee82 100644 --- a/src/Gui/SoAxisCrossKit.h +++ b/src/Gui/SoAxisCrossKit.h @@ -27,6 +27,8 @@ #include #include #include +#include +#include class SbViewport; class SoState; @@ -84,6 +86,33 @@ private: virtual ~SoAxisCrossKit(); }; +class GuiExport SoRegPoint : public SoShape { + typedef SoShape inherited; + + SO_NODE_HEADER(SoRegPoint); + +public: + static void initClass(); + SoRegPoint(); + + void notify(SoNotList * node); + + SoSFVec3f base; + SoSFVec3f normal; + SoSFFloat length; + SoSFColor color; + SoSFString text; + +protected: + virtual ~SoRegPoint(); + virtual void GLRender(SoGLRenderAction *action); + virtual void computeBBox(SoAction *action, SbBox3f &box, SbVec3f ¢er); + virtual void generatePrimitives(SoAction *action); + +private: + SoSeparator* root; +}; + } // namespace Gui #endif // GUI_SOSHAPESCALE_H diff --git a/src/Gui/SoFCDB.cpp b/src/Gui/SoFCDB.cpp index 46435be068..ddf074ca11 100644 --- a/src/Gui/SoFCDB.cpp +++ b/src/Gui/SoFCDB.cpp @@ -95,6 +95,7 @@ void Gui::SoFCDB::init() TranslateManip ::initClass(); SoShapeScale ::initClass(); SoAxisCrossKit ::initClass(); + SoRegPoint ::initClass(); SoDrawingGrid ::initClass(); PropertyItem ::init(); diff --git a/src/Gui/SplitView3DInventor.cpp b/src/Gui/SplitView3DInventor.cpp index f80532af86..7014aaa77a 100644 --- a/src/Gui/SplitView3DInventor.cpp +++ b/src/Gui/SplitView3DInventor.cpp @@ -41,42 +41,29 @@ using namespace Gui; -TYPESYSTEM_SOURCE_ABSTRACT(Gui::SplitView3DInventor,Gui::MDIView); +TYPESYSTEM_SOURCE_ABSTRACT(Gui::AbstractSplitView,Gui::MDIView); -SplitView3DInventor::SplitView3DInventor(int views, Gui::Document* pcDocument, QWidget* parent, Qt::WFlags wflags) +AbstractSplitView::AbstractSplitView(Gui::Document* pcDocument, QWidget* parent, Qt::WFlags wflags) : MDIView(pcDocument,parent, wflags) { // important for highlighting setMouseTracking(true); - +} + +AbstractSplitView::~AbstractSplitView() +{ + hGrp->Detach(this); + for (std::vector::iterator it = _viewer.begin(); it != _viewer.end(); ++it) { + delete *it; + } +} + +void AbstractSplitView::setupSettings() +{ // attach Parameter Observer hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); hGrp->Attach(this); - QSplitter* mainSplitter=0; - - if (views <= 3) { - mainSplitter = new QSplitter(Qt::Horizontal, this); - _viewer.push_back(new View3DInventorViewer(mainSplitter)); - _viewer.push_back(new View3DInventorViewer(mainSplitter)); - if (views==3) - _viewer.push_back(new View3DInventorViewer(mainSplitter)); - } - else { - mainSplitter = new QSplitter(Qt::Vertical, this); - QSplitter *topSplitter = new QSplitter(Qt::Horizontal, mainSplitter); - QSplitter *botSplitter = new QSplitter(Qt::Horizontal, mainSplitter); - _viewer.push_back(new View3DInventorViewer(topSplitter)); - _viewer.push_back(new View3DInventorViewer(topSplitter)); - for (int i=2;isetOpaqueResize( true ); - botSplitter->setOpaqueResize( true ); - } - - mainSplitter->show(); - setCentralWidget(mainSplitter); - // apply the user settings OnChange(*hGrp,"EyeDistance"); OnChange(*hGrp,"CornerCoordSystem"); @@ -100,21 +87,13 @@ SplitView3DInventor::SplitView3DInventor(int views, Gui::Document* pcDocument, Q OnChange(*hGrp,"NavigationStyle"); } -SplitView3DInventor::~SplitView3DInventor() -{ - hGrp->Detach(this); - for (std::vector::iterator it = _viewer.begin(); it != _viewer.end(); ++it) { - delete *it; - } -} - -View3DInventorViewer* SplitView3DInventor::getViewer(unsigned int n) const +View3DInventorViewer* AbstractSplitView::getViewer(unsigned int n) const { return (_viewer.size() > n ? _viewer[n] : 0); } /// Observer message from the ParameterGrp -void SplitView3DInventor::OnChange(ParameterGrp::SubjectType &rCaller,ParameterGrp::MessageType Reason) +void AbstractSplitView::OnChange(ParameterGrp::SubjectType &rCaller,ParameterGrp::MessageType Reason) { const ParameterGrp& rGrp = static_cast(rCaller); if (strcmp(Reason,"HeadlightColor") == 0) { @@ -266,17 +245,17 @@ void SplitView3DInventor::OnChange(ParameterGrp::SubjectType &rCaller,ParameterG } } -void SplitView3DInventor::onUpdate(void) +void AbstractSplitView::onUpdate(void) { update(); } -const char *SplitView3DInventor::getName(void) const +const char *AbstractSplitView::getName(void) const { return "SplitView3DInventor"; } -bool SplitView3DInventor::onMsg(const char* pMsg, const char** ppReturn) +bool AbstractSplitView::onMsg(const char* pMsg, const char** ppReturn) { if (strcmp("ViewFit",pMsg) == 0 ) { for (std::vector::iterator it = _viewer.begin(); it != _viewer.end(); ++it) @@ -346,7 +325,7 @@ bool SplitView3DInventor::onMsg(const char* pMsg, const char** ppReturn) return false; } -bool SplitView3DInventor::onHasMsg(const char* pMsg) const +bool AbstractSplitView::onHasMsg(const char* pMsg) const { if (strcmp("ViewFit",pMsg) == 0) { return true; @@ -375,7 +354,46 @@ bool SplitView3DInventor::onHasMsg(const char* pMsg) const return false; } -void SplitView3DInventor::setCursor(const QCursor& aCursor) +void AbstractSplitView::setCursor(const QCursor& aCursor) { //_viewer->getWidget()->setCursor(aCursor); } + +// ------------------------------------------------------ + +TYPESYSTEM_SOURCE_ABSTRACT(Gui::SplitView3DInventor, Gui::AbstractSplitView); + +SplitView3DInventor::SplitView3DInventor(int views, Gui::Document* pcDocument, QWidget* parent, Qt::WFlags wflags) + : AbstractSplitView(pcDocument,parent, wflags) +{ + QSplitter* mainSplitter=0; + + if (views <= 3) { + mainSplitter = new QSplitter(Qt::Horizontal, this); + _viewer.push_back(new View3DInventorViewer(mainSplitter)); + _viewer.push_back(new View3DInventorViewer(mainSplitter)); + if (views==3) + _viewer.push_back(new View3DInventorViewer(mainSplitter)); + } + else { + mainSplitter = new QSplitter(Qt::Vertical, this); + QSplitter *topSplitter = new QSplitter(Qt::Horizontal, mainSplitter); + QSplitter *botSplitter = new QSplitter(Qt::Horizontal, mainSplitter); + _viewer.push_back(new View3DInventorViewer(topSplitter)); + _viewer.push_back(new View3DInventorViewer(topSplitter)); + for (int i=2;isetOpaqueResize( true ); + botSplitter->setOpaqueResize( true ); + } + + mainSplitter->show(); + setCentralWidget(mainSplitter); + + // apply the user settings + setupSettings(); +} + +SplitView3DInventor::~SplitView3DInventor() +{ +} diff --git a/src/Gui/SplitView3DInventor.h b/src/Gui/SplitView3DInventor.h index c4596e9a95..de7254ec49 100644 --- a/src/Gui/SplitView3DInventor.h +++ b/src/Gui/SplitView3DInventor.h @@ -36,13 +36,13 @@ class View3DInventorViewer; /** The SplitView3DInventor class allows to create a window with two or more Inventor views. * \author Werner Mayer */ -class GuiExport SplitView3DInventor : public MDIView,public ParameterGrp::ObserverType +class GuiExport AbstractSplitView : public MDIView, public ParameterGrp::ObserverType { TYPESYSTEM_HEADER(); public: - SplitView3DInventor(int views, Gui::Document* pcDocument, QWidget* parent, Qt::WFlags wflags=0); - ~SplitView3DInventor(); + AbstractSplitView(Gui::Document* pcDocument, QWidget* parent, Qt::WFlags wflags=0); + ~AbstractSplitView(); virtual const char *getName(void) const; @@ -56,14 +56,27 @@ public: void setCursor(const QCursor&); +protected: + void setupSettings(); + protected: /// handle to the viewer parameter group ParameterGrp::handle hGrp; - -private: std::vector _viewer; }; +/** The SplitView3DInventor class allows to create a window with two or more Inventor views. + * \author Werner Mayer + */ +class GuiExport SplitView3DInventor : public AbstractSplitView +{ + TYPESYSTEM_HEADER(); + +public: + SplitView3DInventor(int views, Gui::Document* pcDocument, QWidget* parent, Qt::WFlags wflags=0); + ~SplitView3DInventor(); +}; + } // namespace Gui #endif //GUI_SPLITVIEW3DINVENTOR_H diff --git a/src/Gui/Workbench.cpp b/src/Gui/Workbench.cpp index d6b82d4abe..dc32d7dc7c 100644 --- a/src/Gui/Workbench.cpp +++ b/src/Gui/Workbench.cpp @@ -441,7 +441,8 @@ MenuItem* StdWorkbench::setupMenuBar() const edit->setCommand("&Edit"); *edit << "Std_Undo" << "Std_Redo" << "Separator" << "Std_Cut" << "Std_Copy" << "Std_Paste" << "Std_DuplicateSelection" << "Separator" - << "Std_Refresh" << "Std_SelectAll" << "Std_Delete" << "Std_Placement" + << "Std_Refresh" << "Std_SelectAll" << "Std_Delete" + << "Std_Placement" << "Std_Alignment" << "Std_Edit" << "Separator" << "Std_DlgPreferences"; // Standard views diff --git a/src/Mod/Complete/Gui/Workbench.cpp b/src/Mod/Complete/Gui/Workbench.cpp index 45a2faa030..80a0336ddf 100644 --- a/src/Mod/Complete/Gui/Workbench.cpp +++ b/src/Mod/Complete/Gui/Workbench.cpp @@ -132,7 +132,8 @@ Gui::MenuItem* Workbench::setupMenuBar() const edit->setCommand("&Edit"); *edit << "Std_Undo" << "Std_Redo" << "Separator" << "Std_Cut" << "Std_Copy" << "Std_Paste" << "Std_DuplicateSelection" << "Separator" - << "Std_Refresh" << "Std_SelectAll" << "Std_Delete" << "Std_Placement" + << "Std_Refresh" << "Std_SelectAll" << "Std_Delete" + << "Std_Placement" << "Std_Alignment" << "Separator" << "Std_DlgPreferences"; // Standard views