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