1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
3 // File: SketchPlugin_ConstraintFillet.cpp
4 // Created: 19 Mar 2015
5 // Author: Artem ZHIDKOV
7 #include "SketchPlugin_ConstraintFillet.h"
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>
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>
28 #include <SketcherPrs_Factory.h>
30 #include <Config_PropManager.h>
31 #include <Events_Loop.h>
33 static const std::string PREVIOUS_VALUE("FilletPreviousRadius");
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);
40 SketchPlugin_ConstraintFillet::SketchPlugin_ConstraintFillet()
44 void SketchPlugin_ConstraintFillet::initAttributes()
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);
57 void SketchPlugin_ConstraintFillet::execute()
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())
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);
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())
87 AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
88 aSubFeature->attribute(SketchPlugin_Constraint::ENTITY_A()));
89 if (!aRefAttr || !aRefAttr->isObject())
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);
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)
119 // Create list of objects composing a fillet
121 FeaturePtr aNewFeatureA = sketch()->addFeature(aFeatureA->getKind());
122 aFeatureA->data()->copyTo(aNewFeatureA->data());
123 aNewFeatureA->execute();
124 aRefListOfFillet->append(aNewFeatureA->firstResult());
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());
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);
137 Events_Loop::loop()->setFlushed(anUpdateEvent, false);
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);
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();
167 for (int i = 0; i < aNbFeatures; i++) {
169 for (; j < 2 * aNbFeatures; j++)
170 if (aStartEndPnt[i]->distance(aStartEndPnt[j]) < 1.e-10) {
172 isStart[1] = j==aNbFeatures;
175 if (j < 2 * aNbFeatures)
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());
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());
192 double x = aDir->x();
193 double y = aDir->y();
197 aDir = aDir->multiplied(-1.0);
199 aTangentDir[i] = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(aDir));
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;
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());
222 // attach new arc to the list
223 aRefListOfFillet->append(aNewArc->lastResult());
224 aRefListOfFillet->setInitialized();
226 // Create list of additional constraints:
227 // 1. Coincidence of boundary points of features and fillet arc
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);
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);
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);
299 Events_Loop::loop()->setFlushed(anUpdateEvent, true);
300 Events_Loop::loop()->flush(anUpdateEvent);
303 AISObjectPtr SketchPlugin_ConstraintFillet::getAISObject(AISObjectPtr thePrevious)
308 AISObjectPtr anAIS = thePrevious;
309 /// TODO: Equal constraint presentation should be put here
315 // ========= Auxiliary functions =================
316 void recalculateAttributes(FeaturePtr theNewArc, const std::string& theNewArcAttribute,
317 FeaturePtr theFeature, const std::string& theFeatureAttribute)
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());
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());