Salome HOME
Merge branch 'Dev_1.1.0' of newgeom:newgeom into Dev_1.1.0
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_ConstraintFillet.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
2
3 // File:    SketchPlugin_ConstraintFillet.cpp
4 // Created: 19 Mar 2015
5 // Author:  Artem ZHIDKOV
6
7 #include "SketchPlugin_ConstraintFillet.h"
8
9 #include <GeomAPI_Dir2d.h>
10 #include <GeomAPI_Pnt2d.h>
11 #include <GeomAPI_XY.h>
12 #include <GeomDataAPI_Point2D.h>
13 #include <ModelAPI_AttributeDouble.h>
14 #include <ModelAPI_AttributeRefAttr.h>
15 #include <ModelAPI_AttributeRefList.h>
16 #include <ModelAPI_Data.h>
17 #include <ModelAPI_Events.h>
18 #include <ModelAPI_ResultConstruction.h>
19 #include <ModelAPI_Session.h>
20
21 #include <SketchPlugin_Arc.h>
22 #include <SketchPlugin_Line.h>
23 #include <SketchPlugin_Sketch.h>
24 #include <SketchPlugin_ConstraintCoincidence.h>
25 #include <SketchPlugin_ConstraintTangent.h>
26 #include <SketchPlugin_ConstraintRadius.h>
27
28 #include <SketcherPrs_Factory.h>
29
30 #include <Config_PropManager.h>
31 #include <Events_Loop.h>
32
33 static const std::string PREVIOUS_VALUE("FilletPreviousRadius");
34
35 /// \brief Attract specified point on theNewArc to the attribute of theFeature
36 static void recalculateAttributes(FeaturePtr theNewArc, const std::string& theNewArcAttribute,
37   FeaturePtr theFeature, const std::string& theFeatureAttribute);
38
39
40 SketchPlugin_ConstraintFillet::SketchPlugin_ConstraintFillet()
41 {
42 }
43
44 void SketchPlugin_ConstraintFillet::initAttributes()
45 {
46   data()->addAttribute(SketchPlugin_Constraint::VALUE(), ModelAPI_AttributeDouble::typeId());
47   data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttr::typeId());
48   data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefAttr::typeId());
49   data()->addAttribute(SketchPlugin_Constraint::ENTITY_C(), ModelAPI_AttributeRefList::typeId());
50   data()->addAttribute(PREVIOUS_VALUE, ModelAPI_AttributeDouble::typeId());
51   // initialize attribute not applicable for user
52   data()->attribute(SketchPlugin_Constraint::ENTITY_C())->setInitialized();
53   data()->attribute(PREVIOUS_VALUE)->setInitialized();
54   std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(data()->attribute(PREVIOUS_VALUE))->setValue(0.0);
55 }
56
57 void SketchPlugin_ConstraintFillet::execute()
58 {
59   std::shared_ptr<ModelAPI_Data> aData = data();
60   ResultConstructionPtr aRC;
61   // Check the base objects are initialized
62   double aFilletRadius = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
63       aData->attribute(SketchPlugin_Constraint::VALUE()))->value();
64   AttributeRefAttrPtr aBaseA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
65       aData->attribute(SketchPlugin_Constraint::ENTITY_A()));
66   AttributeRefAttrPtr aBaseB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
67       aData->attribute(SketchPlugin_Constraint::ENTITY_B()));
68   if (!aBaseA->isInitialized() || !aBaseB->isInitialized() ||
69       !aBaseA->isObject() || !aBaseB->isObject())
70     return;
71   // Check the fillet shapes is not initialized yet
72   AttributeRefListPtr aRefListOfFillet = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
73       aData->attribute(SketchPlugin_Constraint::ENTITY_C()));
74   if (aRefListOfFillet->size() > 0) {
75     // update the Radius constraint
76     ObjectPtr aFilletArcObj = aRefListOfFillet->list().back();
77     aRC = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aFilletArcObj);
78     FeaturePtr aFilletArcFeature = aRC ? aRC->document()->feature(aRC) : 
79       std::dynamic_pointer_cast<ModelAPI_Feature>(aFilletArcObj);
80
81     int aNbSubs = sketch()->numberOfSubs();
82     FeaturePtr aSubFeature;
83     for (int aSub = 0; aSub < aNbSubs; aSub++) {
84       aSubFeature = sketch()->subFeature(aSub);
85       if (aSubFeature->getKind() != SketchPlugin_ConstraintRadius::ID())
86         continue;
87       AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
88           aSubFeature->attribute(SketchPlugin_Constraint::ENTITY_A()));
89       if (!aRefAttr || !aRefAttr->isObject())
90         continue;
91       aRC = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aRefAttr->object());
92       FeaturePtr aFeature = aRC ? aRC->document()->feature(aRC) : 
93         std::dynamic_pointer_cast<ModelAPI_Feature>(aRefAttr->object());
94       if (aFeature == aFilletArcFeature) {
95         // Update radius constraint only if the value is changed in fillet's attribute
96         double aPrevRadius = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
97             aData->attribute(PREVIOUS_VALUE))->value();
98         if (aFilletRadius != aPrevRadius) {
99           AttributeDoublePtr aRadius = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
100               aSubFeature->attribute(SketchPlugin_Constraint::VALUE()));
101           aRadius->setValue(aFilletRadius);
102           std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
103               aData->attribute(PREVIOUS_VALUE))->setValue(aFilletRadius);
104         }
105         break;
106       }
107     }
108     return;
109   }
110   // Obtain features for the base objects
111   FeaturePtr aFeatureA, aFeatureB;
112   aRC = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aBaseA->object());
113   if (aRC) aFeatureA = aRC->document()->feature(aRC);
114   aRC = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aBaseB->object());
115   if (aRC) aFeatureB = aRC->document()->feature(aRC);
116   if (!aFeatureA || !aFeatureB)
117     return;
118
119   // Create list of objects composing a fillet
120   // copy aFeatureA
121   FeaturePtr aNewFeatureA = sketch()->addFeature(aFeatureA->getKind());
122   aFeatureA->data()->copyTo(aNewFeatureA->data());
123   aNewFeatureA->execute();
124   aRefListOfFillet->append(aNewFeatureA->firstResult());
125   // copy aFeatureB
126   FeaturePtr aNewFeatureB = sketch()->addFeature(aFeatureB->getKind());
127   aFeatureB->data()->copyTo(aNewFeatureB->data());
128   aNewFeatureB->execute();
129   aRefListOfFillet->append(aNewFeatureB->firstResult());
130   // create filleting arc (it will be attached to the list later)
131   FeaturePtr aNewArc = sketch()->addFeature(SketchPlugin_Arc::ID());
132
133   // Wait all constraints being created, then send update events
134   static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
135   bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
136   if (isUpdateFlushed)
137     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
138
139   // Calculate arc attributes
140   static const int aNbFeatures = 2;
141   FeaturePtr aFeature[aNbFeatures] = {aNewFeatureA, aNewFeatureB};
142   std::shared_ptr<GeomAPI_Dir2d> aTangentDir[aNbFeatures]; // tangent directions of the features in coincident point
143   bool isStart[aNbFeatures]; // indicates which point the features share
144   std::shared_ptr<GeomAPI_Pnt2d> aStartEndPnt[aNbFeatures * 2]; // first pair of points relate to first feature, second pair -  to second
145   std::string aFeatAttributes[aNbFeatures * 2]; // attributes of features
146   for (int i = 0; i < aNbFeatures; i++) {
147     std::string aStartAttr, aEndAttr;
148     if (aFeature[i]->getKind() == SketchPlugin_Line::ID()) {
149       aStartAttr = SketchPlugin_Line::START_ID();
150       aEndAttr = SketchPlugin_Line::END_ID();
151     } else if (aFeature[i]->getKind() == SketchPlugin_Arc::ID()) {
152       aStartAttr = SketchPlugin_Arc::START_ID();
153       aEndAttr = SketchPlugin_Arc::END_ID();
154     } else { // wrong argument
155       aRefListOfFillet->remove(aNewFeatureA);
156       aRefListOfFillet->remove(aNewFeatureB);
157       aRefListOfFillet->remove(aNewArc);
158       return;
159     }
160     aFeatAttributes[2*i] = aStartAttr;
161     aStartEndPnt[2*i] = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
162         aFeature[i]->attribute(aStartAttr))->pnt();
163     aFeatAttributes[2*i+1] = aEndAttr;
164     aStartEndPnt[2*i+1] = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
165         aFeature[i]->attribute(aEndAttr))->pnt();
166   }
167   for (int i = 0; i < aNbFeatures; i++) {
168     int j = aNbFeatures;
169     for (; j < 2 * aNbFeatures; j++)
170       if (aStartEndPnt[i]->distance(aStartEndPnt[j]) < 1.e-10) {
171         isStart[0] = i==0;
172         isStart[1] = j==aNbFeatures;
173         break;
174       }
175     if (j < 2 * aNbFeatures)
176       break;
177   }
178   // tangent directions of the features
179   for (int i = 0; i < aNbFeatures; i++) {
180     std::shared_ptr<GeomAPI_XY> aDir;
181     if (aFeature[i]->getKind() == SketchPlugin_Line::ID()) {
182       aDir = aStartEndPnt[2*i+1]->xy()->decreased(aStartEndPnt[2*i]->xy());
183       if (!isStart[i])
184         aDir = aDir->multiplied(-1.0);
185     } else if (aFeature[i]->getKind() == SketchPlugin_Arc::ID()) {
186       std::shared_ptr<GeomAPI_Pnt2d> aCenterPoint =
187           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
188           aFeature[i]->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt();
189       aDir = isStart[i] ? aStartEndPnt[2*i]->xy() : aStartEndPnt[2*i+1]->xy();
190       aDir = aDir->decreased(aCenterPoint->xy());
191
192       double x = aDir->x();
193       double y = aDir->y();
194       aDir->setX(-y);
195       aDir->setY(x);
196       if (!isStart[i])
197         aDir = aDir->multiplied(-1.0);
198     }
199     aTangentDir[i] = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(aDir));
200   }
201   // By default, the start point of fillet arc is connected to FeatureA,
202   // and the end point - to FeatureB. But when the angle between TangentDirA and
203   // TangentDirB greater 180 degree, the sequaence of features need to be reversed.
204   double cosBA = aTangentDir[0]->cross(aTangentDir[1]); // cos(B-A), where A and B - angles between corresponding tanget direction and the X axis
205   bool isReversed = cosBA > 0.0;
206
207   std::shared_ptr<GeomAPI_Pnt2d> aSharedPoint = aStartEndPnt[isStart[0] ? 0 : 1];
208   std::shared_ptr<GeomAPI_Dir2d> aBisect(new GeomAPI_Dir2d(
209       aTangentDir[0]->xy()->added(aTangentDir[1]->xy())));
210   std::shared_ptr<GeomAPI_XY> aStep = aBisect->xy()->multiplied(aFilletRadius);
211   std::shared_ptr<GeomAPI_XY> aCenter = aSharedPoint->xy()->added(aStep);
212   std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
213       aNewArc->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(
214       aCenter->x(), aCenter->y());
215   std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
216       aNewArc->attribute(SketchPlugin_Arc::START_ID()))->setValue(
217       aSharedPoint->x() - 1.e-5 * aStep->y(), aSharedPoint->y() + 1.e-5 * aStep->x());
218   std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
219       aNewArc->attribute(SketchPlugin_Arc::END_ID()))->setValue(
220       aSharedPoint->x() + 1.e-5 * aStep->y(), aSharedPoint->y() - 1.e-5 * aStep->x());
221   aNewArc->execute();
222   // attach new arc to the list
223   aRefListOfFillet->append(aNewArc->lastResult());
224   aRefListOfFillet->setInitialized();
225
226   // Create list of additional constraints:
227   // 1. Coincidence of boundary points of features and fillet arc
228   // 1.1. coincidence
229   FeaturePtr aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
230   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
231       aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
232   aRefAttr->setAttr(aNewArc->attribute(SketchPlugin_Arc::START_ID()));
233   aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
234       aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
235   int aFeatInd = isReversed ? 1 : 0;
236   int anAttrInd = (isReversed ? 2 : 0) + (isStart[isReversed ? 1 : 0] ? 0 : 1);
237   aRefAttr->setAttr(aFeature[aFeatInd]->attribute(aFeatAttributes[anAttrInd]));
238   recalculateAttributes(aNewArc, SketchPlugin_Arc::START_ID(), aFeature[aFeatInd], aFeatAttributes[anAttrInd]);
239   aConstraint->execute();
240   ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
241   // 1.2. coincidence
242   aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
243   aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
244       aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
245   aRefAttr->setAttr(aNewArc->attribute(SketchPlugin_Arc::END_ID()));
246   aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
247       aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
248   aFeatInd = isReversed ? 0 : 1;
249   anAttrInd = (isReversed ? 0 : 2) + (isStart[isReversed ? 0 : 1] ? 0 : 1);
250   aRefAttr->setAttr(aFeature[aFeatInd]->attribute(aFeatAttributes[anAttrInd]));
251   recalculateAttributes(aNewArc, SketchPlugin_Arc::END_ID(), aFeature[aFeatInd], aFeatAttributes[anAttrInd]);
252   aConstraint->execute();
253   ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
254   // recalculate center of fillet arc
255   std::shared_ptr<GeomAPI_Pnt2d> aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
256       aNewArc->attribute(SketchPlugin_Arc::START_ID()))->pnt();
257   std::shared_ptr<GeomAPI_Pnt2d> aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
258       aNewArc->attribute(SketchPlugin_Arc::END_ID()))->pnt();
259   aCenter = aStartPoint->xy()->added(aEndPoint->xy())->multiplied(0.5);
260   std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
261       aNewArc->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(
262       aCenter->x(), aCenter->y());
263   // 2. Fillet arc radius
264   aConstraint = sketch()->addFeature(SketchPlugin_ConstraintRadius::ID());
265   aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
266       aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
267   aRefAttr->setObject(aNewArc->lastResult());
268   std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
269       aConstraint->attribute(SketchPlugin_Constraint::VALUE()))->setValue(aFilletRadius);
270   std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
271       aConstraint->attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT()))->setValue(
272       isStart[0] ? aStartEndPnt[0] : aStartEndPnt[1]);
273   aConstraint->execute();
274   ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
275   // 3. Tangency of fillet arc and features
276   for (int i = 0; i < aNbFeatures; i++) {
277     aConstraint = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID());
278     aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
279         aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
280     aRefAttr->setObject(aNewArc->lastResult());
281     aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
282         aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
283     bool isArc = aFeature[i]->getKind() == SketchPlugin_Arc::ID();
284     aRefAttr->setObject(isArc ? aFeature[i]->lastResult() : aFeature[i]->firstResult());
285     aConstraint->execute();
286     ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
287   }
288
289   // make base features auxiliary
290   static Events_ID aRedisplayEvent = Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY);
291   aFeatureA->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true);
292   aFeatureB->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true);
293   ModelAPI_EventCreator::get()->sendUpdated(aFeatureA, aRedisplayEvent);
294   ModelAPI_EventCreator::get()->sendUpdated(aFeatureB, aRedisplayEvent);
295 ////  Events_Loop::loop()->flush(aRedisplayEvent);
296
297   // send events
298   if (isUpdateFlushed)
299     Events_Loop::loop()->setFlushed(anUpdateEvent, true);
300   Events_Loop::loop()->flush(anUpdateEvent);
301 }
302
303 AISObjectPtr SketchPlugin_ConstraintFillet::getAISObject(AISObjectPtr thePrevious)
304 {
305   if (!sketch())
306     return thePrevious;
307
308   AISObjectPtr anAIS = thePrevious;
309   /// TODO: Equal constraint presentation should be put here
310   return anAIS;
311 }
312
313
314
315 // =========   Auxiliary functions   =================
316 void recalculateAttributes(FeaturePtr theNewArc,  const std::string& theNewArcAttribute,
317                             FeaturePtr theFeature, const std::string& theFeatureAttribute)
318 {
319   std::shared_ptr<GeomAPI_Pnt2d> anArcPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
320       theNewArc->attribute(theNewArcAttribute))->pnt();
321   if (theFeature->getKind() == SketchPlugin_Line::ID()) {
322     std::shared_ptr<GeomAPI_Pnt2d> aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
323         theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt();
324     std::shared_ptr<GeomAPI_Pnt2d> aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
325         theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt();
326     std::shared_ptr<GeomAPI_Dir2d> aLineDir(new GeomAPI_Dir2d(
327         aEndPoint->xy()->decreased(aStartPoint->xy())));
328     std::shared_ptr<GeomAPI_XY> aVec = anArcPoint->xy()->decreased(aStartPoint->xy());
329     double aDot = aVec->dot(aLineDir->xy());
330     aVec = aStartPoint->xy()->added(aLineDir->xy()->multiplied(aDot));
331     anArcPoint->setX(aVec->x());
332     anArcPoint->setY(aVec->y());
333   } else if (theFeature->getKind() == SketchPlugin_Arc::ID()) {
334     std::shared_ptr<GeomAPI_Pnt2d> aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
335         theFeature->attribute(SketchPlugin_Arc::START_ID()))->pnt();
336     std::shared_ptr<GeomAPI_Pnt2d> aCenterPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
337         theFeature->attribute(SketchPlugin_Arc::END_ID()))->pnt();
338     double aRadius = aStartPoint->distance(aCenterPoint);
339     std::shared_ptr<GeomAPI_Dir2d> aDir(new GeomAPI_Dir2d(
340         anArcPoint->xy()->decreased(aCenterPoint->xy())));
341     std::shared_ptr<GeomAPI_XY> aPoint = aCenterPoint->xy()->added(aDir->xy()->multiplied(aRadius));
342     anArcPoint->setX(aPoint->x());
343     anArcPoint->setY(aPoint->y());
344   } else
345     return;
346
347   std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
348       theNewArc->attribute(theNewArcAttribute))->setValue(anArcPoint->x(), anArcPoint->y());
349   std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
350       theFeature->attribute(theFeatureAttribute))->setValue(anArcPoint->x(), anArcPoint->y());
351 }