24
#include "PreCompiled.h"
27
# include <QApplication>
30
# include <QMessageBox>
34
# include <QVBoxLayout>
35
# include <Inventor/SoPickedPoint.h>
36
# include <Inventor/actions/SoSearchAction.h>
37
# include <Inventor/events/SoMouseButtonEvent.h>
38
# include <Inventor/fields/SoSFImage.h>
39
# include <Inventor/nodes/SoImage.h>
40
# include <Inventor/nodes/SoMaterial.h>
41
# include <Inventor/nodes/SoOrthographicCamera.h>
42
# include <Inventor/nodes/SoSeparator.h>
43
# include <Inventor/nodes/SoTranslation.h>
44
# include <Inventor/sensors/SoNodeSensor.h>
47
#include <App/Document.h>
48
#include <App/GeoFeature.h>
49
#include <Gui/Application.h>
50
#include <Gui/Document.h>
51
#include <Gui/MainWindow.h>
52
#include <Gui/Selection.h>
53
#include <Gui/SplitView3DInventor.h>
54
#include <Gui/View3DInventorViewer.h>
55
#include <Gui/ViewProviderGeometryObject.h>
56
#include <Gui/WaitCursor.h>
58
#include "ManualAlignment.h"
59
#include "BitmapFactory.h"
60
#include "SoAxisCrossKit.h"
65
namespace sp = std::placeholders;
67
AlignmentGroup::AlignmentGroup() = default;
69
AlignmentGroup::~AlignmentGroup() = default;
71
void AlignmentGroup::addView(App::DocumentObject* pView)
74
App::Document* rDoc = pView->getDocument();
75
Gui::Document* pDoc = Gui::Application::Instance->getDocument(rDoc);
76
auto pProvider = static_cast<Gui::ViewProviderDocumentObject*>
77
(pDoc->getViewProvider(pView));
78
this->_views.push_back(pProvider);
82
std::vector<App::DocumentObject*> AlignmentGroup::getViews() const
84
std::vector<App::DocumentObject*> views;
86
std::vector<Gui::ViewProviderDocumentObject*>::const_iterator it;
87
for (it = this->_views.begin(); it != this->_views.end(); ++it) {
88
App::DocumentObject* pView = (*it)->getObject();
89
views.push_back(pView);
95
bool AlignmentGroup::hasView(Gui::ViewProviderDocumentObject* pView) const
97
std::vector<Gui::ViewProviderDocumentObject*>::const_iterator it;
98
for (it = this->_views.begin(); it != this->_views.end(); ++it) {
106
void AlignmentGroup::removeView(Gui::ViewProviderDocumentObject* pView)
108
std::vector<Gui::ViewProviderDocumentObject*>::iterator it;
109
for (it = this->_views.begin(); it != this->_views.end(); ++it) {
111
this->_views.erase(it);
117
void AlignmentGroup::addToViewer(Gui::View3DInventorViewer* viewer) const
119
std::vector<Gui::ViewProviderDocumentObject*>::const_iterator it;
120
for (it = this->_views.begin(); it != this->_views.end(); ++it)
121
viewer->addViewProvider(*it);
126
void AlignmentGroup::removeFromViewer(Gui::View3DInventorViewer* viewer) const
128
std::vector<Gui::ViewProviderDocumentObject*>::const_iterator it;
129
for (it = this->_views.begin(); it != this->_views.end(); ++it)
130
viewer->removeViewProvider(*it);
133
void AlignmentGroup::setRandomColor()
135
std::vector<Gui::ViewProviderDocumentObject*>::iterator it;
136
for (it = this->_views.begin(); it != this->_views.end(); ++it) {
138
float g = (float)rand()/(float)RAND_MAX;
139
float b = (float)rand()/(float)RAND_MAX;
140
if ((*it)->isDerivedFrom(Gui::ViewProviderGeometryObject::getClassTypeId())) {
141
SoSearchAction searchAction;
142
searchAction.setType(SoMaterial::getClassTypeId());
143
searchAction.setInterest(SoSearchAction::FIRST);
144
searchAction.apply((*it)->getRoot());
145
SoPath* selectionPath = searchAction.getPath();
148
auto material = static_cast<SoMaterial*>(selectionPath->getTail());
149
material->diffuseColor.setValue(r, g, b);
155
Gui::Document* AlignmentGroup::getDocument() const
157
if (this->_views.empty())
159
App::DocumentObject* pView = this->_views[0]->getObject();
161
App::Document* rDoc = pView->getDocument();
162
Gui::Document* pDoc = Gui::Application::Instance->getDocument(rDoc);
169
void AlignmentGroup::addPoint(const PickedPoint& pnt)
171
this->_pickedPoints.push_back(pnt);
174
void AlignmentGroup::removeLastPoint()
176
this->_pickedPoints.pop_back();
179
int AlignmentGroup::countPoints() const
181
return this->_pickedPoints.size();
184
const std::vector<PickedPoint>& AlignmentGroup::getPoints() const
186
return this->_pickedPoints;
189
void AlignmentGroup::clearPoints()
191
this->_pickedPoints.clear();
194
void AlignmentGroup::setAlignable(bool align)
196
std::vector<Gui::ViewProviderDocumentObject*>::iterator it;
197
for (it = this->_views.begin(); it != this->_views.end(); ++it) {
198
auto pAlignMode = dynamic_cast<App::PropertyBool*>((*it)->getPropertyByName("AlignMode"));
200
pAlignMode->setValue(align);
205
dynamic_cast<App::PropertyMaterial*>((*it)->getPropertyByName("ShapeAppearance"));
207
pAppearance->touch();
213
void AlignmentGroup::moveTo(AlignmentGroup& that)
215
std::vector<Gui::ViewProviderDocumentObject*>::iterator it;
216
for (it = this->_views.begin(); it != this->_views.end(); ++it)
217
that._views.push_back(*it);
219
this->_views.clear();
222
void AlignmentGroup::clear()
224
this->_views.clear();
225
this->_pickedPoints.clear();
228
bool AlignmentGroup::isEmpty() const
230
return this->_views.empty();
233
int AlignmentGroup::count() const
235
return this->_views.size();
238
Base::BoundBox3d AlignmentGroup::getBoundingBox() const
240
Base::BoundBox3d box;
241
std::vector<Gui::ViewProviderDocumentObject*>::const_iterator it;
242
for (it = this->_views.begin(); it != this->_views.end(); ++it) {
243
if ((*it)->isDerivedFrom(Gui::ViewProviderGeometryObject::getClassTypeId())) {
244
auto geo = static_cast<App::GeoFeature*>((*it)->getObject());
245
const App::PropertyComplexGeoData* prop = geo->getPropertyOfGeometry();
247
box.Add(prop->getBoundingBox());
255
MovableGroup::MovableGroup() = default;
257
MovableGroup::~MovableGroup() = default;
261
FixedGroup::FixedGroup() = default;
263
FixedGroup::~FixedGroup() = default;
267
MovableGroupModel::MovableGroupModel() = default;
269
MovableGroupModel::~MovableGroupModel() = default;
271
void MovableGroupModel::addGroup(const MovableGroup& grp)
273
this->_groups.push_back(grp);
276
void MovableGroupModel::addGroups(const std::map<int, MovableGroup>& grps)
278
for (const auto & grp : grps)
279
this->_groups.push_back(grp.second);
282
void MovableGroupModel::removeActiveGroup()
284
this->_groups.erase(this->_groups.begin());
287
MovableGroup& MovableGroupModel::activeGroup()
290
if (this->_groups.empty())
291
throw Base::RuntimeError("Empty group");
292
return *(this->_groups.begin());
295
const MovableGroup& MovableGroupModel::activeGroup() const
298
if (this->_groups.empty())
299
throw Base::RuntimeError("Empty group");
300
return this->_groups.front();
303
void MovableGroupModel::continueAlignment()
309
void MovableGroupModel::clear()
311
this->_groups.clear();
314
bool MovableGroupModel::isEmpty() const
316
return this->_groups.empty();
319
int MovableGroupModel::count() const
321
return this->_groups.size();
324
const MovableGroup& MovableGroupModel::getGroup(int i) const
327
throw Base::IndexError("Index out of range");
328
return this->_groups[i];
331
Base::BoundBox3d MovableGroupModel::getBoundingBox() const
333
Base::BoundBox3d box;
334
std::vector<MovableGroup>::const_iterator it;
335
for (it = this->_groups.begin(); it != this->_groups.end(); ++it) {
336
box.Add(it->getBoundingBox());
344
class AlignmentView : public Gui::AbstractSplitView
349
AlignmentView(Gui::Document* pcDocument, QWidget* parent, Qt::WindowFlags wflags=Qt::WindowFlags())
350
: AbstractSplitView(pcDocument, parent, wflags)
353
bool smoothing = false;
354
bool glformat = false;
355
int samples = View3DInventorViewer::getNumSamples();
360
f.setSamples(samples);
362
else if (samples > 0) {
366
QSplitter* mainSplitter=nullptr;
367
mainSplitter = new QSplitter(Qt::Horizontal, this);
369
_viewer.push_back(new View3DInventorViewer(f, mainSplitter));
370
_viewer.push_back(new View3DInventorViewer(f, mainSplitter));
373
_viewer.push_back(new View3DInventorViewer(mainSplitter));
374
_viewer.push_back(new View3DInventorViewer(mainSplitter));
376
setDocumentOfViewers(pcDocument);
378
auto vbox = new QFrame(this);
379
auto layout = new QVBoxLayout();
380
layout->setContentsMargins(0, 0, 0, 0);
381
layout->setSpacing(0);
382
vbox->setLayout(layout);
384
myLabel = new QLabel(this);
385
myLabel->setAutoFillBackground(true);
386
QPalette pal = myLabel->palette();
387
pal.setColor(QPalette::Window, Qt::darkGray);
388
pal.setColor(QPalette::WindowText, Qt::white);
389
myLabel->setPalette(pal);
390
mainSplitter->setPalette(pal);
391
myLabel->setAlignment(Qt::AlignCenter);
392
myLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
393
QFont font = myLabel->font();
394
font.setPointSize(14);
395
myLabel->setFont(font);
396
layout->addWidget(myLabel);
397
layout->addWidget(mainSplitter);
400
setCentralWidget(vbox);
406
for (const auto & i : _viewer)
407
i->getSoRenderManager()->getGLRenderAction()->setSmoothing(true);
410
static_cast<SoGroup*>(getViewer(0)->getSoRenderManager()->getSceneGraph())->
411
addChild(setupHeadUpDisplay(tr("Movable object")));
412
static_cast<SoGroup*>(getViewer(1)->getSoRenderManager()->getSceneGraph())->
413
addChild(setupHeadUpDisplay(tr("Fixed object")));
415
~AlignmentView() override = default;
416
PyObject* getPyObject() override
420
bool canClose() override
424
SoNode* setupHeadUpDisplay(const QString& text) const
426
auto hudRoot = new SoSeparator;
429
auto hudCam = new SoOrthographicCamera();
430
hudCam->viewportMapping = SoCamera::LEAVE_ALONE;
435
auto hudTrans = new SoTranslation;
436
hudTrans->translation.setValue(-0.95f, -0.95f, 0.0f);
438
QFont font = this->font();
439
font.setPointSize(24);
440
QFontMetrics fm(font);
443
front.setRgbF(0.8f, 0.8f, 0.8f);
445
int w = QtTools::horizontalAdvance(fm, text);
448
QImage image(w,h,QImage::Format_ARGB32_Premultiplied);
449
image.fill(0x00000000);
450
QPainter painter(&image);
451
painter.setRenderHint(QPainter::Antialiasing);
452
painter.setPen(front);
453
painter.setFont(font);
454
painter.drawText(0,0,w,h,Qt::AlignLeft,text);
457
Gui::BitmapFactory().convert(image, sfimage);
458
auto hudImage = new SoImage();
459
hudImage->image = sfimage;
463
hudRoot->addChild(hudCam);
464
hudRoot->addChild(hudTrans);
465
hudRoot->addChild(hudImage);
472
class ManualAlignment::Private {
474
SoSeparator * picksepLeft;
475
SoSeparator * picksepRight;
476
SoNodeSensor* sensorCam1{nullptr};
477
SoNodeSensor* sensorCam2{nullptr};
478
SbRotation rot_cam1, rot_cam2;
479
SbVec3f pos_cam1, pos_cam2;
484
picksepLeft = new SoSeparator;
487
picksepRight = new SoSeparator;
492
picksepLeft->unref();
493
picksepRight->unref();
499
void reorientCamera(SoCamera * cam, const SbRotation & rot)
506
cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
507
SbVec3f focalpoint = cam->position.getValue() +
508
cam->focalDistance.getValue() * direction;
511
cam->orientation = rot * cam->orientation.getValue();
514
cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
515
cam->position = focalpoint - cam->focalDistance.getValue() * direction;
519
void copyCameraSettings(SoCamera* cam1, SbRotation& rot_cam1, SbVec3f& pos_cam1,
520
SoCamera* cam2, SbRotation& rot_cam2, SbVec3f& pos_cam2)
525
SbRotation rot = cam1->orientation.getValue();
526
SbRotation dif = rot * rot_cam1.inverse();
530
cam2->enableNotify(false);
531
cam2->nearDistance = cam1->nearDistance;
532
cam2->farDistance = cam1->farDistance;
533
cam2->focalDistance = cam1->focalDistance;
534
reorientCamera(cam2,dif);
535
rot_cam2 = cam2->orientation.getValue();
538
SbVec3f pos = cam1->position.getValue();
539
SbVec3f difpos = pos - pos_cam1;
542
cam1->orientation.getValue().inverse().multVec(difpos,difpos);
544
cam2->orientation.getValue().multVec(difpos,difpos);
545
cam2->position.setValue(cam2->position.getValue()+difpos);
547
if (cam1->getTypeId() == cam2->getTypeId()) {
548
if (cam1->getTypeId() == SoOrthographicCamera::getClassTypeId())
549
static_cast<SoOrthographicCamera*>(cam2)->height =
550
static_cast<SoOrthographicCamera*>(cam1)->height;
553
cam2->enableNotify(true);
556
void syncCameraCB(void * data, SoSensor * s)
558
auto self = static_cast<ManualAlignment*>(data);
561
SoCamera* cam1 = self->myViewer->getViewer(0)->getSoRenderManager()->getCamera();
562
SoCamera* cam2 = self->myViewer->getViewer(1)->getSoRenderManager()->getCamera();
565
auto sensor = static_cast<SoNodeSensor*>(s);
566
SoNode* node = sensor->getAttachedNode();
567
if (node && node->getTypeId().isDerivedFrom(SoCamera::getClassTypeId())) {
569
Private::copyCameraSettings(cam1, self->d->rot_cam1, self->d->pos_cam1,
570
cam2, self->d->rot_cam2, self->d->pos_cam2);
571
self->myViewer->getViewer(1)->redraw();
573
else if (node == cam2) {
574
Private::copyCameraSettings(cam2, self->d->rot_cam2, self->d->pos_cam2,
575
cam1, self->d->rot_cam1, self->d->pos_cam1);
576
self->myViewer->getViewer(0)->redraw();
581
static Base::Placement
582
transformation2x2(const Base::Vector3d& plane1_base,
583
const Base::Vector3d& plane1_xaxis,
584
const Base::Vector3d& plane2_base,
585
const Base::Vector3d& plane2_xaxis)
590
Base::Rotation rot(plane1_xaxis, plane2_xaxis);
592
Base::Vector3d pln_base;
593
rot.multVec(plane1_base,pln_base);
594
Base::Vector3d dif = plane2_base - pln_base;
598
static Base::Placement
599
transformation3x3(const Base::Vector3d& plane1_base,
600
const Base::Vector3d& plane1_zaxis,
601
const Base::Vector3d& plane1_xaxis,
602
const Base::Vector3d& plane2_base,
603
const Base::Vector3d& plane2_zaxis,
604
const Base::Vector3d& plane2_xaxis)
609
Base::Rotation rot(plane1_zaxis, plane2_zaxis);
612
Base::Vector3d dif1 = plane1_base;
613
rot.multVec(dif1,dif1);
614
dif1 = plane2_base - dif1;
615
Base::Placement plm1(dif1, rot);
618
Base::Vector3d pln_xaxis;
619
rot.multVec(plane1_xaxis,pln_xaxis);
620
Base::Rotation rot2(pln_xaxis, plane2_xaxis);
621
Base::Vector3d dif2 = plane2_base;
622
rot2.multVec(dif2,dif2);
623
dif2 = plane2_base - dif2;
624
Base::Placement plm2(dif2, rot2);
632
ManualAlignment* ManualAlignment::_instance = nullptr;
637
ManualAlignment::ManualAlignment()
638
: myViewer(nullptr), myDocument(nullptr), myPickPoints(3), d(new Private)
642
this->connectApplicationDeletedDocument = Gui::Application::Instance->signalDeleteDocument
643
.connect(std::bind(&ManualAlignment::slotDeletedDocument, this, sp::_1));
647
d->sensorCam1 = new SoNodeSensor(Private::syncCameraCB, this);
648
d->sensorCam2 = new SoNodeSensor(Private::syncCameraCB, this);
654
ManualAlignment::~ManualAlignment()
656
this->connectDocumentDeletedObject.disconnect();
657
this->connectApplicationDeletedDocument.disconnect();
666
ManualAlignment* ManualAlignment::instance()
670
_instance = new ManualAlignment();
677
void ManualAlignment::destruct()
680
ManualAlignment* tmp = _instance;
689
bool ManualAlignment::hasInstance()
691
return _instance != nullptr;
694
void ManualAlignment::setMinPoints(int minPoints)
696
if ((minPoints > 0) && (minPoints <= 3))
697
myPickPoints = minPoints;
700
void ManualAlignment::setFixedGroup(const FixedGroup& fixed)
702
this->myFixedGroup = fixed;
703
this->myDocument = fixed.getDocument();
706
void ManualAlignment::setModel(const MovableGroupModel& model)
708
this->myAlignModel = model;
711
void ManualAlignment::clearAll()
713
myFixedGroup.clear();
714
myAlignModel.clear();
715
myDocument = nullptr;
718
void ManualAlignment::setViewingDirections(const Base::Vector3d& view1, const Base::Vector3d& up1,
719
const Base::Vector3d& view2, const Base::Vector3d& up2)
721
if (myViewer.isNull())
725
SbVec3f vz(-view1.x, -view1.y, -view1.z);
727
SbVec3f vy(up1.x, up1.y, up1.z);
729
SbVec3f vx = vy.cross(vz);
732
SbMatrix rot = SbMatrix::identity();
745
SbRotation total(rot);
746
myViewer->getViewer(0)->getSoRenderManager()->getCamera()->orientation.setValue(total);
747
myViewer->getViewer(0)->viewAll();
751
SbVec3f vz(-view2.x, -view2.y, -view2.z);
753
SbVec3f vy(up2.x, up2.y, up2.z);
755
SbVec3f vx = vy.cross(vz);
758
SbMatrix rot = SbMatrix::identity();
771
SbRotation total(rot);
772
myViewer->getViewer(1)->getSoRenderManager()->getCamera()->orientation.setValue(total);
773
myViewer->getViewer(1)->viewAll();
780
void ManualAlignment::startAlignment(Base::Type mousemodel)
783
if (!myViewer.isNull()) {
784
QMessageBox::warning(qApp->activeWindow(), tr("Manual alignment"), tr("The alignment is already in progress."));
788
myTransform = Base::Placement();
790
if (myFixedGroup.isEmpty())
792
if (myAlignModel.isEmpty())
796
myViewer = new AlignmentView(myDocument,Gui::getMainWindow());
797
myViewer->setWindowTitle(tr("Alignment[*]"));
798
myViewer->setWindowIcon(QApplication::windowIcon());
799
myViewer->resize(400, 300);
800
Gui::getMainWindow()->addWindow(myViewer);
801
myViewer->showMaximized();
802
int n = this->myPickPoints;
804
? tr("Please, select at least one point in the left and the right view")
805
: tr("Please, select at least %1 points in the left and the right view").arg(n);
806
myViewer->myLabel->setText(msg);
808
connect(myViewer, &QObject::destroyed, this, &ManualAlignment::reset);
811
myFixedGroup.addToViewer(myViewer->getViewer(1));
812
myFixedGroup.setAlignable(true);
815
SoNode* node1 = myViewer->getViewer(0)->getSceneGraph();
816
if (node1->getTypeId().isDerivedFrom(SoGroup::getClassTypeId())){
817
((SoGroup*)node1)->addChild(d->picksepLeft);
819
SoNode* node2 = myViewer->getViewer(1)->getSceneGraph();
820
if (node2->getTypeId().isDerivedFrom(SoGroup::getClassTypeId())){
821
((SoGroup*)node2)->addChild(d->picksepRight);
824
myViewer->getViewer(0)->setEditing(true);
825
myViewer->getViewer(0)->addEventCallback(SoMouseButtonEvent::getClassTypeId(),
826
ManualAlignment::probePickedCallback);
827
myViewer->getViewer(1)->setEditing(true);
828
myViewer->getViewer(1)->addEventCallback(SoMouseButtonEvent::getClassTypeId(),
829
ManualAlignment::probePickedCallback);
831
myViewer->getViewer(0)->setNavigationType(mousemodel);
832
myViewer->getViewer(1)->setNavigationType(mousemodel);
835
if (this->connectDocumentDeletedObject.connected())
836
this->connectDocumentDeletedObject.disconnect();
838
this->connectDocumentDeletedObject = myDocument->signalDeletedObject.connect(std::bind
839
(&ManualAlignment::slotDeletedObject, this, sp::_1));
848
void ManualAlignment::continueAlignment()
850
myFixedGroup.clearPoints();
851
coinRemoveAllChildren(d->picksepLeft);
852
coinRemoveAllChildren(d->picksepRight);
854
if (!myAlignModel.isEmpty()) {
855
AlignmentGroup& grp = myAlignModel.activeGroup();
857
grp.addToViewer(myViewer->getViewer(0));
858
grp.setAlignable(true);
860
Gui::getMainWindow()->showMessage(tr("Please pick points in the left and right view"));
862
myViewer->getViewer(0)->setEditingCursor(QCursor(Qt::PointingHandCursor));
863
myViewer->getViewer(1)->setEditingCursor(QCursor(Qt::PointingHandCursor));
870
void ManualAlignment::closeViewer()
875
if (myViewer->parentWidget())
876
myViewer->parentWidget()->deleteLater();
883
void ManualAlignment::reset()
885
if (!myAlignModel.isEmpty()) {
886
myAlignModel.activeGroup().setAlignable(false);
887
myAlignModel.activeGroup().clear();
888
myAlignModel.clear();
891
myFixedGroup.setAlignable(false);
892
myFixedGroup.clear();
894
coinRemoveAllChildren(d->picksepLeft);
895
coinRemoveAllChildren(d->picksepRight);
898
this->connectDocumentDeletedObject.disconnect();
899
myDocument = nullptr;
906
void ManualAlignment::finish()
908
if (myViewer.isNull())
912
myDocument->getDocument()->recompute();
916
Gui::getMainWindow()->showMessage(tr("The alignment has finished"));
919
Q_EMIT emitFinished();
925
void ManualAlignment::cancel()
927
if (myViewer.isNull())
931
myTransform = Base::Placement();
934
Gui::getMainWindow()->showMessage(tr("The alignment has been canceled"));
937
Q_EMIT emitCanceled();
940
void ManualAlignment::align()
943
if (myAlignModel.activeGroup().countPoints() < myPickPoints) {
944
QMessageBox::warning(myViewer, tr("Manual alignment"),
945
tr("Too few points picked in the left view."
946
" At least %1 points are needed.").arg(myPickPoints));
948
else if (myFixedGroup.countPoints() < myPickPoints) {
949
QMessageBox::warning(myViewer, tr("Manual alignment"),
950
tr("Too few points picked in the right view."
951
" At least %1 points are needed.").arg(myPickPoints));
953
else if (myAlignModel.activeGroup().countPoints() != myFixedGroup.countPoints()) {
954
QMessageBox::warning(myViewer, tr("Manual alignment"),
955
tr("Different number of points picked in left and right view.\n"
956
"On the left view %1 points are picked,\n"
957
"on the right view %2 points are picked.")
958
.arg(myAlignModel.activeGroup().countPoints())
959
.arg(myFixedGroup.countPoints()));
963
myAlignModel.activeGroup().removeFromViewer(myViewer->getViewer(0));
964
myAlignModel.activeGroup().setAlignable(false);
965
std::vector<App::DocumentObject*> pViews = myAlignModel.activeGroup().getViews();
966
Gui::getMainWindow()->showMessage(tr("Try to align group of views"));
969
bool ok = computeAlignment(myAlignModel.activeGroup().getPoints(), myFixedGroup.getPoints());
970
if (ok && myDocument) {
972
myDocument->openCommand(QT_TRANSLATE_NOOP("Command", "Align"));
973
for (const auto & pView : pViews)
975
myDocument->commitCommand();
979
myAlignModel.activeGroup().setAlignable(true);
980
myAlignModel.activeGroup().addToViewer(myViewer->getViewer(1));
981
myAlignModel.activeGroup().moveTo(myFixedGroup);
982
myAlignModel.continueAlignment();
986
auto ret = QMessageBox::critical(myViewer, tr("Manual alignment"),
987
tr("The alignment failed.\nHow do you want to proceed?"),
988
QMessageBox::Retry | QMessageBox::Ignore | QMessageBox::Abort);
989
if ( ret == QMessageBox::Ignore ) {
990
myAlignModel.continueAlignment();
992
else if ( ret == QMessageBox::Abort ) {
1002
void ManualAlignment::showInstructions()
1005
if (myAlignModel.activeGroup().countPoints() < myPickPoints) {
1006
Gui::getMainWindow()->showMessage(
1007
tr("Too few points picked in the left view."
1008
" At least %1 points are needed.").arg(myPickPoints));
1010
else if (myFixedGroup.countPoints() < myPickPoints) {
1011
Gui::getMainWindow()->showMessage(
1012
tr("Too few points picked in the right view."
1013
" At least %1 points are needed.").arg(myPickPoints));
1015
else if (myAlignModel.activeGroup().countPoints() != myFixedGroup.countPoints()) {
1016
Gui::getMainWindow()->showMessage(
1017
tr("Different number of points picked in left and right view. "
1018
"On the left view %1 points are picked, "
1019
"on the right view %2 points are picked.")
1020
.arg(myAlignModel.activeGroup().countPoints())
1021
.arg(myFixedGroup.countPoints()));
1025
bool ManualAlignment::canAlign() const
1027
if (myAlignModel.activeGroup().countPoints() == myFixedGroup.countPoints()) {
1028
if (myFixedGroup.countPoints() >= myPickPoints)
1039
bool ManualAlignment::computeAlignment(const std::vector<PickedPoint>& movPts,
1040
const std::vector<PickedPoint>& fixPts)
1042
assert((int)movPts.size() >= myPickPoints);
1043
assert((int)fixPts.size() >= myPickPoints);
1044
assert((int)movPts.size() == (int)fixPts.size());
1045
myTransform = Base::Placement();
1047
if (movPts.size() == 1) {
1049
myTransform.setPosition(fixPts[0].point - movPts[0].point);
1051
else if (movPts.size() == 2) {
1052
const Base::Vector3d& p1 = movPts[0].point;
1053
const Base::Vector3d& p2 = movPts[1].point;
1054
Base::Vector3d d1 = p2-p1;
1057
const Base::Vector3d& q1 = fixPts[0].point;
1058
const Base::Vector3d& q2 = fixPts[1].point;
1059
Base::Vector3d d2 = q2-q1;
1062
myTransform = Private::transformation2x2(p1, d1, q1, d2);
1064
else if (movPts.size() >= 3) {
1065
const Base::Vector3d& p1 = movPts[0].point;
1066
const Base::Vector3d& p2 = movPts[1].point;
1067
const Base::Vector3d& p3 = movPts[2].point;
1068
Base::Vector3d d1 = p2-p1;
1070
Base::Vector3d n1 = (p2-p1) % (p3-p1);
1073
const Base::Vector3d& q1 = fixPts[0].point;
1074
const Base::Vector3d& q2 = fixPts[1].point;
1075
const Base::Vector3d& q3 = fixPts[2].point;
1076
Base::Vector3d d2 = q2-q1;
1078
Base::Vector3d n2 = (q2-q1) % (q3-q1);
1081
myTransform = Private::transformation3x3(p1, d1, n1, q1, d2, n2);
1090
void ManualAlignment::alignObject(App::DocumentObject *obj)
1092
if (obj->isDerivedFrom<App::GeoFeature>()) {
1093
auto geom = static_cast<App::GeoFeature*>(obj);
1094
geom->transformPlacement(this->myTransform);
1101
SoNode* ManualAlignment::pickedPointsSubGraph(const SbVec3f& p, const SbVec3f& n, int id)
1103
static const float color_table [10][3] = {
1116
int index = (id-1) % 10;
1118
auto probe = new SoRegPoint();
1119
probe->base.setValue(p);
1120
probe->normal.setValue(n);
1121
probe->color.setValue(color_table[index][0],color_table[index][1],color_table[index][2]);
1122
SbString s(tr("Point_%1").arg(id).toStdString().c_str());
1123
probe->text.setValue(s);
1130
void ManualAlignment::slotDeletedDocument(const Gui::Document& Doc)
1132
if (&Doc == this->myDocument)
1139
void ManualAlignment::slotDeletedObject(const Gui::ViewProvider& Obj)
1142
if (Obj.isDerivedFrom<Gui::ViewProviderDocumentObject>()) {
1145
auto vp = const_cast<Gui::ViewProviderDocumentObject*>
1146
(static_cast<const Gui::ViewProviderDocumentObject*>(&Obj));
1147
if (myAlignModel.activeGroup().hasView(vp)) {
1148
myViewer->getViewer(0)->removeViewProvider(vp);
1151
if (myFixedGroup.hasView(vp)) {
1152
myViewer->getViewer(1)->removeViewProvider(vp);
1161
void ManualAlignment::onAlign()
1166
void ManualAlignment::onRemoveLastPointMoveable()
1168
int nPoints = myAlignModel.activeGroup().countPoints();
1170
myAlignModel.activeGroup().removeLastPoint();
1171
d->picksepLeft->removeChild(nPoints-1);
1175
void ManualAlignment::onRemoveLastPointFixed()
1177
int nPoints = myFixedGroup.countPoints();
1179
myFixedGroup.removeLastPoint();
1180
d->picksepRight->removeChild(nPoints-1);
1184
void ManualAlignment::onClear()
1186
myAlignModel.activeGroup().clear();
1187
myFixedGroup.clear();
1189
coinRemoveAllChildren(d->picksepLeft);
1190
coinRemoveAllChildren(d->picksepRight);
1193
void ManualAlignment::onCancel()
1198
void ManualAlignment::probePickedCallback(void * ud, SoEventCallback * n)
1202
auto view = static_cast<Gui::View3DInventorViewer*>(n->getUserData());
1203
const SoEvent* ev = n->getEvent();
1204
if (ev->getTypeId() == SoMouseButtonEvent::getClassTypeId()) {
1206
n->getAction()->setHandled();
1209
auto mbe = static_cast<const SoMouseButtonEvent *>(ev);
1210
if (mbe->getButton() == SoMouseButtonEvent::BUTTON1 && mbe->getState() == SoButtonEvent::DOWN) {
1212
ManualAlignment* self = ManualAlignment::instance();
1216
const SoPickedPoint * point = view->getPickedPoint(n);
1218
auto vp = static_cast<Gui::ViewProvider*>(view->getViewProviderByPath(point->getPath()));
1219
if (vp && vp->isDerivedFrom<Gui::ViewProviderDocumentObject>()) {
1220
auto that = static_cast<Gui::ViewProviderDocumentObject*>(vp);
1221
if (self->applyPickedProbe(that, point)) {
1222
const SbVec3f& vec = point->getPoint();
1223
Gui::getMainWindow()->showMessage(
1224
tr("Point picked at (%1,%2,%3)")
1225
.arg(vec[0]).arg(vec[1]).arg(vec[2]));
1228
Gui::getMainWindow()->showMessage(
1229
tr("No point was found on model"));
1234
Gui::getMainWindow()->showMessage(
1235
tr("No point was picked"));
1238
else if (mbe->getButton() == SoMouseButtonEvent::BUTTON2 && mbe->getState() == SoButtonEvent::UP) {
1239
ManualAlignment* self = ManualAlignment::instance();
1240
if (self->myAlignModel.isEmpty() || self->myFixedGroup.isEmpty())
1242
self->showInstructions();
1244
if (view == self->myViewer->getViewer(0))
1245
nPoints = self->myAlignModel.activeGroup().countPoints();
1247
nPoints = self->myFixedGroup.countPoints();
1249
QAction* fi = menu.addAction(tr("&Align"));
1250
QAction* rem = menu.addAction(tr("&Remove last point"));
1252
QAction* ca = menu.addAction(tr("&Cancel"));
1253
fi->setEnabled(self->canAlign());
1254
rem->setEnabled(nPoints > 0);
1255
menu.addSeparator();
1256
QAction* sync = menu.addAction(tr("&Synchronize views"));
1257
sync->setCheckable(true);
1258
if (self->d->sensorCam1->getAttachedNode())
1259
sync->setChecked(true);
1260
QAction* id = menu.exec(QCursor::pos());
1263
QTimer::singleShot(300, self, &ManualAlignment::onAlign);
1265
else if ((id == rem) && (view == self->myViewer->getViewer(0))) {
1266
QTimer::singleShot(300, self, &ManualAlignment::onRemoveLastPointMoveable);
1268
else if ((id == rem) && (view == self->myViewer->getViewer(1))) {
1269
QTimer::singleShot(300, self, &ManualAlignment::onRemoveLastPointFixed);
1275
else if (id == ca) {
1277
QTimer::singleShot(300, self, &ManualAlignment::onCancel);
1279
else if (id == sync) {
1281
if (sync->isChecked()) {
1282
SoCamera* cam1 = self->myViewer->getViewer(0)->getSoRenderManager()->getCamera();
1283
SoCamera* cam2 = self->myViewer->getViewer(1)->getSoRenderManager()->getCamera();
1285
self->d->sensorCam1->attach(cam1);
1286
self->d->rot_cam1 = cam1->orientation.getValue();
1287
self->d->pos_cam1 = cam1->position.getValue();
1288
self->d->sensorCam2->attach(cam2);
1289
self->d->rot_cam2 = cam2->orientation.getValue();
1290
self->d->pos_cam2 = cam2->position.getValue();
1294
self->d->sensorCam1->detach();
1295
self->d->sensorCam2->detach();
1306
bool ManualAlignment::applyPickedProbe(Gui::ViewProviderDocumentObject* prov, const SoPickedPoint* pnt)
1308
const SbVec3f& vec = pnt->getPoint();
1309
const SbVec3f& nor = pnt->getNormal();
1312
if (myAlignModel.activeGroup().hasView(prov)) {
1313
std::vector<Base::Vector3d> pts = prov->getModelPoints(pnt);
1317
pp.point = pts.front();
1318
myAlignModel.activeGroup().addPoint(pp);
1320
d->picksepLeft->addChild(pickedPointsSubGraph(vec, nor, myAlignModel.activeGroup().countPoints()));
1323
else if (myFixedGroup.hasView(prov)) {
1324
std::vector<Base::Vector3d> pts = prov->getModelPoints(pnt);
1328
pp.point = pts.front();
1329
myFixedGroup.addPoint(pp);
1331
d->picksepRight->addChild(pickedPointsSubGraph(vec, nor, myFixedGroup.countPoints()));
1338
#include "moc_ManualAlignment.cpp"