1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
3 // File: PartSet_SketcherMgr.cpp
4 // Created: 19 Dec 2014
5 // Author: Vitaly SMETANNIKOV
7 #include "PartSet_SketcherMgr.h"
8 #include "PartSet_Module.h"
9 #include "PartSet_WidgetPoint2D.h"
10 #include "PartSet_Tools.h"
12 #include <XGUI_ModuleConnector.h>
13 #include <XGUI_Displayer.h>
14 #include <XGUI_Workshop.h>
16 #include <ModuleBase_IViewer.h>
17 #include <ModuleBase_IWorkshop.h>
18 #include <ModuleBase_IViewWindow.h>
19 #include <ModuleBase_Operation.h>
20 #include <ModuleBase_ISelection.h>
21 #include <ModuleBase_IPropertyPanel.h>
22 #include <ModuleBase_Operation.h>
24 #include <Events_Loop.h>
26 #include <SketchPlugin_Line.h>
27 #include <SketchPlugin_Sketch.h>
28 #include <SketchPlugin_Point.h>
29 #include <SketchPlugin_Arc.h>
30 #include <SketchPlugin_Circle.h>
31 #include <SketchPlugin_ConstraintLength.h>
32 #include <SketchPlugin_ConstraintDistance.h>
33 #include <SketchPlugin_ConstraintParallel.h>
34 #include <SketchPlugin_ConstraintPerpendicular.h>
35 #include <SketchPlugin_ConstraintRadius.h>
36 #include <SketchPlugin_ConstraintRigid.h>
38 #include <ModelAPI_Events.h>
40 #include <QMouseEvent>
41 #include <QApplication>
46 /// Returns list of unique objects by sum of objects from List1 and List2
47 QList<ObjectPtr> getSumList(const QList<ModuleBase_ViewerPrs>& theList1,
48 const QList<ModuleBase_ViewerPrs>& theList2)
50 QList<ObjectPtr> aRes;
51 foreach (ModuleBase_ViewerPrs aPrs, theList1) {
52 if (!aRes.contains(aPrs.object()))
53 aRes.append(aPrs.object());
55 foreach (ModuleBase_ViewerPrs aPrs, theList2) {
56 if (!aRes.contains(aPrs.object()))
57 aRes.append(aPrs.object());
65 PartSet_SketcherMgr::PartSet_SketcherMgr(PartSet_Module* theModule)
66 : QObject(theModule), myModule(theModule), myIsDragging(false), myDragDone(false)
68 ModuleBase_IWorkshop* aWorkshop = myModule->workshop();
69 ModuleBase_IViewer* aViewer = aWorkshop->viewer();
71 connect(aViewer, SIGNAL(mousePress(ModuleBase_IViewWindow*, QMouseEvent*)),
72 this, SLOT(onMousePressed(ModuleBase_IViewWindow*, QMouseEvent*)));
74 connect(aViewer, SIGNAL(mouseRelease(ModuleBase_IViewWindow*, QMouseEvent*)),
75 this, SLOT(onMouseReleased(ModuleBase_IViewWindow*, QMouseEvent*)));
77 connect(aViewer, SIGNAL(mouseMove(ModuleBase_IViewWindow*, QMouseEvent*)),
78 this, SLOT(onMouseMoved(ModuleBase_IViewWindow*, QMouseEvent*)));
80 connect(aViewer, SIGNAL(mouseDoubleClick(ModuleBase_IViewWindow*, QMouseEvent*)),
81 this, SLOT(onMouseDoubleClick(ModuleBase_IViewWindow*, QMouseEvent*)));
84 PartSet_SketcherMgr::~PartSet_SketcherMgr()
86 if (!myPlaneFilter.IsNull())
87 myPlaneFilter.Nullify();
90 void PartSet_SketcherMgr::onMousePressed(ModuleBase_IViewWindow* theWnd, QMouseEvent* theEvent)
93 if (!(theEvent->buttons() & Qt::LeftButton))
96 ModuleBase_IWorkshop* aWorkshop = myModule->workshop();
97 ModuleBase_Operation* aOperation = aWorkshop->currentOperation();
98 // Use only for sketch operations
99 if (aOperation && myCurrentSketch) {
100 if (!PartSet_Tools::sketchPlane(myCurrentSketch))
103 bool isSketcher = (aOperation->id().toStdString() == SketchPlugin_Sketch::ID());
104 bool isSketchOpe = sketchOperationIdList().contains(aOperation->id());
106 // Avoid non-sketch operations
107 if ((!isSketchOpe) && (!isSketcher))
110 bool isEditing = aOperation->isEditOperation();
112 // Ignore creation sketch operation
113 if ((!isSketcher) && (!isEditing))
116 if (theEvent->modifiers()) {
117 // If user performs multiselection
118 if (isSketchOpe /* && (!isSketcher)*/)
119 if (!aOperation->commit())
123 // Remember highlighted objects for editing
124 ModuleBase_ISelection* aSelect = aWorkshop->selection();
125 QList<ModuleBase_ViewerPrs> aHighlighted = aSelect->getHighlighted();
126 QList<ModuleBase_ViewerPrs> aSelected = aSelect->getSelected();
127 myEditingFeatures.clear();
128 myEditingAttr.clear();
129 if ((aHighlighted.size() == 0) && (aSelected.size() == 0)) {
130 if (isSketchOpe && (!isSketcher))
131 // commit previous operation
132 if (!aOperation->commit())
137 QObjectPtrList aSelObjects = getSumList(aHighlighted, aSelected);
138 //foreach (ModuleBase_ViewerPrs aPrs, aHighlighted) {
139 // aSelObjects.append(aPrs.object());
142 if ((aHighlighted.size() == 1) && (aSelected.size() == 0)) {
143 // Move by selected shape (vertex). Can be used only for single selection
144 foreach(ModuleBase_ViewerPrs aPrs, aHighlighted) {
145 FeaturePtr aFeature = ModelAPI_Feature::feature(aHighlighted.first().object());
147 myEditingFeatures.append(aFeature);
148 TopoDS_Shape aShape = aPrs.shape();
149 if (!aShape.IsNull()) {
150 if (aShape.ShapeType() == TopAbs_VERTEX) {
151 AttributePtr aAttr = PartSet_Tools::findAttributeBy2dPoint(myEditingFeatures.first(),
152 aShape, myCurrentSketch);
154 myEditingAttr.append(aAttr);
160 // Provide multi-selection. Can be used only for features
161 foreach (ObjectPtr aObj, aSelObjects) {
162 FeaturePtr aFeature = ModelAPI_Feature::feature(aObj);
163 if (aFeature && (!myEditingFeatures.contains(aFeature)))
164 myEditingFeatures.append(aFeature);
168 // If nothing highlighted - return
169 if (myEditingFeatures.size() == 0)
174 get2dPoint(theWnd, theEvent, myCurX, myCurY);
176 aWorkshop->viewer()->enableMultiselection(false);
179 } else if (isSketchOpe && isEditing) {
180 // If selected another object
184 get2dPoint(theWnd, theEvent, myCurX, myCurY);
186 aWorkshop->viewer()->enableMultiselection(false);
188 // This is necessary in order to finalize previous operation
189 QApplication::processEvents();
195 void PartSet_SketcherMgr::onMouseReleased(ModuleBase_IViewWindow* theWnd, QMouseEvent* theEvent)
197 ModuleBase_IWorkshop* aWorkshop = myModule->workshop();
198 ModuleBase_Operation* aOp = aWorkshop->currentOperation();
201 if (!sketchOperationIdList().contains(aOp->id()))
204 // Only for sketcher operations
205 ModuleBase_IViewer* aViewer = aWorkshop->viewer();
207 myIsDragging = false;
209 aViewer->enableMultiselection(true);
211 myEditingFeatures.clear();
212 myEditingAttr.clear();
216 if (!aViewer->isMultiSelectionEnabled()) {
217 aViewer->enableMultiselection(true);
219 aViewer->AISContext()->MoveTo(theEvent->x(), theEvent->y(), theWnd->v3dView());
220 if (theEvent->modifiers() & Qt::ShiftModifier)
221 aViewer->AISContext()->ShiftSelect();
223 aViewer->AISContext()->Select();
226 void PartSet_SketcherMgr::onMouseMoved(ModuleBase_IViewWindow* theWnd, QMouseEvent* theEvent)
229 ModuleBase_Operation* aOperation = myModule->workshop()->currentOperation();
230 if (aOperation->id().toStdString() == SketchPlugin_Sketch::ID())
231 return; // No edit operation activated
233 static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_MOVED);
234 Handle(V3d_View) aView = theWnd->v3dView();
235 gp_Pnt aPoint = PartSet_Tools::convertClickToPoint(theEvent->pos(), aView);
237 PartSet_Tools::convertTo2D(aPoint, myCurrentSketch, aView, aX, aY);
238 double dX = aX - myCurX;
239 double dY = aY - myCurY;
241 if ((aOperation->id().toStdString() == SketchPlugin_Line::ID()) &&
242 (myEditingAttr.size() == 1) &&
243 myEditingAttr.first()) {
244 // probably we have prehighlighted point
245 AttributePtr aAttr = myEditingAttr.first();
246 std::string aAttrId = aAttr->id();
247 ModuleBase_IPropertyPanel* aPanel = aOperation->propertyPanel();
248 QList<ModuleBase_ModelWidget*> aWidgets = aPanel->modelWidgets();
249 // Find corresponded widget to provide dragging
250 foreach (ModuleBase_ModelWidget* aWgt, aWidgets) {
251 if (aWgt->attributeID() == aAttrId) {
252 PartSet_WidgetPoint2D* aWgt2d = dynamic_cast<PartSet_WidgetPoint2D*>(aWgt);
254 aWgt2d->setPoint(aWgt2d->x() + dX, aWgt2d->y() + dY);
260 foreach(FeaturePtr aFeature, myEditingFeatures) {
261 std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
262 std::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
263 if (aSketchFeature) {
264 aSketchFeature->move(dX, dY);
265 ModelAPI_EventCreator::get()->sendUpdated(aSketchFeature, anEvent);
268 Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_MOVED));
269 Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
277 void PartSet_SketcherMgr::onMouseDoubleClick(ModuleBase_IViewWindow* theWnd, QMouseEvent* theEvent)
279 ModuleBase_Operation* aOperation = myModule->workshop()->currentOperation();
280 if (aOperation && aOperation->isEditOperation()) {
281 std::string aId = aOperation->id().toStdString();
282 if ((aId == SketchPlugin_ConstraintLength::ID()) ||
283 (aId == SketchPlugin_ConstraintDistance::ID()) ||
284 (aId == SketchPlugin_ConstraintRadius::ID()))
286 // Activate dimension value editing on double click
287 ModuleBase_IPropertyPanel* aPanel = aOperation->propertyPanel();
288 QList<ModuleBase_ModelWidget*> aWidgets = aPanel->modelWidgets();
289 // Find corresponded widget to activate value editing
290 foreach (ModuleBase_ModelWidget* aWgt, aWidgets) {
291 if (aWgt->attributeID() == "ConstraintValue") {
300 void PartSet_SketcherMgr::get2dPoint(ModuleBase_IViewWindow* theWnd, QMouseEvent* theEvent,
301 double& theX, double& theY)
303 Handle(V3d_View) aView = theWnd->v3dView();
304 gp_Pnt aPoint = PartSet_Tools::convertClickToPoint(theEvent->pos(), aView);
305 PartSet_Tools::convertTo2D(aPoint, myCurrentSketch, aView, theX, theY);
308 void PartSet_SketcherMgr::launchEditing()
310 if (myEditingFeatures.size() > 0) {
311 FeaturePtr aFeature = myEditingFeatures.first();
312 std::shared_ptr<SketchPlugin_Feature> aSPFeature =
313 std::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
315 myModule->editFeature(aSPFeature);
321 QStringList PartSet_SketcherMgr::sketchOperationIdList()
323 static QStringList aIds;
324 if (aIds.size() == 0) {
325 aIds << SketchPlugin_Line::ID().c_str();
326 aIds << SketchPlugin_Point::ID().c_str();
327 aIds << SketchPlugin_Arc::ID().c_str();
328 aIds << SketchPlugin_Circle::ID().c_str();
329 aIds << SketchPlugin_ConstraintLength::ID().c_str();
330 aIds << SketchPlugin_ConstraintDistance::ID().c_str();
331 aIds << SketchPlugin_ConstraintRigid::ID().c_str();
332 aIds << SketchPlugin_ConstraintRadius::ID().c_str();
333 aIds << SketchPlugin_ConstraintPerpendicular::ID().c_str();
334 aIds << SketchPlugin_ConstraintParallel::ID().c_str();
339 void PartSet_SketcherMgr::startSketch(ModuleBase_Operation* theOperation)
341 // Display all sketcher sub-Objects
342 myCurrentSketch = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(theOperation->feature());
343 XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myModule->workshop());
344 XGUI_Displayer* aDisplayer = aConnector->workshop()->displayer();
346 // Hide sketcher result
347 std::list<ResultPtr> aResults = myCurrentSketch->results();
348 std::list<ResultPtr>::const_iterator aIt;
349 for (aIt = aResults.begin(); aIt != aResults.end(); ++aIt) {
350 aDisplayer->erase((*aIt), false);
352 aDisplayer->erase(myCurrentSketch, false);
354 // Display sketcher objects
355 for (int i = 0; i < myCurrentSketch->numberOfSubs(); i++) {
356 FeaturePtr aFeature = myCurrentSketch->subFeature(i);
357 std::list<ResultPtr> aResults = aFeature->results();
358 std::list<ResultPtr>::const_iterator aIt;
359 for (aIt = aResults.begin(); aIt != aResults.end(); ++aIt) {
360 aDisplayer->display((*aIt), false);
362 aDisplayer->display(aFeature, false);
365 if (myPlaneFilter.IsNull())
366 myPlaneFilter = new ModuleBase_ShapeInPlaneFilter();
368 myModule->workshop()->viewer()->addSelectionFilter(myPlaneFilter);
369 if (theOperation->isEditOperation()) {
370 // If it is editing of sketch then it means that plane is already defined
371 std::shared_ptr<GeomAPI_Pln> aPln = PartSet_Tools::sketchPlane(myCurrentSketch);
372 myPlaneFilter->setPlane(aPln->impl<gp_Pln>());
374 aDisplayer->updateViewer();
377 void PartSet_SketcherMgr::stopSketch(ModuleBase_Operation* theOperation)
379 DataPtr aData = myCurrentSketch->data();
380 if ((!aData) || (!aData->isValid())) {
381 // The sketch was aborted
382 myCurrentSketch = CompositeFeaturePtr();
385 // Hide all sketcher sub-Objects
386 XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myModule->workshop());
387 XGUI_Displayer* aDisplayer = aConnector->workshop()->displayer();
388 for (int i = 0; i < myCurrentSketch->numberOfSubs(); i++) {
389 FeaturePtr aFeature = myCurrentSketch->subFeature(i);
390 std::list<ResultPtr> aResults = aFeature->results();
391 std::list<ResultPtr>::const_iterator aIt;
392 for (aIt = aResults.begin(); aIt != aResults.end(); ++aIt) {
393 aDisplayer->erase((*aIt), false);
395 aDisplayer->erase(aFeature, false);
397 // Display sketcher result
398 std::list<ResultPtr> aResults = myCurrentSketch->results();
399 std::list<ResultPtr>::const_iterator aIt;
400 for (aIt = aResults.begin(); aIt != aResults.end(); ++aIt) {
401 aDisplayer->display((*aIt), false);
403 aDisplayer->display(myCurrentSketch);
405 myCurrentSketch = CompositeFeaturePtr();
406 myModule->workshop()->viewer()->removeSelectionFilter(myPlaneFilter);
407 aDisplayer->updateViewer();
411 void PartSet_SketcherMgr::onPlaneSelected(const std::shared_ptr<GeomAPI_Pln>& thePln)
413 myPlaneFilter->setPlane(thePln->impl<gp_Pln>());