Salome HOME
99449702d9d7cccd3fc93c395375655e7b130b41
[modules/shaper.git] / src / PartSet / PartSet_BSplineWidget.cpp
1 // Copyright (C) 2019-2021  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include <PartSet_BSplineWidget.h>
21
22 #include <SketchPlugin_BSpline.h>
23
24 #include <ModuleBase_Tools.h>
25
26 #include <ModelAPI_AttributeDoubleArray.h>
27
28 #include <GeomDataAPI_Point2DArray.h>
29
30 #include <GeomAPI_Pnt2d.h>
31
32 #include <QFormLayout>
33 #include <QGroupBox>
34 #include <QLabel>
35 #include <QVBoxLayout>
36 #include <QScrollArea>
37 #include <QToolButton>
38
39
40 static const double THE_MIN_WEIGHT = 1.e-7;
41
42 PartSet_BSplineWidget::PartSet_BSplineWidget(
43     QWidget* theParent,
44     const Config_WidgetAPI* theData)
45   : ModuleBase_ModelWidget(theParent, theData)
46 {
47   QVBoxLayout* aMainLayout = new QVBoxLayout(this);
48   aMainLayout->setContentsMargins(0, 0, 0, 0);
49
50   // GroupBox to keep widgets for B-spline poles and weights
51   myPolesGroupBox = new QGroupBox(translate("Poles and weights"), this);
52   aMainLayout->addWidget(myPolesGroupBox);
53
54   QVBoxLayout* aLayout = new QVBoxLayout(myPolesGroupBox);
55   ModuleBase_Tools::adjustMargins(aLayout);
56
57   myScrollArea = new QScrollArea(myPolesGroupBox);
58   myScrollArea->setWidgetResizable(true);
59   myScrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
60   myScrollArea->setFrameStyle(QFrame::NoFrame);
61   aLayout->addWidget(myScrollArea);
62
63   QWidget* aContainer = new QWidget(myScrollArea);
64   QVBoxLayout* aBoxLay = new QVBoxLayout(aContainer);
65   aBoxLay->setContentsMargins(0, 0, 0, 0);
66
67   // layout of GroupBox
68   myPolesWgt = new QWidget(aContainer);
69   QGridLayout* aGroupLayout = new QGridLayout(myPolesWgt);
70   aGroupLayout->setColumnStretch(1, 1);
71   ModuleBase_Tools::adjustMargins(aGroupLayout);
72
73   restoreValueCustom();
74   aBoxLay->addWidget(myPolesWgt);
75   aBoxLay->addStretch(1);
76   myScrollArea->setWidget(aContainer);
77 }
78
79 void PartSet_BSplineWidget::setFeature(const FeaturePtr& theFeature,
80                                             const bool theToStoreValue,
81                                             const bool isUpdateFlushed)
82 {
83   ModuleBase_ModelWidget::setFeature(theFeature, theToStoreValue, isUpdateFlushed);
84   restoreValueCustom();
85 }
86
87 void PartSet_BSplineWidget::deactivate()
88 {
89   ModuleBase_ModelWidget::deactivate();
90   storeValueCustom();
91 }
92
93
94 QList<QWidget*> PartSet_BSplineWidget::getControls() const
95 {
96   QList<QWidget*> aControls;
97   aControls.append(myScrollArea);
98   return aControls;
99 }
100
101 void PartSet_BSplineWidget::storePolesAndWeights() const
102 {
103   std::shared_ptr<ModelAPI_Data> aData = myFeature->data();
104   AttributeDoubleArrayPtr aWeightsArray = aData->realArray(SketchPlugin_BSpline::WEIGHTS_ID());
105
106   std::list<BSplinePoleWidgets>::const_iterator anIt = myPoles.begin();
107   for (int anIndex = 0; anIt != myPoles.end(); ++anIndex, ++anIt) {
108     double aWeight = anIt->myWeight->value();
109     if (aWeight < THE_MIN_WEIGHT)
110       aWeight = THE_MIN_WEIGHT;
111     aWeightsArray->setValue(anIndex, aWeight);
112   }
113 }
114
115 bool PartSet_BSplineWidget::storeValueCustom()
116 {
117   std::shared_ptr<ModelAPI_Data> aData = myFeature->data();
118   if (!aData || !aData->isValid()) // can be on abort of sketcher element
119     return false;
120
121   AttributeDoubleArrayPtr aWeights = aData->realArray(SketchPlugin_BSpline::WEIGHTS_ID());
122
123   bool isBlocked = blockSignals(true);
124   storePolesAndWeights();
125   ModuleBase_Tools::flushUpdated(myFeature);
126   blockSignals(isBlocked);
127
128   updateObject(myFeature);
129   return true;
130 }
131
132 bool PartSet_BSplineWidget::restoreValueCustom()
133 {
134   if (!myFeature)
135     return false;
136
137   DataPtr aData = myFeature->data();
138
139   AttributePoint2DArrayPtr aPoles = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(
140       aData->attribute(SketchPlugin_BSpline::POLES_ID()));
141   AttributeDoubleArrayPtr aWeights = aData->realArray(SketchPlugin_BSpline::WEIGHTS_ID());
142
143   while ((int)myPoles.size() < aPoles->size())
144     addPoleWidget();
145
146   std::list<BSplinePoleWidgets>::iterator anIt = myPoles.begin();
147   for (int anIndex = 0; anIt != myPoles.end(); ++anIt, ++anIndex) {
148     GeomPnt2dPtr aPoint = aPoles->pnt(anIndex);
149     anIt->myX->setValue(aPoint->x());
150     anIt->myY->setValue(aPoint->y());
151     bool isBlocked = anIt->myWeight->blockSignals(true);
152     anIt->myWeight->setValue(aWeights->value(anIndex));
153     anIt->myWeight->blockSignals(isBlocked);
154   }
155
156   return true;
157 }
158
159 void PartSet_BSplineWidget::addPoleWidget()
160 {
161   QGridLayout* aGroupLay = dynamic_cast<QGridLayout*>(myPolesWgt->layout());
162   int aNbPoles = (int)myPoles.size();
163   QString aPoleStr = translate("Pole %1").arg(aNbPoles + 1);
164
165   myPoles.push_back(BSplinePoleWidgets());
166   BSplinePoleWidgets& aPole = myPoles.back();
167   aGroupLay->addWidget(createPoleWidget(aPole, aPoleStr, myPolesWgt), aNbPoles, 1);
168 }
169
170 QGroupBox* PartSet_BSplineWidget::createPoleWidget(BSplinePoleWidgets& thePole,
171   const QString& theName, QWidget* theParent)
172 {
173   QGroupBox* aPoleGroupBox = new QGroupBox(theName, theParent);
174   QGridLayout* aPoleLay = new QGridLayout(aPoleGroupBox);
175   aPoleLay->setSpacing(0);
176   ModuleBase_Tools::zeroMargins(aPoleLay);
177
178   thePole.myX = new ModuleBase_LabelValue(aPoleGroupBox, tr("X"));
179   aPoleLay->addWidget(thePole.myX, 0, 0, 1, 3);
180   thePole.myY = new ModuleBase_LabelValue(aPoleGroupBox, tr("Y"));
181   aPoleLay->addWidget(thePole.myY, 1, 0, 1, 3);
182   thePole.myWeight = new ModuleBase_ParamSpinBox(aPoleGroupBox);
183   thePole.myWeight->setMinimum(THE_MIN_WEIGHT);
184
185   aPoleLay->addWidget(new QLabel(translate("Weight :"), aPoleGroupBox), 2, 0);
186   aPoleLay->addWidget(thePole.myWeight, 2, 1);
187   // we should listen textChanged signal as valueChanged do not send when text is modified
188   connect(thePole.myWeight, SIGNAL(textChanged(const QString&)),
189     this, SIGNAL(valuesChanged()));
190
191   thePole.myAddBtn = new QToolButton(aPoleGroupBox);
192   thePole.myAddBtn->setIcon(QIcon(":pictures/add.png"));
193   thePole.myAddBtn->setToolTip(translate("Add a new pole after the current"));
194   aPoleLay->addWidget(thePole.myAddBtn, 2, 2);
195   connect(thePole.myAddBtn, SIGNAL(clicked(bool)), SLOT(onAddPole()));
196
197   return aPoleGroupBox;
198 }
199
200
201 void PartSet_BSplineWidget::onAddPole()
202 {
203   QObject* aObj = sender();
204   std::list<BSplinePoleWidgets>::const_iterator aIt;
205   int aId = 0;
206   bool aFound = false;
207   for (aIt = myPoles.cbegin(); aIt != myPoles.cend(); aIt++, aId++) {
208     if ((*aIt).myAddBtn == aObj) {
209       aFound = true;
210       break;
211     }
212   }
213   if (aFound) {
214     // add a new pole after found Id
215     std::ostringstream anActionName;
216     anActionName << SketchPlugin_BSplineBase::ADD_POLE_ACTION_ID() << "#" << aId;
217     if (feature()->customAction(anActionName.str()))
218       updateObject(feature());
219
220     restoreValueCustom();
221   }
222 }