Salome HOME
3e25988ebf7def9cb3b1f376a2a7d9c44e1657f8
[modules/shaper.git] / src / PartSet / PartSet_WidgetPoint2d.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:        PartSet_WidgetPoint2D.cpp
4 // Created:     25 Apr 2014
5 // Author:      Natalia ERMOLAEVA
6
7 #include "PartSet_WidgetPoint2d.h"
8 #include <PartSet_Tools.h>
9 #include <PartSet_Module.h>
10 #include <PartSet_LockApplyMgr.h>
11
12 #include <ModuleBase_ParamSpinBox.h>
13 #include <ModuleBase_Tools.h>
14 #include <ModuleBase_IViewer.h>
15 #include <ModuleBase_IViewWindow.h>
16 #include <ModuleBase_ISelection.h>
17
18 #include <Config_Keywords.h>
19 #include <Config_WidgetAPI.h>
20
21 #include <Events_Loop.h>
22 #include <ModelAPI_Events.h>
23
24 #include <ModelAPI_Feature.h>
25 #include <ModelAPI_Data.h>
26 #include <ModelAPI_Object.h>
27 #include <GeomDataAPI_Point2D.h>
28 #include <GeomAPI_Pnt2d.h>
29
30 #include <SketchPlugin_Feature.h>
31 #include <SketchPlugin_ConstraintCoincidence.h>
32 #include <SketchPlugin_Line.h>
33 #include <SketchPlugin_Arc.h>
34 #include <SketchPlugin_Circle.h>
35 #include <SketchPlugin_Point.h>
36
37 #include <QGroupBox>
38 #include <QGridLayout>
39 #include <QLabel>
40 #include <QEvent>
41 #include <QMouseEvent>
42 #include <QApplication>
43
44 #include <TopoDS.hxx>
45 #include <TopoDS_Vertex.hxx>
46 #include <BRep_Tool.hxx>
47
48 #include <cfloat>
49 #include <climits>
50
51 const double MaxCoordinate = 1e12;
52
53 static QStringList MyFeaturesForCoincedence;
54
55 PartSet_WidgetPoint2D::PartSet_WidgetPoint2D(QWidget* theParent, 
56                                              ModuleBase_IWorkshop* theWorkshop,
57                                              const Config_WidgetAPI* theData,
58                                              const std::string& theParentId)
59  : ModuleBase_ModelWidget(theParent, theData, theParentId), myWorkshop(theWorkshop)
60 {
61   if (MyFeaturesForCoincedence.isEmpty()) {
62     MyFeaturesForCoincedence << SketchPlugin_Line::ID().c_str()
63       << SketchPlugin_Arc::ID().c_str()
64       << SketchPlugin_Point::ID().c_str()
65       << SketchPlugin_Circle::ID().c_str();
66   }
67   myLockApplyMgr = new PartSet_LockApplyMgr(theParent, myWorkshop);
68
69   // the control should accept the focus, so the boolen flag is corrected to be true
70   myIsObligatory = true;
71   //myOptionParam = theData->getProperty(PREVIOUS_FEATURE_PARAM);
72   QString aPageName = QString::fromStdString(theData->getProperty(CONTAINER_PAGE_NAME));
73   myGroupBox = new QGroupBox(aPageName, theParent);
74   myGroupBox->setFlat(false);
75
76   QGridLayout* aGroupLay = new QGridLayout(myGroupBox);
77   ModuleBase_Tools::adjustMargins(aGroupLay);
78   aGroupLay->setSpacing(2);
79   aGroupLay->setColumnStretch(1, 1);
80   {
81     QLabel* aLabel = new QLabel(myGroupBox);
82     aLabel->setText(tr("X "));
83     aGroupLay->addWidget(aLabel, 0, 0);
84
85     myXSpin = new ModuleBase_ParamSpinBox(myGroupBox);
86     myXSpin->setMinimum(-DBL_MAX);
87     myXSpin->setMaximum(DBL_MAX);
88     myXSpin->setToolTip(tr("X"));
89     aGroupLay->addWidget(myXSpin, 0, 1);
90
91     // Apply widget value change by enter/tab event.
92     //connect(myXSpin, SIGNAL(valueChanged(const QString&)), this, SLOT(onValuesChanged()));
93     connect(myXSpin, SIGNAL(editingFinished()), this, SLOT(onValuesChanged()));
94   }
95   {
96     QLabel* aLabel = new QLabel(myGroupBox);
97     aLabel->setText(tr("Y "));
98     aGroupLay->addWidget(aLabel, 1, 0);
99
100     myYSpin = new ModuleBase_ParamSpinBox(myGroupBox);
101     myYSpin->setMinimum(-DBL_MAX);
102     myYSpin->setMaximum(DBL_MAX);
103     myYSpin->setToolTip(tr("Y"));
104     aGroupLay->addWidget(myYSpin, 1, 1);
105
106     // Apply widget value change by enter/tab event.
107     //connect(myYSpin, SIGNAL(valueChanged(const QString&)), this, SLOT(onValuesChanged()));
108     connect(myYSpin, SIGNAL(editingFinished()), this, SLOT(onValuesChanged()));
109   }
110   QVBoxLayout* aLayout = new QVBoxLayout(this);
111   ModuleBase_Tools::zeroMargins(aLayout);
112   aLayout->addWidget(myGroupBox);
113   setLayout(aLayout);
114 }
115
116 bool PartSet_WidgetPoint2D::reset()
117 {
118   bool aDone = false;
119   if (!isUseReset() || isComputedDefault() || myXSpin->hasVariable() || myYSpin->hasVariable()) {
120     aDone = false;
121   }
122   else {
123     bool isOk;
124     double aDefValue = QString::fromStdString(getDefaultValue()).toDouble(&isOk);
125     // it is important to block the spin box control in order to do not through out the
126     // locking of the validating state.
127     ModuleBase_Tools::setSpinValue(myXSpin, isOk ? aDefValue : 0.0);
128     ModuleBase_Tools::setSpinValue(myYSpin, isOk ? aDefValue : 0.0);
129     storeValueCustom();
130     aDone = true;
131   }
132   return aDone;
133 }
134
135 PartSet_WidgetPoint2D::~PartSet_WidgetPoint2D()
136 {
137 }
138
139 bool PartSet_WidgetPoint2D::setSelection(QList<ModuleBase_ViewerPrs>& theValues,
140                                          const bool theToValidate)
141 {
142   if (theValues.empty())
143     return false;
144
145   ModuleBase_ViewerPrs aValue = theValues.takeFirst();
146
147   Handle(V3d_View) aView = myWorkshop->viewer()->activeView();
148   bool isDone = false;
149   TopoDS_Shape aShape = aValue.shape();
150   double aX, aY;
151   if (getPoint2d(aView, aShape, aX, aY)) {
152     isDone = setPoint(aX, aY);
153   }
154   return isDone;
155 }
156
157 bool PartSet_WidgetPoint2D::setPoint(double theX, double theY)
158 {
159   if (fabs(theX) >= MaxCoordinate)
160     return false;
161   if (fabs(theY) >= MaxCoordinate)
162     return false;
163
164   ModuleBase_Tools::setSpinValue(myXSpin, theX);
165   ModuleBase_Tools::setSpinValue(myYSpin, theY);
166
167   storeValue();
168   return true;
169 }
170
171 bool PartSet_WidgetPoint2D::storeValueCustom() const
172 {
173   std::shared_ptr<ModelAPI_Data> aData = myFeature->data();
174   if (!aData) // can be on abort of sketcher element
175     return false;
176   std::shared_ptr<GeomDataAPI_Point2D> aPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
177       aData->attribute(attributeID()));
178
179   PartSet_WidgetPoint2D* that = (PartSet_WidgetPoint2D*) this;
180   bool isBlocked = that->blockSignals(true);
181   bool isImmutable = aPoint->setImmutable(true);
182
183   // if text is not empty then setValue will be ignored
184   // so we should set the text at first
185   aPoint->setText(myXSpin->hasVariable() ? myXSpin->text().toStdString() : "",
186                   myYSpin->hasVariable() ? myYSpin->text().toStdString() : "");
187   aPoint->setValue(!myXSpin->hasVariable() ? myXSpin->value() : aPoint->x(),
188                    !myYSpin->hasVariable() ? myYSpin->value() : aPoint->y());
189
190   // after movement the solver will call the update event: optimization
191   moveObject(myFeature);
192   aPoint->setImmutable(isImmutable);
193   that->blockSignals(isBlocked);
194
195   return true;
196 }
197
198 bool PartSet_WidgetPoint2D::restoreValueCustom()
199 {
200   std::shared_ptr<ModelAPI_Data> aData = myFeature->data();
201   std::shared_ptr<GeomDataAPI_Point2D> aPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
202       aData->attribute(attributeID()));
203   QString aTextX = QString::fromStdString(aPoint->textX());
204   QString aTextY = QString::fromStdString(aPoint->textY());
205
206   bool isDouble = false;
207   double aVal = 0;
208   if (aTextX.isEmpty()) {
209     ModuleBase_Tools::setSpinValue(myXSpin, aPoint->x());
210   } else {
211     aVal = aTextX.toDouble(&isDouble);
212     if (isDouble)
213       ModuleBase_Tools::setSpinValue(myXSpin, aVal);
214     else
215       ModuleBase_Tools::setSpinText(myXSpin, aTextX);
216   }
217   if (aTextY.isEmpty()) {
218     ModuleBase_Tools::setSpinValue(myYSpin, aPoint->y());
219   } else {
220     aVal = aTextY.toDouble(&isDouble);
221     if (isDouble)
222       ModuleBase_Tools::setSpinValue(myYSpin, aVal);
223     else
224       ModuleBase_Tools::setSpinText(myYSpin, aTextY);
225   }
226   //if (aTextX.empty() || aTextY.empty()) {
227   //  ModuleBase_Tools::setSpinValue(myXSpin, aPoint->x());
228   //  ModuleBase_Tools::setSpinValue(myYSpin, aPoint->y());
229   //} else {
230   //  ModuleBase_Tools::setSpinText(myXSpin, QString::fromStdString(aTextX));
231   //  ModuleBase_Tools::setSpinText(myYSpin, QString::fromStdString(aTextY));
232   //}
233   return true;
234 }
235
236 QList<QWidget*> PartSet_WidgetPoint2D::getControls() const
237 {
238   QList<QWidget*> aControls;
239   aControls.append(myXSpin);
240   aControls.append(myYSpin);
241   return aControls;
242 }
243
244
245 void PartSet_WidgetPoint2D::activateCustom()
246 {
247   ModuleBase_IViewer* aViewer = myWorkshop->viewer();
248   connect(aViewer, SIGNAL(mouseMove(ModuleBase_IViewWindow*, QMouseEvent*)), 
249           this, SLOT(onMouseMove(ModuleBase_IViewWindow*, QMouseEvent*)));
250   connect(aViewer, SIGNAL(mouseRelease(ModuleBase_IViewWindow*, QMouseEvent*)), 
251           this, SLOT(onMouseRelease(ModuleBase_IViewWindow*, QMouseEvent*)));
252
253   QIntList aModes;
254   aModes << TopAbs_VERTEX;
255   aModes << TopAbs_EDGE;
256   myWorkshop->activateSubShapesSelection(aModes);
257
258   myLockApplyMgr->activate();
259 }
260
261 void PartSet_WidgetPoint2D::deactivate()
262 {
263   ModuleBase_IViewer* aViewer = myWorkshop->viewer();
264   disconnect(aViewer, SIGNAL(mouseMove(ModuleBase_IViewWindow*, QMouseEvent*)),
265              this, SLOT(onMouseMove(ModuleBase_IViewWindow*, QMouseEvent*)));
266   disconnect(aViewer, SIGNAL(mouseRelease(ModuleBase_IViewWindow*, QMouseEvent*)), 
267              this, SLOT(onMouseRelease(ModuleBase_IViewWindow*, QMouseEvent*)));
268
269   myWorkshop->deactivateSubShapesSelection();
270
271   myLockApplyMgr->deactivate();
272 }
273
274 bool PartSet_WidgetPoint2D::getPoint2d(const Handle(V3d_View)& theView, 
275                                        const TopoDS_Shape& theShape, 
276                                        double& theX, double& theY) const
277 {
278   if (!theShape.IsNull()) {
279     if (theShape.ShapeType() == TopAbs_VERTEX) {
280       const TopoDS_Vertex& aVertex = TopoDS::Vertex(theShape);
281       if (!aVertex.IsNull()) {
282         // A case when point is taken from existing vertex
283         gp_Pnt aPoint = BRep_Tool::Pnt(aVertex);
284         PartSet_Tools::convertTo2D(aPoint, mySketch, theView, theX, theY);
285         return true;
286       }
287     }
288   }
289   return false;
290 }
291
292 void PartSet_WidgetPoint2D::setConstraintWith(const ObjectPtr& theObject)
293 {
294   // Create point-edge coincedence
295   FeaturePtr aFeature = mySketch->addFeature(SketchPlugin_ConstraintCoincidence::ID());
296   std::shared_ptr<ModelAPI_Data> aData = aFeature->data();
297
298   std::shared_ptr<ModelAPI_AttributeRefAttr> aRef1 = std::dynamic_pointer_cast<
299       ModelAPI_AttributeRefAttr>(aData->attribute(SketchPlugin_Constraint::ENTITY_A()));
300   AttributePtr aThisAttr = feature()->data()->attribute(attributeID());
301   std::shared_ptr<GeomDataAPI_Point2D> aThisPoint = 
302     std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aThisAttr);
303   aRef1->setAttr(aThisPoint);
304
305   std::shared_ptr<ModelAPI_AttributeRefAttr> aRef2 = std::dynamic_pointer_cast<
306       ModelAPI_AttributeRefAttr>(aData->attribute(SketchPlugin_Constraint::ENTITY_B()));
307   aRef2->setObject(theObject);
308
309   aFeature->execute();
310 }
311
312 void PartSet_WidgetPoint2D::onMouseRelease(ModuleBase_IViewWindow* theWnd, QMouseEvent* theEvent)
313 {
314   // the contex menu release by the right button should not be processed by this widget
315   if (theEvent->button() != Qt::LeftButton)
316     return;
317
318   ModuleBase_ISelection* aSelection = myWorkshop->selection();
319   Handle(V3d_View) aView = theWnd->v3dView();
320   // TODO: This fragment doesn't work because bug in OCC Viewer. It can be used after fixing.
321   NCollection_List<TopoDS_Shape> aShapes;
322   std::list<ObjectPtr> aObjects;
323   aSelection->selectedShapes(aShapes, aObjects);
324   // if we have selection
325   if (aShapes.Extent() > 0) {
326     TopoDS_Shape aShape = aShapes.First();
327     ObjectPtr aObject = aObjects.front();
328     FeaturePtr aSelectedFeature = ModelAPI_Feature::feature(aObject);
329     bool anExternal = false;
330     if (aSelectedFeature.get() != NULL) {
331       std::shared_ptr<SketchPlugin_Feature> aSPFeature = 
332               std::dynamic_pointer_cast<SketchPlugin_Feature>(aSelectedFeature);
333       if ((!aSPFeature) && (!aShape.IsNull())) {
334         anExternal = true;
335         ResultPtr aFixedObject = PartSet_Tools::findFixedObjectByExternal(aShape, aObject, mySketch);
336         if (!aFixedObject.get())
337           aObject = PartSet_Tools::createFixedObjectByExternal(aShape, aObject, mySketch);
338
339         double aX, aY;
340         if (getPoint2d(aView, aShape, aX, aY) && isFeatureContainsPoint(myFeature, aX, aY)) {
341           // do not create a constraint to the point, which already used by the feature
342           // if the feature contains the point, focus is not switched
343           setPoint(aX, aY);
344         }
345         else {
346           if (getPoint2d(aView, aShape, aX, aY))
347             setPoint(aX, aY);
348           setConstraintWith(aObject);
349           emit vertexSelected();
350           emit focusOutWidget(this);
351         }
352       }
353     }
354     if (!anExternal) {
355       double aX, aY;
356       bool isProcessed = false;
357       if (getPoint2d(aView, aShape, aX, aY) && isFeatureContainsPoint(myFeature, aX, aY)) {
358         // when the point is selected, the coordinates of the point should be set into the attribute
359         // if the feature contains the point, focus is not switched
360         setPoint(aX, aY);
361       }
362       else {
363         // do not set a coincidence constraint in the attribute if the feature contains a point
364         // with the same coordinates. It is important for line creation in order to do not set
365         // the same constraints for the same points, oterwise the result line has zero length.
366         if (getPoint2d(aView, aShape, aX, aY)) {
367           setPoint(aX, aY);
368           PartSet_Tools::setConstraints(mySketch, feature(), attributeID(), aX, aY);
369         }
370         else if (aShape.ShapeType() == TopAbs_EDGE) {
371           if (MyFeaturesForCoincedence.contains(myFeature->getKind().c_str()))
372             setConstraintWith(aObject);
373         }
374         // it is important to perform updateObject() in order to the current value is 
375         // processed by Sketch Solver. Test case: line is created from a previous point
376         // to some distance, but in the area of the highlighting of the point. Constraint
377         // coincidence is created, after the solver is performed, the distance between the
378         // points of the line becomes less than the tolerance. Validator of the line returns
379         // false, the line will be aborted, but sketch stays valid.
380         updateObject(feature());
381         emit vertexSelected();
382         emit focusOutWidget(this);
383       }
384     }
385   }
386   // End of Bug dependent fragment
387   else {
388     // A case when point is taken from mouse event
389     gp_Pnt aPoint = PartSet_Tools::convertClickToPoint(theEvent->pos(), theWnd->v3dView());
390     double aX, anY;
391     PartSet_Tools::convertTo2D(aPoint, mySketch, aView, aX, anY);
392
393     // if the feature contains the point, focus is not switched
394     if (!setPoint(aX, anY) || isFeatureContainsPoint(myFeature, aX, anY))
395       return;
396
397     /// Start alternative code
398     //std::shared_ptr<GeomDataAPI_Point2D> aFeaturePoint = std::dynamic_pointer_cast<
399     //    GeomDataAPI_Point2D>(feature()->data()->attribute(attributeID()));
400     //QList<FeaturePtr> aIgnore;
401     //aIgnore.append(feature());
402
403     //double aTolerance = aView->Convert(7);
404     //std::shared_ptr<GeomDataAPI_Point2D> aAttrPnt = 
405     //  PartSet_Tools::findAttributePoint(mySketch, aX, anY, aTolerance, aIgnore);
406     //if (aAttrPnt.get() != NULL) {
407     //  aFeaturePoint->setValue(aAttrPnt->pnt());
408     //  PartSet_Tools::createConstraint(mySketch, aAttrPnt, aFeaturePoint);
409     //  emit vertexSelected();
410     //}
411     /// End alternative code
412     emit focusOutWidget(this);
413   }
414 }
415
416
417 void PartSet_WidgetPoint2D::onMouseMove(ModuleBase_IViewWindow* theWnd, QMouseEvent* theEvent)
418 {
419   if (isEditingMode())
420     return;
421
422   gp_Pnt aPoint = PartSet_Tools::convertClickToPoint(theEvent->pos(), theWnd->v3dView());
423
424   double aX, anY;
425   PartSet_Tools::convertTo2D(aPoint, mySketch, theWnd->v3dView(), aX, anY);
426   setPoint(aX, anY);
427 }
428
429 double PartSet_WidgetPoint2D::x() const
430 {
431   return myXSpin->value();
432 }
433
434 double PartSet_WidgetPoint2D::y() const
435 {
436   return myYSpin->value();
437 }
438
439
440 bool PartSet_WidgetPoint2D::isFeatureContainsPoint(const FeaturePtr& theFeature,
441                                                    double theX, double theY)
442 {
443   bool aPointIsFound = false;
444   AttributePtr aWidgetAttribute = myFeature->attribute(attributeID());
445
446   std::shared_ptr<GeomAPI_Pnt2d> aPnt2d = 
447                                     std::shared_ptr<GeomAPI_Pnt2d>(new GeomAPI_Pnt2d(theX, theY));
448   std::list<AttributePtr> anAttributes =
449                                 myFeature->data()->attributes(GeomDataAPI_Point2D::typeId());
450   std::list<AttributePtr>::iterator anIter = anAttributes.begin();
451   for(; anIter != anAttributes.end() && !aPointIsFound; anIter++) {
452     AttributePoint2DPtr aPoint2DAttribute =
453       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(*anIter);
454     if (aPoint2DAttribute == aWidgetAttribute)
455       continue;
456     if (aPoint2DAttribute.get() && aPoint2DAttribute->isInitialized()) {
457       aPointIsFound = aPoint2DAttribute->pnt()->isEqual(aPnt2d);
458     }
459   }
460   return aPointIsFound;
461 }
462
463 void PartSet_WidgetPoint2D::onValuesChanged()
464 {
465   myLockApplyMgr->valuesChanged();
466   emit valuesChanged();
467 }