Salome HOME
Fix for the issue #1928
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_Validators.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
2
3 // File:        SketchPlugin_Validators.cpp
4 // Created:     01 Aug 2014
5 // Author:      Vitaly SMETANNIKOV
6
7 #include "SketchPlugin_Validators.h"
8
9 #include "SketchPlugin_Arc.h"
10 #include "SketchPlugin_Circle.h"
11 #include "SketchPlugin_ConstraintCoincidence.h"
12 #include "SketchPlugin_ConstraintDistance.h"
13 #include "SketchPlugin_ConstraintFillet.h"
14 #include "SketchPlugin_ConstraintRigid.h"
15 #include "SketchPlugin_ConstraintTangent.h"
16 #include "SketchPlugin_Line.h"
17 #include "SketchPlugin_Point.h"
18 #include "SketchPlugin_Sketch.h"
19 #include "SketchPlugin_Tools.h"
20
21 #include "SketcherPrs_Tools.h"
22
23 #include <Events_InfoMessage.h>
24
25 #include <ModelAPI_Data.h>
26 #include <ModelAPI_Validator.h>
27 #include <ModelAPI_AttributeDouble.h>
28 #include <ModelAPI_AttributeRefAttr.h>
29
30 #include <ModelAPI_AttributeRefAttrList.h>
31 #include <ModelAPI_AttributeRefList.h>
32 #include <ModelAPI_AttributeSelectionList.h>
33 #include <ModelAPI_AttributeString.h>
34 #include <ModelAPI_Session.h>
35 #include <ModelAPI_Tools.h>
36 #include <ModelAPI_ResultConstruction.h>
37
38 #include <ModelGeomAlgo_Point2D.h>
39
40 #include <GeomAPI_Circ.h>
41 #include <GeomAPI_Lin.h>
42 #include <GeomAPI_Edge.h>
43 #include <GeomAPI_Vertex.h>
44 #include <GeomDataAPI_Point2D.h>
45
46 #include <algorithm>
47 #include <cmath>
48
49 const double tolerance = 1.e-7;
50
51 bool SketchPlugin_DistanceAttrValidator::isValid(const AttributePtr& theAttribute,
52                                                  const std::list<std::string>& theArguments,
53                                                  Events_InfoMessage& theError) const
54 {
55   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
56     theError = "The attribute with the %1 type is not processed";
57     theError.arg(theAttribute->attributeType());
58     return false;
59   }
60
61   // there is a check whether the feature contains a point and a linear edge or two point values
62   std::string aParamA = theArguments.front();
63   SessionPtr aMgr = ModelAPI_Session::get();
64   ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
65
66   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
67   bool isObject = aRefAttr->isObject();
68   if (!isObject) {
69     // an attribute is a point. A point value is valid always for the distance
70     return true;
71   } else {
72     // 1. check whether the references object is a linear
73     ObjectPtr anObject = aRefAttr->object();
74
75     const ModelAPI_AttributeValidator* aShapeValidator =
76       dynamic_cast<const ModelAPI_AttributeValidator*>(
77       aFactory->validator("GeomValidators_ShapeType"));
78     std::list<std::string> anArguments;
79     anArguments.push_back("circle");
80     Events_InfoMessage aCircleError;
81     bool aShapeValid = aShapeValidator->isValid(aRefAttr, anArguments, aCircleError);
82     // the circle line is not a valid case
83     if (aShapeValid) {
84       theError = "Circle can not be used in distance constraint";
85       return false;
86     }
87
88     anArguments.clear();
89     anArguments.push_back("line");
90     Events_InfoMessage aLineError;
91     aShapeValid = aShapeValidator->isValid(aRefAttr, anArguments, aLineError);
92     // if the attribute value is not a line, that means it is a vertex. A vertex is always valid
93     if (aShapeValid) {
94       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
95       // If it is a line then we have to check that first attribute id not a line
96       std::shared_ptr<SketchPlugin_Feature> aSFeature =
97         std::dynamic_pointer_cast<SketchPlugin_Feature>(theAttribute->owner());
98       SketchPlugin_Sketch* aSketch = aSFeature->sketch();
99       std::shared_ptr<GeomAPI_Ax3> aPlane = SketchPlugin_Sketch::plane(aSketch);
100       std::shared_ptr<GeomDataAPI_Point2D> aPoint = SketcherPrs_Tools::getFeaturePoint(
101         aFeature->data(), aParamA, aPlane);
102       if (!aPoint.get()) {
103         theError = "One of parameters of distance constraint should be a point";
104         return false;
105       }
106     }
107   }
108   return true;
109 }
110
111 static bool isCoincident(FeaturePtr theFeature1, FeaturePtr theFeature2)
112 {
113   AttributePtr aFeature1PointAttr[2];
114   if(theFeature1->getKind() == SketchPlugin_Line::ID()) {
115     aFeature1PointAttr[0] = theFeature1->attribute(SketchPlugin_Line::START_ID());
116     aFeature1PointAttr[1] = theFeature1->attribute(SketchPlugin_Line::END_ID());
117   } else if(theFeature1->getKind() == SketchPlugin_Arc::ID()) {
118     aFeature1PointAttr[0] = theFeature1->attribute(SketchPlugin_Arc::START_ID());
119     aFeature1PointAttr[1] = theFeature1->attribute(SketchPlugin_Arc::END_ID());
120   }
121
122   std::set<AttributePtr> aRefsList = theFeature1->data()->refsToMe();
123   for(std::set<AttributePtr>::const_iterator aRefIt = aRefsList.begin();
124       aRefIt != aRefsList.end();
125       ++aRefIt) {
126     // Find constraint
127     FeaturePtr aRefFeature = std::dynamic_pointer_cast<ModelAPI_Feature>((*aRefIt)->owner());
128     if(aRefFeature->getKind() != SketchPlugin_ConstraintCoincidence::ID())
129       continue;
130     AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(*aRefIt);
131     AttributePtr anAttr = aRefAttr->attr();
132     if(anAttr != aFeature1PointAttr[0] && anAttr != aFeature1PointAttr[1])
133       continue;
134
135     // Get coincides from constraint.
136     std::set<FeaturePtr> aCoinsides;
137     SketchPlugin_Tools::findCoincidences(aRefFeature,
138                                          SketchPlugin_ConstraintCoincidence::ENTITY_A(),
139                                          aCoinsides);
140     SketchPlugin_Tools::findCoincidences(aRefFeature,
141                                          SketchPlugin_ConstraintCoincidence::ENTITY_B(),
142                                          aCoinsides);
143
144     if(aCoinsides.find(theFeature2) != aCoinsides.end()) {
145       return true;
146     }
147   }
148
149   return false;
150 }
151
152 static bool hasCoincidentPoint(FeaturePtr theFeature1, FeaturePtr theFeature2)
153 {
154   if(theFeature1->getKind() == SketchPlugin_Circle::ID() ||
155       theFeature2->getKind() == SketchPlugin_Circle::ID()) {
156     return false;
157   }
158
159   return (isCoincident(theFeature1, theFeature2) && isCoincident(theFeature2, theFeature1));
160 }
161
162 bool SketchPlugin_TangentAttrValidator::isValid(const AttributePtr& theAttribute,
163                                                 const std::list<std::string>& theArguments,
164                                                 Events_InfoMessage& theError) const
165 {
166   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
167     theError = "The attribute with the %1 type is not processed";
168     theError.arg(theAttribute->attributeType());
169     return false;
170   }
171
172   // there is a check whether the feature contains a point and a linear edge or two point values
173   std::string aParamA = theArguments.front();
174   SessionPtr aMgr = ModelAPI_Session::get();
175   ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
176
177   FeaturePtr anAttributeFeature =
178     std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
179   AttributeRefAttrPtr aRefAttr =
180     std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
181
182   bool isObject = aRefAttr->isObject();
183   ObjectPtr anObject = aRefAttr->object();
184   if (isObject && anObject.get()) {
185     FeaturePtr aRefFea = ModelAPI_Feature::feature(anObject);
186
187     AttributeRefAttrPtr aOtherAttr = anAttributeFeature->data()->refattr(aParamA);
188     ObjectPtr aOtherObject = aOtherAttr->object();
189     FeaturePtr aOtherFea = ModelAPI_Feature::feature(aOtherObject);
190     if (!aOtherFea)
191       return true;
192
193     if ((aRefFea->getKind() == SketchPlugin_Arc::ID() ||
194         aOtherFea->getKind() == SketchPlugin_Arc::ID()) &&
195         !hasCoincidentPoint(aRefFea, aOtherFea))
196       return false;
197
198     if (aRefFea->getKind() == SketchPlugin_Line::ID()) {
199       if (aOtherFea->getKind() != SketchPlugin_Arc::ID() &&
200           aOtherFea->getKind() != SketchPlugin_Circle::ID()) {
201         theError = "It refers to a %1, but %2 is neither an %3 nor %4";
202         theError.arg(SketchPlugin_Line::ID()).arg(aParamA)
203             .arg(SketchPlugin_Arc::ID()).arg(SketchPlugin_Circle::ID());
204         return false;
205       }
206     }
207     else if (aRefFea->getKind() == SketchPlugin_Arc::ID()) {
208       if (aOtherFea->getKind() != SketchPlugin_Line::ID() &&
209         aOtherFea->getKind() != SketchPlugin_Arc::ID()) {
210         theError = "It refers to an %1, but %2 is not a %3 or an %4";
211         theError.arg(SketchPlugin_Arc::ID()).arg(aParamA)
212             .arg(SketchPlugin_Line::ID()).arg(SketchPlugin_Arc::ID());
213         return false;
214       }
215     }
216     else if (aRefFea->getKind() == SketchPlugin_Circle::ID()) {
217       if (aOtherFea->getKind() != SketchPlugin_Line::ID()) {
218         theError = "It refers to an %1, but %2 is not a %3";
219         theError.arg(SketchPlugin_Circle::ID()).arg(aParamA)
220             .arg(SketchPlugin_Line::ID());
221         return false;
222       }
223     }
224     else {
225       theError = "It refers to %1, but should refer to %2 or %3 or %4";
226       theError.arg(aRefFea->getKind()).arg(SketchPlugin_Line::ID())
227           .arg(SketchPlugin_Arc::ID()).arg(SketchPlugin_Circle::ID());
228       return false;
229     }
230     return true;
231   }
232   else {
233     theError = "It uses an empty object";
234     return false;
235   }
236
237   return true;
238 }
239
240 bool SketchPlugin_NotFixedValidator::isValid(const AttributePtr& theAttribute,
241                                              const std::list<std::string>& theArguments,
242                                              Events_InfoMessage& theError) const
243 {
244   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
245     theError = "The attribute with the %1 type is not processed";
246     theError.arg(theAttribute->attributeType());
247     return false;
248   }
249
250   std::shared_ptr<SketchPlugin_Feature> aFeature =
251       std::dynamic_pointer_cast<SketchPlugin_Feature>(theAttribute->owner());
252   if (!aFeature)
253     return true;
254
255   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
256
257   SketchPlugin_Sketch* aSketch = aFeature->sketch();
258   int aNbFeatures = aSketch->numberOfSubs();
259   for (int anInd = 0; anInd < aNbFeatures; anInd++) {
260     FeaturePtr aSubFeature = aSketch->subFeature(anInd);
261     if (aSubFeature->getKind() != SketchPlugin_ConstraintRigid::ID() || aSubFeature == aFeature)
262       continue;
263     AttributeRefAttrPtr aRAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
264         aSubFeature->attribute(SketchPlugin_ConstraintRigid::ENTITY_A()));
265     if (aRefAttr->isObject()) {
266       if (aRefAttr->object() == aRAttr->object()) {
267         ObjectPtr anObject = aRefAttr->object();
268         std::string aName = anObject.get() ? anObject->data()->name() : "";
269         theError = "The object %1 has been already fixed.";
270         theError.arg(aName);
271         return false;
272       }
273     }
274     else if (aRefAttr->attr() == aRAttr->attr()) {
275       AttributePtr anAttribute = aRefAttr->attr();
276       std::string aName = anAttribute.get() ? anAttribute->id() : "";
277       theError = "The attribute %1 has been already fixed.";
278       theError.arg(aName);
279       return false;
280     }
281   }
282   return true;
283 }
284
285 bool SketchPlugin_EqualAttrValidator::isValid(const AttributePtr& theAttribute,
286                                               const std::list<std::string>& theArguments,
287                                               Events_InfoMessage& theError) const
288 {
289   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
290     theError = "The attribute with the %1 type is not processed";
291     theError.arg(theAttribute->attributeType());
292     return false;
293   }
294
295   std::string aParamA = theArguments.front();
296   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
297   AttributeRefAttrPtr aRefAttr[2];
298   aRefAttr[0] = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
299   aRefAttr[1] = aFeature->data()->refattr(aParamA);
300
301   if (!aRefAttr[0]->isObject() || !aRefAttr[1]->isObject()) {
302     theError = "Attributes can not be used in equal constraint";
303     return false;
304   }
305
306   std::string aType[2];
307   std::list<std::string> anArguments;
308   for (int i = 0; i < 2; i++) {
309     ObjectPtr anObject = aRefAttr[i]->object();
310     if (!anObject.get()) {
311       theError = "An empty object is used.";
312       return false;
313     }
314
315     aFeature = ModelAPI_Feature::feature(anObject);
316     if (!aFeature.get()) {
317       theError = "An empty feature is used.";
318       return false;
319     }
320
321     aType[i] = aFeature->getKind();
322     if (aFeature->getKind() != SketchPlugin_Line::ID() &&
323         aFeature->getKind() != SketchPlugin_Circle::ID() &&
324         aFeature->getKind() != SketchPlugin_Arc::ID()) {
325       theError = "The %1 feature kind of attribute is wrong. It should be %2 or %3 or %4";
326       theError.arg(aFeature->getKind()).arg(SketchPlugin_Line::ID())
327           .arg(SketchPlugin_Circle::ID()).arg(SketchPlugin_Arc::ID());
328       // wrong type of attribute
329       return false;
330     }
331   }
332
333   if ((aType[0] == SketchPlugin_Line::ID() || aType[1] == SketchPlugin_Line::ID()) &&
334       aType[0] != aType[1]) {
335     theError = "Feature with kinds %1 and %2 can not be equal.";
336     theError.arg(aType[0]).arg(aType[1]);
337     return false;
338   }
339   return true;
340 }
341
342 bool SketchPlugin_MirrorAttrValidator::isValid(const AttributePtr& theAttribute,
343                                                const std::list<std::string>& theArguments,
344                                                Events_InfoMessage& theError) const
345 {
346   if (theAttribute->attributeType() != ModelAPI_AttributeRefList::typeId()) {
347     theError = "The attribute with the %1 type is not processed";
348     theError.arg(theAttribute->attributeType());
349     return false;
350   }
351
352   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
353   AttributeRefListPtr aSelAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(theAttribute);
354
355   AttributeRefListPtr aRefListOfMirrored = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
356       aFeature->attribute(SketchPlugin_Constraint::ENTITY_C()));
357   std::list<ObjectPtr> aMirroredObjects = aRefListOfMirrored->list();
358
359   for(int anInd = 0; anInd < aSelAttr->size(); anInd++) {
360     ObjectPtr aSelObject = aSelAttr->object(anInd);
361     std::string aName = aSelObject.get() ? aSelObject->data()->name() : "";
362     std::list<ObjectPtr>::iterator aMirIter = aMirroredObjects.begin();
363     for (; aMirIter != aMirroredObjects.end(); aMirIter++)
364       if (aSelObject == *aMirIter) {
365         theError = "The object %1 is a result of mirror";
366         theError.arg(aName);
367         return false;
368       }
369   }
370   return true;
371 }
372
373 bool SketchPlugin_CoincidenceAttrValidator::isValid(const AttributePtr& theAttribute,
374                                                     const std::list<std::string>& theArguments,
375                                                     Events_InfoMessage& theError) const
376 {
377   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
378     theError = "The attribute with the %1 type is not processed";
379     theError.arg(theAttribute->attributeType());
380     return false;
381   }
382
383   // there is a check whether the feature contains a point and a linear edge or two point values
384   std::string aParamA = theArguments.front();
385   SessionPtr aMgr = ModelAPI_Session::get();
386   ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
387
388   FeaturePtr aConstraint = std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
389   AttributeRefAttrPtr aRefAttrA = aConstraint->data()->refattr(aParamA);
390   if (!aRefAttrA) {
391     theError = "The %1 attribute should be %2";
392     theError.arg(aParamA).arg(ModelAPI_AttributeRefAttr::typeId());
393     return false;
394   }
395
396   AttributeRefAttrPtr aRefAttrB =
397     std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
398
399   // first attribute is a point, it may coincide with any object
400   if (!aRefAttrA->isObject())
401     return true;
402   else {
403     ObjectPtr anObject = aRefAttrA->object();
404     if (!anObject.get()) {
405       theError = "%1 attribute has an empty object";
406       theError.arg(aParamA);
407       return false;
408     }
409     FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttrA->object());
410     if (!aFeature.get()) {
411       theError = "%1 attribute has an empty feature";
412       theError.arg(aParamA);
413       return false;
414     }
415
416     if (aFeature->getKind() == SketchPlugin_Point::ID())
417       return true;
418   }
419
420   // second attribute is a point, it may coincide with any object
421   if (!aRefAttrB->isObject())
422     return true;
423   else {
424     FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttrB->object());
425     if (!aFeature) {
426       theError = "%1 attribute has an empty object";
427       theError.arg(theAttribute->id());
428       return false;
429     }
430     if (aFeature->getKind() == SketchPlugin_Point::ID())
431       return true;
432   }
433   theError = "There is no an attribute filled by a point";
434   return false;
435 }
436
437
438 bool SketchPlugin_CopyValidator::isValid(const AttributePtr& theAttribute,
439                                          const std::list<std::string>& theArguments,
440                                          Events_InfoMessage& theError) const
441 {
442   if (theAttribute->attributeType() != ModelAPI_AttributeRefList::typeId()) {
443     theError = "The attribute with the %1 type is not processed";
444     theError.arg(theAttribute->attributeType());
445     return false;
446   }
447
448   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
449   AttributeRefListPtr aSelAttr =
450     std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(theAttribute);
451
452   AttributeRefListPtr aRefListOfInitial = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
453       aFeature->attribute(SketchPlugin_Constraint::ENTITY_A()));
454   AttributeRefListPtr aRefListOfCopied = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
455       aFeature->attribute(SketchPlugin_Constraint::ENTITY_B()));
456   std::list<ObjectPtr> anInitialObjects = aRefListOfInitial->list();
457   std::list<ObjectPtr> aCopiedObjects = aRefListOfCopied->list();
458
459   std::list<ObjectPtr>::iterator anObjIter;
460   for(int anInd = 0; anInd < aSelAttr->size(); anInd++) {
461     ObjectPtr aSelObject = aSelAttr->object(anInd);
462     anObjIter = anInitialObjects.begin();
463     for (; anObjIter != anInitialObjects.end(); anObjIter++)
464       if (aSelObject == *anObjIter)
465         break;
466     if (anObjIter != anInitialObjects.end())
467       continue;
468     anObjIter = aCopiedObjects.begin();
469     for (; anObjIter != aCopiedObjects.end(); anObjIter++)
470       if (aSelObject == *anObjIter) {
471         std::string aName = aSelObject.get() ? aSelObject->data()->name() : "";
472         theError = "The object %1 is a result of copy";
473         theError.arg(aName);
474         return false;
475       }
476   }
477   return true;
478 }
479
480 bool SketchPlugin_SolverErrorValidator::isValid(
481   const std::shared_ptr<ModelAPI_Feature>& theFeature,
482   const std::list<std::string>& theArguments,
483   Events_InfoMessage& theError) const
484 {
485   AttributeStringPtr aAttributeString = theFeature->string(SketchPlugin_Sketch::SOLVER_ERROR());
486
487   if (!aAttributeString->value().empty()) {
488     theError = aAttributeString->value();
489     return false;
490   }
491
492   return true;
493 }
494
495 bool SketchPlugin_SolverErrorValidator::isNotObligatory(std::string theFeature,
496                                                         std::string theAttribute)
497 {
498   return true;
499 }
500
501 static bool hasSameTangentFeature(const std::set<AttributePtr>& theRefsList,
502                                   const FeaturePtr theFeature)
503 {
504   for(std::set<AttributePtr>::const_iterator
505       anIt = theRefsList.cbegin(); anIt != theRefsList.cend(); ++anIt) {
506     std::shared_ptr<ModelAPI_Attribute> aAttr = (*anIt);
507     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
508     if (aFeature->getKind() == SketchPlugin_ConstraintTangent::ID()) {
509       AttributeRefAttrPtr anAttrRefA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
510         aFeature->attribute(SketchPlugin_ConstraintTangent::ENTITY_A()));
511       AttributeRefAttrPtr anAttrRefB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
512         aFeature->attribute(SketchPlugin_ConstraintTangent::ENTITY_B()));
513       if(anAttrRefA.get()) {
514         ResultPtr aResA = std::dynamic_pointer_cast<ModelAPI_Result>(anAttrRefA->object());
515         if(aResA.get()) {
516           DocumentPtr aDoc = aResA->document();
517           if(aDoc.get()) {
518             FeaturePtr aFeatureA = aDoc->feature(aResA);
519             if(aFeatureA.get() && aFeatureA == theFeature) {
520               return true;
521             }
522           }
523         }
524       }
525       if(anAttrRefB.get()) {
526         ResultPtr aResB = std::dynamic_pointer_cast<ModelAPI_Result>(anAttrRefB->object());
527         if(aResB.get()) {
528           DocumentPtr aDoc = aResB->document();
529           if(aDoc.get()) {
530             FeaturePtr aFeatureB = aDoc->feature(aResB);
531             if(aFeatureB.get() && aFeatureB == theFeature) {
532               return true;
533             }
534           }
535         }
536       }
537     }
538   }
539   return false;
540 }
541
542 bool SketchPlugin_FilletVertexValidator::isValid(const AttributePtr& theAttribute,
543                                                  const std::list<std::string>& theArguments,
544                                                  Events_InfoMessage& theError) const
545 {
546   std::shared_ptr<SketchPlugin_ConstraintFillet> aFilletFeature =
547     std::dynamic_pointer_cast<SketchPlugin_ConstraintFillet>(theAttribute->owner());
548   AttributeRefAttrListPtr aPointsRefList =
549     std::dynamic_pointer_cast<ModelAPI_AttributeRefAttrList>(theAttribute);
550   if(aPointsRefList->size() == 0) {
551     theError = "Error: List of points is empty.";
552     return false;
553   }
554
555   std::map<AttributePtr, SketchPlugin_ConstraintFillet::FilletFeatures> aPointsFeaturesMap =
556     aFilletFeature->pointsFeaturesMap();
557   std::set<AttributePtr> aSetOfPointsOnResultEdges;
558   for(std::map<AttributePtr, SketchPlugin_ConstraintFillet::FilletFeatures>::iterator
559       aPointsIter = aPointsFeaturesMap.begin();
560       aPointsIter != aPointsFeaturesMap.end();
561       ++aPointsIter) {
562     const SketchPlugin_ConstraintFillet::FilletFeatures& aFeatures = aPointsIter->second;
563     const std::list<FeaturePtr>& aResultEdges = aFeatures.resultEdges;
564     for(std::list<FeaturePtr>::const_iterator aResultIter = aResultEdges.cbegin();
565         aResultIter != aResultEdges.cend();
566         ++aResultIter) {
567       FeaturePtr aResultFeature = *aResultIter;
568       if(aResultFeature->getKind() == SketchPlugin_Line::ID()) {
569         aSetOfPointsOnResultEdges.insert(aResultFeature->attribute(SketchPlugin_Line::START_ID()));
570         aSetOfPointsOnResultEdges.insert(aResultFeature->attribute(SketchPlugin_Line::END_ID()));
571       } else if(aResultFeature->getKind() == SketchPlugin_Arc::ID()) {
572         aSetOfPointsOnResultEdges.insert(aResultFeature->attribute(SketchPlugin_Arc::START_ID()));
573         aSetOfPointsOnResultEdges.insert(aResultFeature->attribute(SketchPlugin_Arc::END_ID()));
574       }
575     }
576   }
577
578   std::list<std::pair<ObjectPtr, AttributePtr>> aPointsList = aPointsRefList->list();
579   for(std::list<std::pair<ObjectPtr, AttributePtr>>::const_iterator
580       aPointsIt = aPointsList.cbegin(); aPointsIt != aPointsList.cend(); aPointsIt++) {
581     ObjectPtr anObject = (*aPointsIt).first;
582     AttributePtr aPointAttribute = (*aPointsIt).second;
583     if (!aPointAttribute.get())
584         return false;
585     std::shared_ptr<GeomAPI_Pnt2d> aSelectedPnt =
586       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aPointAttribute)->pnt();
587
588     // If we alredy have some result then:
589     // - if it is the same point all ok, just skip it
590     // - if it is point on the fillet result edge then it is not valid
591     if(!aPointsFeaturesMap.empty()) {
592       if(aPointsFeaturesMap.find(aPointAttribute) != aPointsFeaturesMap.end()) {
593         continue;
594       }
595
596       // Check that selected point not on the one of the fillet result edge.
597       if(aSetOfPointsOnResultEdges.find(aPointAttribute) != aSetOfPointsOnResultEdges.end()) {
598         return false;
599       }
600     }
601
602     // Obtain constraint coincidence for the fillet point.
603     const std::set<AttributePtr>& aRefsList = aPointAttribute->owner()->data()->refsToMe();
604     FeaturePtr aConstraintCoincidence;
605     for(std::set<AttributePtr>::const_iterator anIt = aRefsList.cbegin();
606         anIt != aRefsList.cend(); ++anIt) {
607       std::shared_ptr<ModelAPI_Attribute> aAttr = (*anIt);
608       FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
609       if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
610         AttributeRefAttrPtr anAttrRefA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
611           aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_A()));
612         AttributeRefAttrPtr anAttrRefB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
613           aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_B()));
614         if(anAttrRefA.get() && !anAttrRefA->isObject()) {
615           AttributePtr anAttrA = anAttrRefA->attr();
616           if(aPointAttribute == anAttrA) {
617             aConstraintCoincidence = aConstrFeature;
618             break;
619           }
620         }
621         if(anAttrRefB.get() && !anAttrRefB->isObject()) {
622           AttributePtr anAttrB = anAttrRefB->attr();
623           if(aPointAttribute == anAttrB) {
624             aConstraintCoincidence = aConstrFeature;
625             break;
626           }
627         }
628       }
629     }
630
631     if(!aConstraintCoincidence.get()) {
632       theError = "Error: one of the selected point does not have coicidence.";
633       return false;
634     }
635
636     // Get coincides from constraint.
637     std::set<FeaturePtr> aCoinsides;
638     SketchPlugin_Tools::findCoincidences(aConstraintCoincidence,
639                                          SketchPlugin_ConstraintCoincidence::ENTITY_A(),
640                                          aCoinsides);
641     SketchPlugin_Tools::findCoincidences(aConstraintCoincidence,
642                                          SketchPlugin_ConstraintCoincidence::ENTITY_B(),
643                                          aCoinsides);
644
645     // Remove points from set of coincides.
646     std::set<FeaturePtr> aNewSetOfCoincides;
647     for(std::set<FeaturePtr>::iterator anIt = aCoinsides.begin();
648         anIt != aCoinsides.end(); ++anIt) {
649       if((*anIt)->getKind() != SketchPlugin_Line::ID() &&
650          (*anIt)->getKind() != SketchPlugin_Arc::ID()) {
651            continue;
652       }
653       if((*anIt)->getKind() == SketchPlugin_Arc::ID()) {
654         AttributePtr anArcCenter = (*anIt)->attribute(SketchPlugin_Arc::CENTER_ID());
655         std::shared_ptr<GeomAPI_Pnt2d> anArcCenterPnt =
656           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anArcCenter)->pnt();
657         double aDistSelectedArcCenter = aSelectedPnt->distance(anArcCenterPnt);
658         if(aDistSelectedArcCenter < tolerance) {
659           continue;
660         }
661       }
662       aNewSetOfCoincides.insert(*anIt);
663     }
664     aCoinsides = aNewSetOfCoincides;
665
666     // If we still have more than two coincides remove auxilary entities from set of coincides.
667     if(aCoinsides.size() > 2) {
668       aNewSetOfCoincides.clear();
669       for(std::set<FeaturePtr>::iterator anIt = aCoinsides.begin();
670           anIt != aCoinsides.end(); ++anIt) {
671         if(!(*anIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) {
672           aNewSetOfCoincides.insert(*anIt);
673         }
674       }
675       aCoinsides = aNewSetOfCoincides;
676     }
677
678     if(aCoinsides.size() != 2) {
679       theError = "Error: One of the selected points does not have two suitable edges for fillet.";
680       return false;
681     }
682
683     // Check that selected edges don't have tangent constraint.
684     std::set<FeaturePtr>::iterator anIt = aCoinsides.begin();
685     FeaturePtr aFirstFeature = *anIt++;
686     FeaturePtr aSecondFeature = *anIt;
687     const std::set<AttributePtr>& aFirstFeatureRefsList = aFirstFeature->data()->refsToMe();
688     if(hasSameTangentFeature(aFirstFeatureRefsList, aSecondFeature)) {
689       theError = "Error: Edges in selected point has tangent constraint.";
690       return false;
691     }
692
693     std::list<ResultPtr> aFirstResults = aFirstFeature->results();
694     for(std::list<ResultPtr>::iterator aResIt = aFirstResults.begin();
695         aResIt != aFirstResults.end(); ++aResIt) {
696       ResultPtr aRes = *aResIt;
697       const std::set<AttributePtr>& aResRefsList = aRes->data()->refsToMe();
698       if(hasSameTangentFeature(aResRefsList, aSecondFeature)) {
699         theError = "Error: Edges in selected point has tangent constraint.";
700         return false;
701       }
702     }
703
704     // Check that lines not collinear
705     if(aFirstFeature->getKind() == SketchPlugin_Line::ID() &&
706         aSecondFeature->getKind() == SketchPlugin_Line::ID()) {
707       std::string aStartAttr = SketchPlugin_Line::START_ID();
708       std::string anEndAttr = SketchPlugin_Line::END_ID();
709       std::shared_ptr<GeomAPI_Pnt2d> aFirstStartPnt, aFirstEndPnt, aSecondStartPnt, aSecondEndPnt;
710       aFirstStartPnt =
711         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
712         aFirstFeature->attribute(aStartAttr))->pnt();
713       aFirstEndPnt =
714         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aFirstFeature->attribute(anEndAttr))->pnt();
715       aSecondStartPnt =
716         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
717         aSecondFeature->attribute(aStartAttr))->pnt();
718       aSecondEndPnt =
719         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
720         aSecondFeature->attribute(anEndAttr))->pnt();
721       double aCheck1 =
722         fabs((aFirstEndPnt->x() - aFirstStartPnt->x()) *
723         (aSecondStartPnt->y() - aFirstStartPnt->y()) -
724         (aSecondStartPnt->x() - aFirstStartPnt->x()) * (aFirstEndPnt->y() - aFirstStartPnt->y()));
725       double aCheck2 =
726         fabs((aFirstEndPnt->x() - aFirstStartPnt->x()) *
727         (aSecondEndPnt->y() - aFirstStartPnt->y()) -
728         (aSecondEndPnt->x() - aFirstStartPnt->x()) * (aFirstEndPnt->y() - aFirstStartPnt->y()));
729       if(aCheck1 < 1.e-7 && aCheck2 < 1.e-7) {
730         return false;
731       }
732     }
733   }
734
735   return true;
736 }
737
738 bool SketchPlugin_MiddlePointAttrValidator::isValid(const AttributePtr& theAttribute,
739                                                     const std::list<std::string>& theArguments,
740                                                     Events_InfoMessage& theError) const
741 {
742   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
743     theError = "The attribute with the %1 type is not processed";
744     theError.arg(theAttribute->attributeType());
745     return false;
746   }
747
748   // there is a check whether the feature contains a point and a linear edge or two point values
749   std::string aParamA = theArguments.front();
750   SessionPtr aMgr = ModelAPI_Session::get();
751   ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
752
753   FeaturePtr anAttributeFeature =
754     std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
755   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
756   AttributeRefAttrPtr anOtherAttr = anAttributeFeature->data()->refattr(aParamA);
757
758   AttributeRefAttrPtr aRefAttrs[2] = {aRefAttr, anOtherAttr};
759   int aNbPoints = 0;
760   int aNbLines = 0;
761   for (int i = 0; i < 2; ++i) {
762     if (!aRefAttrs[i]->isObject())
763       ++aNbPoints;
764     else {
765       FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttrs[i]->object());
766       if (!aFeature) {
767         if (aNbPoints + aNbLines != 0)
768           return true;
769         else continue;
770       }
771
772       if (aFeature->getKind() == SketchPlugin_Point::ID())
773         ++aNbPoints;
774       else if (aFeature->getKind() == SketchPlugin_Line::ID())
775         ++aNbLines;
776     }
777   }
778
779   if (aNbPoints != 1 || aNbLines != 1) {
780     theError = "Middle point constraint allows points and lines only";
781     return false;
782   }
783   return true;
784 }
785
786 bool SketchPlugin_ArcTangentPointValidator::isValid(const AttributePtr& theAttribute,
787                                                     const std::list<std::string>& /*theArguments*/,
788                                                     Events_InfoMessage& theError) const
789 {
790   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
791     theError = "The attribute with the %1 type is not processed";
792     theError.arg(theAttribute->attributeType());
793     return false;
794   }
795   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
796   AttributePtr anAttr = aRefAttr->attr();
797   if (!anAttr) {
798     theError = "The attribute %1 should be a point";
799     theError.arg(theAttribute->id());
800     return false;
801   }
802
803   FeaturePtr anAttrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anAttr->owner());
804   const std::string& aFeatureType = anAttrFeature->getKind();
805   if (aFeatureType == SketchPlugin_Arc::ID()) {
806     // selected point should not be a center of arc
807     const std::string& aPntId = anAttr->id();
808     if (aPntId != SketchPlugin_Arc::START_ID() && aPntId != SketchPlugin_Arc::END_ID()) {
809       theError = "The attribute %1 is not supported";
810       theError.arg(aPntId);
811       return false;
812     }
813   }
814   else if (aFeatureType == SketchPlugin_Line::ID()) {
815     // selected point should be bound point of line
816     const std::string& aPntId = anAttr->id();
817     if (aPntId != SketchPlugin_Line::START_ID() && aPntId != SketchPlugin_Line::END_ID()) {
818       theError = "The attribute %1 is not supported";
819       theError.arg(aPntId);
820       return false;
821     }
822   }
823   else {
824     theError = "Unable to build tangent arc on %1";
825     theError.arg(anAttrFeature->getKind());
826     return false;
827   }
828
829   // Check the tangent point is equal to arc end
830   FeaturePtr anArc = std::dynamic_pointer_cast<ModelAPI_Feature>(aRefAttr->owner());
831   std::shared_ptr<GeomDataAPI_Point2D> anEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
832       anArc->attribute(SketchPlugin_Arc::END_ID()));
833   if (anEndPoint->isInitialized()) {
834     std::shared_ptr<GeomDataAPI_Point2D> aTangPt =
835         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttr);
836     if (aTangPt->pnt()->distance(anEndPoint->pnt()) < tolerance) {
837       theError = "Unable to build arc on same points";
838       return false;
839     }
840   }
841
842   return true;
843 }
844
845 bool SketchPlugin_IntersectionValidator::isValid(const AttributePtr& theAttribute,
846                                                  const std::list<std::string>& theArguments,
847                                                  Events_InfoMessage& theError) const
848 {
849   if (theAttribute->attributeType() != ModelAPI_AttributeSelection::typeId()) {
850     theError = "The attribute with the %1 type is not processed";
851     theError.arg(theAttribute->attributeType());
852     return false;
853   }
854   AttributeSelectionPtr aLineAttr =
855                        std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(theAttribute);
856   std::shared_ptr<GeomAPI_Edge> anEdge;
857   if(aLineAttr && aLineAttr->value() && aLineAttr->value()->isEdge()) {
858     anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(aLineAttr->value()));
859   } else if(aLineAttr->context() &&
860             aLineAttr->context()->shape() && aLineAttr->context()->shape()->isEdge()) {
861     anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(aLineAttr->context()->shape()));
862   }
863
864   if (!anEdge || !anEdge->isLine()) {
865     theError = "The attribute %1 should be a line";
866     theError.arg(theAttribute->id());
867     return false;
868   }
869
870   std::shared_ptr<GeomAPI_Dir> aLineDir = anEdge->line()->direction();
871
872   // find a sketch
873   std::shared_ptr<SketchPlugin_Sketch> aSketch;
874   std::set<AttributePtr> aRefs = aLineAttr->owner()->data()->refsToMe();
875   std::set<AttributePtr>::const_iterator anIt = aRefs.begin();
876   for (; anIt != aRefs.end(); ++anIt) {
877     CompositeFeaturePtr aComp =
878         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>((*anIt)->owner());
879     if (aComp && aComp->getKind() == SketchPlugin_Sketch::ID()) {
880       aSketch = std::dynamic_pointer_cast<SketchPlugin_Sketch>(aComp);
881       break;
882     }
883   }
884   if (!aSketch) {
885     theError = "There is no sketch referring to the current feature";
886     return false;
887   }
888
889   std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
890   std::shared_ptr<GeomAPI_Dir> aNormal = aPlane->direction();
891   return fabs(aNormal->dot(aLineDir)) > tolerance * tolerance;
892 }
893
894 bool SketchPlugin_SplitValidator::isValid(const AttributePtr& theAttribute,
895                                           const std::list<std::string>& theArguments,
896                                           Events_InfoMessage& theError) const
897 {
898   bool aValid = false;
899
900   if (theAttribute->attributeType() != ModelAPI_AttributeReference::typeId()) {
901     theError = "The attribute with the %1 type is not processed";
902     theError.arg(theAttribute->attributeType());
903     return aValid;
904   }
905   AttributeReferencePtr aFeatureAttr =
906             std::dynamic_pointer_cast<ModelAPI_AttributeReference>(theAttribute);
907
908   ObjectPtr anAttrObject = aFeatureAttr->value();
909   FeaturePtr anAttrFeature = ModelAPI_Feature::feature(anAttrObject);
910   if (!anAttrFeature)
911     return aValid;
912
913   std::string aKind = anAttrFeature->getKind();
914   if (aKind == SketchPlugin_Line::ID() ||
915       aKind == SketchPlugin_Arc::ID() ||
916       aKind == SketchPlugin_Circle::ID()) {
917
918     std::set<GeomShapePtr> anEdgeShapes;
919     ModelAPI_Tools::shapesOfType(anAttrFeature, GeomAPI_Shape::EDGE, anEdgeShapes);
920     if (anEdgeShapes.empty() || anEdgeShapes.size() > 1 /*there case has not existed yet*/)
921       return aValid;
922
923     // coincidences to the feature
924     std::set<std::shared_ptr<GeomDataAPI_Point2D> > aRefAttributes;
925     ModelGeomAlgo_Point2D::getPointsOfReference(anAttrFeature,
926                         SketchPlugin_ConstraintCoincidence::ID(),
927                         aRefAttributes, SketchPlugin_Point::ID(), SketchPlugin_Point::COORD_ID());
928
929     GeomShapePtr anAttrShape = *anEdgeShapes.begin();
930     std::shared_ptr<SketchPlugin_Feature> aSFeature =
931                                  std::dynamic_pointer_cast<SketchPlugin_Feature>(anAttrFeature);
932     SketchPlugin_Sketch* aSketch = aSFeature->sketch();
933
934     std::shared_ptr<ModelAPI_Data> aData = aSketch->data();
935     std::shared_ptr<GeomDataAPI_Point> aC = std::dynamic_pointer_cast<GeomDataAPI_Point>(
936         aData->attribute(SketchPlugin_Sketch::ORIGIN_ID()));
937     std::shared_ptr<GeomDataAPI_Dir> aX = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
938         aData->attribute(SketchPlugin_Sketch::DIRX_ID()));
939     std::shared_ptr<GeomDataAPI_Dir> aNorm = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
940         aData->attribute(SketchPlugin_Sketch::NORM_ID()));
941     std::shared_ptr<GeomAPI_Dir> aDirY(new GeomAPI_Dir(aNorm->dir()->cross(aX->dir())));
942
943     std::list<std::shared_ptr<GeomAPI_Pnt> > aPoints;
944     std::map<std::shared_ptr<GeomDataAPI_Point2D>, std::shared_ptr<GeomAPI_Pnt> >
945       aPointToAttributes;
946     ModelGeomAlgo_Point2D::getPointsInsideShape(anAttrShape, aRefAttributes, aC->pnt(),
947                                                 aX->dir(), aDirY, aPoints, aPointToAttributes);
948
949     int aCoincidentToFeature = (int)aPoints.size();
950     if (aKind == SketchPlugin_Circle::ID())
951       aValid = aCoincidentToFeature >= 2;
952     else
953       aValid = aCoincidentToFeature >= 1;
954   }
955
956   return aValid;
957 }
958
959 bool SketchPlugin_ProjectionValidator::isValid(const AttributePtr& theAttribute,
960                                                const std::list<std::string>& theArguments,
961                                                Events_InfoMessage& theError) const
962 {
963   if (theAttribute->attributeType() != ModelAPI_AttributeSelection::typeId()) {
964     theError = "The attribute with the %1 type is not processed";
965     theError.arg(theAttribute->attributeType());
966     return false;
967   }
968
969   AttributeSelectionPtr aFeatureAttr =
970       std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(theAttribute);
971   std::shared_ptr<GeomAPI_Edge> anEdge;
972   if (aFeatureAttr.get()) {
973     GeomShapePtr aVal = aFeatureAttr->value();
974     ResultPtr aRes = aFeatureAttr->context();
975     if(aFeatureAttr->value() && aFeatureAttr->value()->isEdge()) {
976       anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(aFeatureAttr->value()));
977     } else if(aFeatureAttr->context() && aFeatureAttr->context()->shape() &&
978               aFeatureAttr->context()->shape()->isEdge()) {
979       anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(aFeatureAttr->context()->shape()));
980     }
981   }
982   if (!anEdge) {
983     theError = "The attribute %1 should be an edge";
984     theError.arg(theAttribute->id());
985     return false;
986   }
987
988   // find a sketch
989   std::shared_ptr<SketchPlugin_Sketch> aSketch;
990   std::set<AttributePtr> aRefs = theAttribute->owner()->data()->refsToMe();
991   std::set<AttributePtr>::const_iterator anIt = aRefs.begin();
992   for (; anIt != aRefs.end(); ++anIt) {
993     CompositeFeaturePtr aComp =
994         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>((*anIt)->owner());
995     if (aComp && aComp->getKind() == SketchPlugin_Sketch::ID()) {
996       aSketch = std::dynamic_pointer_cast<SketchPlugin_Sketch>(aComp);
997       break;
998     }
999   }
1000   if (!aSketch) {
1001     theError = "There is no sketch referring to the current feature";
1002     return false;
1003   }
1004
1005   std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
1006   std::shared_ptr<GeomAPI_Dir> aNormal = aPlane->direction();
1007   std::shared_ptr<GeomAPI_Pnt> anOrigin = aPlane->location();
1008
1009   if (anEdge->isLine()) {
1010     std::shared_ptr<GeomAPI_Lin> aLine = anEdge->line();
1011     std::shared_ptr<GeomAPI_Dir> aLineDir = aLine->direction();
1012     std::shared_ptr<GeomAPI_Pnt> aLineLoc = aLine->location();
1013     double aDot = aNormal->dot(aLineDir);
1014     double aDist = aLineLoc->xyz()->decreased(anOrigin->xyz())->dot(aNormal->xyz());
1015     bool aValid = (fabs(aDot) >= tolerance && fabs(aDot) < 1.0 - tolerance) ||
1016            (fabs(aDot) < tolerance && fabs(aDist) > tolerance);
1017     if (!aValid)
1018       theError = "Error: Edge is already in the sketch plane.";
1019     return aValid;
1020   }
1021   else if (anEdge->isCircle() || anEdge->isArc()) {
1022     std::shared_ptr<GeomAPI_Circ> aCircle = anEdge->circle();
1023     std::shared_ptr<GeomAPI_Dir> aCircNormal = aCircle->normal();
1024     std::shared_ptr<GeomAPI_Pnt> aCircCenter = aCircle->center();
1025     double aDot = fabs(aNormal->dot(aCircNormal));
1026     double aDist = aCircCenter->xyz()->decreased(anOrigin->xyz())->dot(aNormal->xyz());
1027     bool aValid = fabs(aDot - 1.0) < tolerance * tolerance && fabs(aDist) > tolerance;
1028     if (!aValid)
1029       theError.arg(anEdge->isCircle() ? "Error: Cirlce is already in the sketch plane."
1030                                       : "Error: Arc is already in the sketch plane.");
1031     return aValid;
1032   }
1033
1034   theError = "Error: Selected object is not line, circle or arc.";
1035   return false;
1036 }