Salome HOME
Issue #2024: Redesign of circle and arc of circle
[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_ConstraintRigid.h"
14 #include "SketchPlugin_ConstraintTangent.h"
15 #include "SketchPlugin_Fillet.h"
16 #include "SketchPlugin_Line.h"
17 #include "SketchPlugin_MacroCircle.h"
18 #include "SketchPlugin_Point.h"
19 #include "SketchPlugin_Sketch.h"
20 #include "SketchPlugin_Trim.h"
21 #include "SketchPlugin_Tools.h"
22
23 #include "SketcherPrs_Tools.h"
24
25 #include <Events_InfoMessage.h>
26
27 #include <ModelAPI_Data.h>
28 #include <ModelAPI_Validator.h>
29 #include <ModelAPI_AttributeDouble.h>
30 #include <ModelAPI_AttributeRefAttr.h>
31
32 #include <ModelAPI_AttributeRefAttrList.h>
33 #include <ModelAPI_AttributeRefList.h>
34 #include <ModelAPI_AttributeSelectionList.h>
35 #include <ModelAPI_AttributeString.h>
36 #include <ModelAPI_Session.h>
37 #include <ModelAPI_Tools.h>
38 #include <ModelAPI_ResultConstruction.h>
39
40 #include <ModelGeomAlgo_Point2D.h>
41 #include <ModelGeomAlgo_Shape.h>
42
43 #include <GeomAPI_Circ.h>
44 #include <GeomAPI_Dir2d.h>
45 #include <GeomAPI_Lin.h>
46 #include <GeomAPI_Edge.h>
47 #include <GeomAPI_Vertex.h>
48 #include <GeomDataAPI_Point2D.h>
49
50 #include <algorithm>
51 #include <cmath>
52
53 const double tolerance = 1.e-7;
54
55 bool SketchPlugin_DistanceAttrValidator::isValid(const AttributePtr& theAttribute,
56                                                  const std::list<std::string>& theArguments,
57                                                  Events_InfoMessage& theError) const
58 {
59   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
60     theError = "The attribute with the %1 type is not processed";
61     theError.arg(theAttribute->attributeType());
62     return false;
63   }
64
65   // there is a check whether the feature contains a point and a linear edge or two point values
66   std::string aParamA = theArguments.front();
67   SessionPtr aMgr = ModelAPI_Session::get();
68   ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
69
70   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>
71                                                                       (theAttribute);
72   bool isObject = aRefAttr->isObject();
73   if (!isObject) {
74     // an attribute is a point. A point value is valid always for the distance
75     return true;
76   } else {
77     // 1. check whether the references object is a linear
78     ObjectPtr anObject = aRefAttr->object();
79
80     const ModelAPI_AttributeValidator* aShapeValidator =
81       dynamic_cast<const ModelAPI_AttributeValidator*>(
82       aFactory->validator("GeomValidators_ShapeType"));
83     std::list<std::string> anArguments;
84     anArguments.push_back("circle");
85     Events_InfoMessage aCircleError;
86     bool aShapeValid = aShapeValidator->isValid(aRefAttr, anArguments, aCircleError);
87     // the circle line is not a valid case
88     if (aShapeValid) {
89       theError = "Circle can not be used in distance constraint";
90       return false;
91     }
92
93     anArguments.clear();
94     anArguments.push_back("line");
95     Events_InfoMessage aLineError;
96     aShapeValid = aShapeValidator->isValid(aRefAttr, anArguments, aLineError);
97     // if the attribute value is not a line, that means it is a vertex. A vertex is always valid
98     if (aShapeValid) {
99       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
100       // If it is a line then we have to check that first attribute id not a line
101       std::shared_ptr<SketchPlugin_Feature> aSFeature =
102         std::dynamic_pointer_cast<SketchPlugin_Feature>(theAttribute->owner());
103       SketchPlugin_Sketch* aSketch = aSFeature->sketch();
104       std::shared_ptr<GeomAPI_Ax3> aPlane = SketchPlugin_Sketch::plane(aSketch);
105       std::shared_ptr<GeomDataAPI_Point2D> aPoint = SketcherPrs_Tools::getFeaturePoint(
106         aFeature->data(), aParamA, aPlane);
107       if (!aPoint.get()) {
108         theError = "One of parameters of distance constraint should be a point";
109         return false;
110       }
111     }
112   }
113   return true;
114 }
115
116 bool SketchPlugin_TangentAttrValidator::isValid(const AttributePtr& theAttribute,
117                                                 const std::list<std::string>& theArguments,
118                                                 Events_InfoMessage& theError) const
119 {
120   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
121     theError = "The attribute with the %1 type is not processed";
122     theError.arg(theAttribute->attributeType());
123     return false;
124   }
125
126   // there is a check whether the feature contains a point and a linear edge or two point values
127   std::string aParamA = theArguments.front();
128   SessionPtr aMgr = ModelAPI_Session::get();
129   ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
130
131   FeaturePtr anAttributeFeature =
132     std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
133   AttributeRefAttrPtr aRefAttr =
134     std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
135
136   bool isObject = aRefAttr->isObject();
137   ObjectPtr anObject = aRefAttr->object();
138   if (isObject && anObject.get()) {
139     FeaturePtr aRefFea = ModelAPI_Feature::feature(anObject);
140
141     AttributeRefAttrPtr aOtherAttr = anAttributeFeature->data()->refattr(aParamA);
142     ObjectPtr aOtherObject = aOtherAttr->object();
143     FeaturePtr aOtherFea = ModelAPI_Feature::feature(aOtherObject);
144     if (!aOtherFea)
145       return true;
146
147     if (aRefFea->getKind() == SketchPlugin_Line::ID()) {
148       if (aOtherFea->getKind() != SketchPlugin_Arc::ID() &&
149           aOtherFea->getKind() != SketchPlugin_Circle::ID()) {
150         theError = "It refers to a %1, but %2 is neither an %3 nor %4";
151         theError.arg(SketchPlugin_Line::ID()).arg(aParamA)
152             .arg(SketchPlugin_Arc::ID()).arg(SketchPlugin_Circle::ID());
153         return false;
154       }
155     }
156     else if (aRefFea->getKind() == SketchPlugin_Arc::ID() ||
157              aRefFea->getKind() == SketchPlugin_Circle::ID()) {
158       if (aOtherFea->getKind() != SketchPlugin_Line::ID() &&
159           aOtherFea->getKind() != SketchPlugin_Arc::ID() &&
160           aOtherFea->getKind() != SketchPlugin_Circle::ID()) {
161         theError = "It refers to an %1, but %2 is not a %3 or an %4 or a %5";
162         theError.arg(SketchPlugin_Arc::ID()).arg(aParamA)
163             .arg(SketchPlugin_Line::ID()).arg(SketchPlugin_Arc::ID())
164             .arg(SketchPlugin_Circle::ID());
165         return false;
166       }
167     }
168     else {
169       theError = "It refers to %1, but should refer to %2 or %3 or %4";
170       theError.arg(aRefFea->getKind()).arg(SketchPlugin_Line::ID())
171           .arg(SketchPlugin_Arc::ID()).arg(SketchPlugin_Circle::ID());
172       return false;
173     }
174     return true;
175   }
176   else {
177     theError = "It uses an empty object";
178     return false;
179   }
180
181   return true;
182 }
183
184 bool SketchPlugin_NotFixedValidator::isValid(const AttributePtr& theAttribute,
185                                              const std::list<std::string>& theArguments,
186                                              Events_InfoMessage& theError) const
187 {
188   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
189     theError = "The attribute with the %1 type is not processed";
190     theError.arg(theAttribute->attributeType());
191     return false;
192   }
193
194   std::shared_ptr<SketchPlugin_Feature> aFeature =
195       std::dynamic_pointer_cast<SketchPlugin_Feature>(theAttribute->owner());
196   if (!aFeature)
197     return true;
198
199   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
200
201   SketchPlugin_Sketch* aSketch = aFeature->sketch();
202   int aNbFeatures = aSketch->numberOfSubs();
203   for (int anInd = 0; anInd < aNbFeatures; anInd++) {
204     FeaturePtr aSubFeature = aSketch->subFeature(anInd);
205     if (aSubFeature->getKind() != SketchPlugin_ConstraintRigid::ID() || aSubFeature == aFeature)
206       continue;
207     AttributeRefAttrPtr aRAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
208         aSubFeature->attribute(SketchPlugin_ConstraintRigid::ENTITY_A()));
209     if (aRefAttr->isObject()) {
210       if (aRefAttr->object() == aRAttr->object()) {
211         ObjectPtr anObject = aRefAttr->object();
212         std::string aName = anObject.get() ? anObject->data()->name() : "";
213         theError = "The object %1 has been already fixed.";
214         theError.arg(aName);
215         return false;
216       }
217     }
218     else if (aRefAttr->attr() == aRAttr->attr()) {
219       AttributePtr anAttribute = aRefAttr->attr();
220       std::string aName = anAttribute.get() ? anAttribute->id() : "";
221       theError = "The attribute %1 has been already fixed.";
222       theError.arg(aName);
223       return false;
224     }
225   }
226   return true;
227 }
228
229 bool SketchPlugin_EqualAttrValidator::isValid(const AttributePtr& theAttribute,
230                                               const std::list<std::string>& theArguments,
231                                               Events_InfoMessage& theError) const
232 {
233   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
234     theError = "The attribute with the %1 type is not processed";
235     theError.arg(theAttribute->attributeType());
236     return false;
237   }
238
239   std::string aParamA = theArguments.front();
240   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
241   AttributeRefAttrPtr aRefAttr[2];
242   aRefAttr[0] = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
243   aRefAttr[1] = aFeature->data()->refattr(aParamA);
244
245   if (!aRefAttr[0]->isObject() || !aRefAttr[1]->isObject()) {
246     theError = "Attributes can not be used in equal constraint";
247     return false;
248   }
249
250   std::string aType[2];
251   std::list<std::string> anArguments;
252   for (int i = 0; i < 2; i++) {
253     ObjectPtr anObject = aRefAttr[i]->object();
254     if (!anObject.get()) {
255       theError = "An empty object is used.";
256       return false;
257     }
258
259     aFeature = ModelAPI_Feature::feature(anObject);
260     if (!aFeature.get()) {
261       theError = "An empty feature is used.";
262       return false;
263     }
264
265     aType[i] = aFeature->getKind();
266     if (aFeature->getKind() != SketchPlugin_Line::ID() &&
267         aFeature->getKind() != SketchPlugin_Circle::ID() &&
268         aFeature->getKind() != SketchPlugin_Arc::ID()) {
269       theError = "The %1 feature kind of attribute is wrong. It should be %2 or %3 or %4";
270       theError.arg(aFeature->getKind()).arg(SketchPlugin_Line::ID())
271           .arg(SketchPlugin_Circle::ID()).arg(SketchPlugin_Arc::ID());
272       // wrong type of attribute
273       return false;
274     }
275   }
276
277   if ((aType[0] == SketchPlugin_Line::ID() || aType[1] == SketchPlugin_Line::ID()) &&
278       aType[0] != aType[1]) {
279     theError = "Feature with kinds %1 and %2 can not be equal.";
280     theError.arg(aType[0]).arg(aType[1]);
281     return false;
282   }
283   return true;
284 }
285
286 bool SketchPlugin_MirrorAttrValidator::isValid(const AttributePtr& theAttribute,
287                                                const std::list<std::string>& theArguments,
288                                                Events_InfoMessage& theError) const
289 {
290   if (theAttribute->attributeType() != ModelAPI_AttributeRefList::typeId()) {
291     theError = "The attribute with the %1 type is not processed";
292     theError.arg(theAttribute->attributeType());
293     return false;
294   }
295
296   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
297   AttributeRefListPtr aSelAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(theAttribute);
298
299   AttributeRefListPtr aRefListOfMirrored = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
300       aFeature->attribute(SketchPlugin_Constraint::ENTITY_C()));
301   std::list<ObjectPtr> aMirroredObjects = aRefListOfMirrored->list();
302
303   for(int anInd = 0; anInd < aSelAttr->size(); anInd++) {
304     ObjectPtr aSelObject = aSelAttr->object(anInd);
305     std::string aName = aSelObject.get() ? aSelObject->data()->name() : "";
306     std::list<ObjectPtr>::iterator aMirIter = aMirroredObjects.begin();
307     for (; aMirIter != aMirroredObjects.end(); aMirIter++)
308       if (aSelObject == *aMirIter) {
309         theError = "The object %1 is a result of mirror";
310         theError.arg(aName);
311         return false;
312       }
313   }
314   return true;
315 }
316
317 bool SketchPlugin_CoincidenceAttrValidator::isValid(const AttributePtr& theAttribute,
318                                                     const std::list<std::string>& theArguments,
319                                                     Events_InfoMessage& theError) const
320 {
321   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
322     theError = "The attribute with the %1 type is not processed";
323     theError.arg(theAttribute->attributeType());
324     return false;
325   }
326
327   // there is a check whether the feature contains a point and a linear edge or two point values
328   std::string aParamA = theArguments.front();
329   SessionPtr aMgr = ModelAPI_Session::get();
330   ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
331
332   FeaturePtr aConstraint = std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
333   AttributeRefAttrPtr aRefAttrA = aConstraint->data()->refattr(aParamA);
334   if (!aRefAttrA) {
335     theError = "The %1 attribute should be %2";
336     theError.arg(aParamA).arg(ModelAPI_AttributeRefAttr::typeId());
337     return false;
338   }
339
340   AttributeRefAttrPtr aRefAttrB =
341     std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
342
343   // first attribute is a point, it may coincide with any object
344   if (!aRefAttrA->isObject())
345     return true;
346   else {
347     ObjectPtr anObject = aRefAttrA->object();
348     if (!anObject.get()) {
349       theError = "%1 attribute has an empty object";
350       theError.arg(aParamA);
351       return false;
352     }
353     FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttrA->object());
354     if (!aFeature.get()) {
355       theError = "%1 attribute has an empty feature";
356       theError.arg(aParamA);
357       return false;
358     }
359
360     if (aFeature->getKind() == SketchPlugin_Point::ID())
361       return true;
362   }
363
364   // second attribute is a point, it may coincide with any object
365   if (!aRefAttrB->isObject())
366     return true;
367   else {
368     FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttrB->object());
369     if (!aFeature) {
370       theError = "%1 attribute has an empty object";
371       theError.arg(theAttribute->id());
372       return false;
373     }
374     if (aFeature->getKind() == SketchPlugin_Point::ID())
375       return true;
376   }
377   theError = "There is no an attribute filled by a point";
378   return false;
379 }
380
381
382 bool SketchPlugin_CopyValidator::isValid(const AttributePtr& theAttribute,
383                                          const std::list<std::string>& theArguments,
384                                          Events_InfoMessage& theError) const
385 {
386   if (theAttribute->attributeType() != ModelAPI_AttributeRefList::typeId()) {
387     theError = "The attribute with the %1 type is not processed";
388     theError.arg(theAttribute->attributeType());
389     return false;
390   }
391
392   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
393   AttributeRefListPtr aSelAttr =
394     std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(theAttribute);
395
396   AttributeRefListPtr aRefListOfInitial = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
397       aFeature->attribute(SketchPlugin_Constraint::ENTITY_A()));
398   AttributeRefListPtr aRefListOfCopied = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
399       aFeature->attribute(SketchPlugin_Constraint::ENTITY_B()));
400   std::list<ObjectPtr> anInitialObjects = aRefListOfInitial->list();
401   std::list<ObjectPtr> aCopiedObjects = aRefListOfCopied->list();
402
403   std::list<ObjectPtr>::iterator anObjIter;
404   for(int anInd = 0; anInd < aSelAttr->size(); anInd++) {
405     ObjectPtr aSelObject = aSelAttr->object(anInd);
406     anObjIter = anInitialObjects.begin();
407     for (; anObjIter != anInitialObjects.end(); anObjIter++)
408       if (aSelObject == *anObjIter)
409         break;
410     if (anObjIter != anInitialObjects.end())
411       continue;
412     anObjIter = aCopiedObjects.begin();
413     for (; anObjIter != aCopiedObjects.end(); anObjIter++)
414       if (aSelObject == *anObjIter) {
415         std::string aName = aSelObject.get() ? aSelObject->data()->name() : "";
416         theError = "The object %1 is a result of copy";
417         theError.arg(aName);
418         return false;
419       }
420   }
421   return true;
422 }
423
424 bool SketchPlugin_SolverErrorValidator::isValid(
425   const std::shared_ptr<ModelAPI_Feature>& theFeature,
426   const std::list<std::string>& theArguments,
427   Events_InfoMessage& theError) const
428 {
429   AttributeStringPtr aAttributeString = theFeature->string(SketchPlugin_Sketch::SOLVER_ERROR());
430
431   if (!aAttributeString->value().empty()) {
432     theError = aAttributeString->value();
433     return false;
434   }
435
436   return true;
437 }
438
439 bool SketchPlugin_SolverErrorValidator::isNotObligatory(std::string theFeature,
440                                                         std::string theAttribute)
441 {
442   return true;
443 }
444
445 static bool hasSameTangentFeature(const std::set<AttributePtr>& theRefsList,
446                                   const FeaturePtr theFeature)
447 {
448   for(std::set<AttributePtr>::const_iterator
449       anIt = theRefsList.cbegin(); anIt != theRefsList.cend(); ++anIt) {
450     std::shared_ptr<ModelAPI_Attribute> aAttr = (*anIt);
451     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
452     if (aFeature->getKind() == SketchPlugin_ConstraintTangent::ID()) {
453       AttributeRefAttrPtr anAttrRefA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
454         aFeature->attribute(SketchPlugin_ConstraintTangent::ENTITY_A()));
455       AttributeRefAttrPtr anAttrRefB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
456         aFeature->attribute(SketchPlugin_ConstraintTangent::ENTITY_B()));
457       if(anAttrRefA.get()) {
458         ResultPtr aResA = std::dynamic_pointer_cast<ModelAPI_Result>(anAttrRefA->object());
459         if(aResA.get()) {
460           DocumentPtr aDoc = aResA->document();
461           if(aDoc.get()) {
462             FeaturePtr aFeatureA = aDoc->feature(aResA);
463             if(aFeatureA.get() && aFeatureA == theFeature) {
464               return true;
465             }
466           }
467         }
468       }
469       if(anAttrRefB.get()) {
470         ResultPtr aResB = std::dynamic_pointer_cast<ModelAPI_Result>(anAttrRefB->object());
471         if(aResB.get()) {
472           DocumentPtr aDoc = aResB->document();
473           if(aDoc.get()) {
474             FeaturePtr aFeatureB = aDoc->feature(aResB);
475             if(aFeatureB.get() && aFeatureB == theFeature) {
476               return true;
477             }
478           }
479         }
480       }
481     }
482   }
483   return false;
484 }
485
486 bool SketchPlugin_FilletVertexValidator::isValid(const AttributePtr& theAttribute,
487                                                  const std::list<std::string>& theArguments,
488                                                  Events_InfoMessage& theError) const
489 {
490   AttributeRefAttrPtr aPointRefAttr =
491     std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
492   if(!aPointRefAttr.get()) {
493     theError = "Error: Point not selected.";
494     return false;
495   }
496
497   AttributePtr aPointAttribute = aPointRefAttr->attr();
498   if (!aPointAttribute.get()) {
499     theError = "Error: Bad point selected.";
500     return false;
501   }
502   std::shared_ptr<GeomAPI_Pnt2d> aSelectedPnt =
503     std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aPointAttribute)->pnt();
504
505   // Obtain constraint coincidence for the fillet point.
506   const std::set<AttributePtr>& aRefsList = aPointAttribute->owner()->data()->refsToMe();
507   FeaturePtr aConstraintCoincidence;
508   for(std::set<AttributePtr>::const_iterator anIt = aRefsList.cbegin();
509       anIt != aRefsList.cend(); ++anIt) {
510     std::shared_ptr<ModelAPI_Attribute> aAttr = (*anIt);
511     FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
512     if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
513       AttributeRefAttrPtr anAttrRefA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
514         aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_A()));
515       AttributeRefAttrPtr anAttrRefB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
516         aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_B()));
517       if(anAttrRefA.get() && !anAttrRefA->isObject()) {
518         AttributePtr anAttrA = anAttrRefA->attr();
519         if(aPointAttribute == anAttrA) {
520           aConstraintCoincidence = aConstrFeature;
521           break;
522         }
523       }
524       if(anAttrRefB.get() && !anAttrRefB->isObject()) {
525         AttributePtr anAttrB = anAttrRefB->attr();
526         if(aPointAttribute == anAttrB) {
527           aConstraintCoincidence = aConstrFeature;
528           break;
529         }
530       }
531     }
532   }
533
534   if(!aConstraintCoincidence.get()) {
535     theError = "Error: one of the selected point does not have coicidence.";
536     return false;
537   }
538
539   // Get coincides from constraint.
540   std::set<FeaturePtr> aCoinsides;
541   SketchPlugin_Tools::findCoincidences(aConstraintCoincidence,
542                                         SketchPlugin_ConstraintCoincidence::ENTITY_A(),
543                                         aCoinsides);
544   SketchPlugin_Tools::findCoincidences(aConstraintCoincidence,
545                                         SketchPlugin_ConstraintCoincidence::ENTITY_B(),
546                                         aCoinsides);
547
548   // Remove points from set of coincides.
549   std::set<FeaturePtr> aNewSetOfCoincides;
550   for(std::set<FeaturePtr>::iterator anIt = aCoinsides.begin();
551       anIt != aCoinsides.end(); ++anIt) {
552     std::shared_ptr<SketchPlugin_SketchEntity> aSketchEntity =
553       std::dynamic_pointer_cast<SketchPlugin_SketchEntity>(*anIt);
554     if(aSketchEntity.get() && aSketchEntity->isCopy()) {
555       continue;
556     }
557     if((*anIt)->getKind() != SketchPlugin_Line::ID() &&
558         (*anIt)->getKind() != SketchPlugin_Arc::ID()) {
559           continue;
560     }
561     if((*anIt)->getKind() == SketchPlugin_Arc::ID()) {
562       AttributePtr anArcCenter = (*anIt)->attribute(SketchPlugin_Arc::CENTER_ID());
563       std::shared_ptr<GeomAPI_Pnt2d> anArcCenterPnt =
564         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anArcCenter)->pnt();
565       double aDistSelectedArcCenter = aSelectedPnt->distance(anArcCenterPnt);
566       if(aDistSelectedArcCenter < tolerance) {
567         continue;
568       }
569     }
570     aNewSetOfCoincides.insert(*anIt);
571   }
572   aCoinsides = aNewSetOfCoincides;
573
574   // If we still have more than two coincides remove auxilary entities from set of coincides.
575   if(aCoinsides.size() > 2) {
576     aNewSetOfCoincides.clear();
577     for(std::set<FeaturePtr>::iterator anIt = aCoinsides.begin();
578         anIt != aCoinsides.end(); ++anIt) {
579       if(!(*anIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) {
580         aNewSetOfCoincides.insert(*anIt);
581       }
582     }
583     aCoinsides = aNewSetOfCoincides;
584   }
585
586   if(aCoinsides.size() != 2) {
587     theError = "Error: One of the selected points does not have two suitable edges for fillet.";
588     return false;
589   }
590
591   // Check that selected edges don't have tangent constraint.
592   std::set<FeaturePtr>::iterator anIt = aCoinsides.begin();
593   FeaturePtr aFirstFeature = *anIt++;
594   FeaturePtr aSecondFeature = *anIt;
595   const std::set<AttributePtr>& aFirstFeatureRefsList = aFirstFeature->data()->refsToMe();
596   if(hasSameTangentFeature(aFirstFeatureRefsList, aSecondFeature)) {
597     theError = "Error: Edges in selected point has tangent constraint.";
598     return false;
599   }
600
601   std::list<ResultPtr> aFirstResults = aFirstFeature->results();
602   for(std::list<ResultPtr>::iterator aResIt = aFirstResults.begin();
603       aResIt != aFirstResults.end(); ++aResIt) {
604     ResultPtr aRes = *aResIt;
605     const std::set<AttributePtr>& aResRefsList = aRes->data()->refsToMe();
606     if(hasSameTangentFeature(aResRefsList, aSecondFeature)) {
607       theError = "Error: Edges in selected point has tangent constraint.";
608       return false;
609     }
610   }
611
612   // Check that lines not collinear
613   if(aFirstFeature->getKind() == SketchPlugin_Line::ID() &&
614       aSecondFeature->getKind() == SketchPlugin_Line::ID()) {
615     std::string aStartAttr = SketchPlugin_Line::START_ID();
616     std::string anEndAttr = SketchPlugin_Line::END_ID();
617     std::shared_ptr<GeomAPI_Pnt2d> aFirstStartPnt, aFirstEndPnt, aSecondStartPnt, aSecondEndPnt;
618     aFirstStartPnt =
619       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
620       aFirstFeature->attribute(aStartAttr))->pnt();
621     aFirstEndPnt =
622       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aFirstFeature->attribute(anEndAttr))->pnt();
623     aSecondStartPnt =
624       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
625       aSecondFeature->attribute(aStartAttr))->pnt();
626     aSecondEndPnt =
627       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
628       aSecondFeature->attribute(anEndAttr))->pnt();
629     double aCheck1 =
630       fabs((aFirstEndPnt->x() - aFirstStartPnt->x()) *
631       (aSecondStartPnt->y() - aFirstStartPnt->y()) -
632       (aSecondStartPnt->x() - aFirstStartPnt->x()) * (aFirstEndPnt->y() - aFirstStartPnt->y()));
633     double aCheck2 =
634       fabs((aFirstEndPnt->x() - aFirstStartPnt->x()) *
635       (aSecondEndPnt->y() - aFirstStartPnt->y()) -
636       (aSecondEndPnt->x() - aFirstStartPnt->x()) * (aFirstEndPnt->y() - aFirstStartPnt->y()));
637     if(aCheck1 < 1.e-7 && aCheck2 < 1.e-7) {
638       return false;
639     }
640   }
641
642
643   return true;
644 }
645
646 bool SketchPlugin_MiddlePointAttrValidator::isValid(const AttributePtr& theAttribute,
647                                                     const std::list<std::string>& theArguments,
648                                                     Events_InfoMessage& theError) const
649 {
650   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
651     theError = "The attribute with the %1 type is not processed";
652     theError.arg(theAttribute->attributeType());
653     return false;
654   }
655
656   // there is a check whether the feature contains a point and a linear edge or two point values
657   std::string aParamA = theArguments.front();
658   SessionPtr aMgr = ModelAPI_Session::get();
659   ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
660
661   FeaturePtr anAttributeFeature =
662     std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
663   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
664   AttributeRefAttrPtr anOtherAttr = anAttributeFeature->data()->refattr(aParamA);
665
666   AttributeRefAttrPtr aRefAttrs[2] = {aRefAttr, anOtherAttr};
667   int aNbPoints = 0;
668   int aNbLines = 0;
669   for (int i = 0; i < 2; ++i) {
670     if (!aRefAttrs[i]->isObject())
671       ++aNbPoints;
672     else {
673       FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttrs[i]->object());
674       if (!aFeature) {
675         if (aNbPoints + aNbLines != 0)
676           return true;
677         else continue;
678       }
679
680       if (aFeature->getKind() == SketchPlugin_Point::ID())
681         ++aNbPoints;
682       else if (aFeature->getKind() == SketchPlugin_Line::ID())
683         ++aNbLines;
684     }
685   }
686
687   if (aNbPoints != 1 || aNbLines != 1) {
688     theError = "Middle point constraint allows points and lines only";
689     return false;
690   }
691   return true;
692 }
693
694 bool SketchPlugin_ArcTangentPointValidator::isValid(const AttributePtr& theAttribute,
695                                                     const std::list<std::string>& /*theArguments*/,
696                                                     Events_InfoMessage& theError) const
697 {
698   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
699     theError = "The attribute with the %1 type is not processed";
700     theError.arg(theAttribute->attributeType());
701     return false;
702   }
703   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
704   AttributePtr anAttr = aRefAttr->attr();
705   if (!anAttr) {
706     theError = "The attribute %1 should be a point";
707     theError.arg(theAttribute->id());
708     return false;
709   }
710
711   FeaturePtr anAttrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anAttr->owner());
712   const std::string& aFeatureType = anAttrFeature->getKind();
713   if (aFeatureType == SketchPlugin_Arc::ID()) {
714     // selected point should not be a center of arc
715     const std::string& aPntId = anAttr->id();
716     if (aPntId != SketchPlugin_Arc::START_ID() && aPntId != SketchPlugin_Arc::END_ID()) {
717       theError = "The attribute %1 is not supported";
718       theError.arg(aPntId);
719       return false;
720     }
721   }
722   else if (aFeatureType == SketchPlugin_Line::ID()) {
723     // selected point should be bound point of line
724     const std::string& aPntId = anAttr->id();
725     if (aPntId != SketchPlugin_Line::START_ID() && aPntId != SketchPlugin_Line::END_ID()) {
726       theError = "The attribute %1 is not supported";
727       theError.arg(aPntId);
728       return false;
729     }
730   }
731   else {
732     theError = "Unable to build tangent arc on %1";
733     theError.arg(anAttrFeature->getKind());
734     return false;
735   }
736
737   return true;
738 }
739
740 bool SketchPlugin_IntersectionValidator::isValid(const AttributePtr& theAttribute,
741                                                  const std::list<std::string>& theArguments,
742                                                  Events_InfoMessage& theError) const
743 {
744   if (theAttribute->attributeType() != ModelAPI_AttributeSelection::typeId()) {
745     theError = "The attribute with the %1 type is not processed";
746     theError.arg(theAttribute->attributeType());
747     return false;
748   }
749   AttributeSelectionPtr aLineAttr =
750                        std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(theAttribute);
751   std::shared_ptr<GeomAPI_Edge> anEdge;
752   if(aLineAttr && aLineAttr->value() && aLineAttr->value()->isEdge()) {
753     anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(aLineAttr->value()));
754   } else if(aLineAttr->context() &&
755             aLineAttr->context()->shape() && aLineAttr->context()->shape()->isEdge()) {
756     anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(aLineAttr->context()->shape()));
757   }
758
759   if (!anEdge || !anEdge->isLine()) {
760     theError = "The attribute %1 should be a line";
761     theError.arg(theAttribute->id());
762     return false;
763   }
764
765   std::shared_ptr<GeomAPI_Dir> aLineDir = anEdge->line()->direction();
766
767   // find a sketch
768   std::shared_ptr<SketchPlugin_Sketch> aSketch;
769   std::set<AttributePtr> aRefs = aLineAttr->owner()->data()->refsToMe();
770   std::set<AttributePtr>::const_iterator anIt = aRefs.begin();
771   for (; anIt != aRefs.end(); ++anIt) {
772     CompositeFeaturePtr aComp =
773         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>((*anIt)->owner());
774     if (aComp && aComp->getKind() == SketchPlugin_Sketch::ID()) {
775       aSketch = std::dynamic_pointer_cast<SketchPlugin_Sketch>(aComp);
776       break;
777     }
778   }
779   if (!aSketch) {
780     theError = "There is no sketch referring to the current feature";
781     return false;
782   }
783
784   std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
785   std::shared_ptr<GeomAPI_Dir> aNormal = aPlane->direction();
786   return fabs(aNormal->dot(aLineDir)) > tolerance * tolerance;
787 }
788
789 bool SketchPlugin_SplitValidator::isValid(const AttributePtr& theAttribute,
790                                           const std::list<std::string>& theArguments,
791                                           Events_InfoMessage& theError) const
792 {
793   bool aValid = false;
794
795   if (theAttribute->attributeType() != ModelAPI_AttributeReference::typeId()) {
796     theError = "The attribute with the %1 type is not processed";
797     theError.arg(theAttribute->attributeType());
798     return aValid;
799   }
800   AttributeReferencePtr aFeatureAttr =
801             std::dynamic_pointer_cast<ModelAPI_AttributeReference>(theAttribute);
802
803   ObjectPtr anAttrObject = aFeatureAttr->value();
804   FeaturePtr anAttrFeature = ModelAPI_Feature::feature(anAttrObject);
805   if (!anAttrFeature)
806     return aValid;
807
808   std::string aKind = anAttrFeature->getKind();
809   if (aKind == SketchPlugin_Line::ID() ||
810       aKind == SketchPlugin_Arc::ID() ||
811       aKind == SketchPlugin_Circle::ID()) {
812
813     std::set<ResultPtr> anEdgeShapes;
814     ModelGeomAlgo_Shape::shapesOfType(anAttrFeature, GeomAPI_Shape::EDGE, anEdgeShapes);
815     if (anEdgeShapes.empty() || anEdgeShapes.size() > 1 /*there case has not existed yet*/)
816       return aValid;
817
818     // coincidences to the feature
819     std::set<std::shared_ptr<GeomDataAPI_Point2D> > aRefAttributes;
820     ModelGeomAlgo_Point2D::getPointsOfReference(anAttrFeature,
821                         SketchPlugin_ConstraintCoincidence::ID(),
822                         aRefAttributes, SketchPlugin_Point::ID(), SketchPlugin_Point::COORD_ID());
823
824     GeomShapePtr anAttrShape = (*anEdgeShapes.begin())->shape();
825     std::shared_ptr<SketchPlugin_Feature> aSFeature =
826                                  std::dynamic_pointer_cast<SketchPlugin_Feature>(anAttrFeature);
827     SketchPlugin_Sketch* aSketch = aSFeature->sketch();
828
829     std::shared_ptr<ModelAPI_Data> aData = aSketch->data();
830     std::shared_ptr<GeomDataAPI_Point> aC = std::dynamic_pointer_cast<GeomDataAPI_Point>(
831         aData->attribute(SketchPlugin_Sketch::ORIGIN_ID()));
832     std::shared_ptr<GeomDataAPI_Dir> aX = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
833         aData->attribute(SketchPlugin_Sketch::DIRX_ID()));
834     std::shared_ptr<GeomDataAPI_Dir> aNorm = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
835         aData->attribute(SketchPlugin_Sketch::NORM_ID()));
836     std::shared_ptr<GeomAPI_Dir> aDirY(new GeomAPI_Dir(aNorm->dir()->cross(aX->dir())));
837
838     typedef std::map<std::shared_ptr<GeomAPI_Pnt>,
839                      std::pair<std::list<std::shared_ptr<GeomDataAPI_Point2D> >,
840                                std::list<std::shared_ptr<ModelAPI_Object> > > > PointToRefsMap;
841     PointToRefsMap aPointsInfo;
842
843     ModelGeomAlgo_Point2D::getPointsInsideShape(anAttrShape, aRefAttributes, aC->pnt(),
844                                                 aX->dir(), aDirY, aPointsInfo);
845     int aCoincidentToFeature = (int)aPointsInfo.size();
846     if (aKind == SketchPlugin_Circle::ID())
847       aValid = aCoincidentToFeature >= 2;
848     else
849       aValid = aCoincidentToFeature >= 1;
850   }
851
852   return aValid;
853 }
854
855 bool SketchPlugin_TrimValidator::isValid(const AttributePtr& theAttribute,
856                                          const std::list<std::string>& theArguments,
857                                          Events_InfoMessage& theError) const
858 {
859   bool aValid = false;
860
861   if (theAttribute->attributeType() != ModelAPI_AttributeReference::typeId()) {
862     theError = "The attribute with the %1 type is not processed";
863     theError.arg(theAttribute->attributeType());
864     return aValid;
865   }
866   AttributeReferencePtr aBaseObjectAttr =
867             std::dynamic_pointer_cast<ModelAPI_AttributeReference>(theAttribute);
868
869   std::shared_ptr<SketchPlugin_Feature> aTrimFeature =
870                  std::dynamic_pointer_cast<SketchPlugin_Feature>(theAttribute->owner());
871
872   ObjectPtr aBaseObject = aBaseObjectAttr->value();
873   if (!aBaseObject) {
874     AttributePtr aPreviewAttr = aTrimFeature->attribute(SketchPlugin_Trim::PREVIEW_OBJECT());
875     aBaseObjectAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(aPreviewAttr);
876     aBaseObject = aBaseObjectAttr->value();
877
878     //return aValid;
879   }
880
881   FeaturePtr aBaseFeature = ModelAPI_Feature::feature(aBaseObject);
882   if (!aBaseFeature)
883     return aValid;
884
885   std::string aKind = aBaseFeature->getKind();
886   if (aKind != SketchPlugin_Line::ID() &&
887       aKind != SketchPlugin_Arc::ID() &&
888       aKind != SketchPlugin_Circle::ID())
889     return aValid;
890
891   // point on feature
892   AttributePoint2DPtr aPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
893                        aTrimFeature->data()->attribute(SketchPlugin_Trim::PREVIEW_POINT()));
894
895   SketchPlugin_Sketch* aSketch = aTrimFeature->sketch();
896
897   std::shared_ptr<GeomAPI_Pnt2d> anAttributePnt2d = aPoint->pnt();
898   std::shared_ptr<GeomAPI_Pnt> anAttributePnt = aSketch->to3D(anAttributePnt2d->x(),
899                                                               anAttributePnt2d->y());
900
901   std::map<ObjectPtr, std::set<GeomShapePtr> > aCashedShapes;
902   std::map<ObjectPtr, std::map<std::shared_ptr<GeomAPI_Pnt>,
903            std::pair<std::list<std::shared_ptr<GeomDataAPI_Point2D> >,
904                      std::list<std::shared_ptr<ModelAPI_Object> > > > > anObjectToPoints;
905   SketchPlugin_Trim::fillObjectShapes(aBaseObject, aSketch->data()->owner(),
906                                       aCashedShapes, anObjectToPoints);
907   const std::set<GeomShapePtr>& aShapes = aCashedShapes[aBaseObject];
908
909   return aShapes.size() > 1;
910 }
911
912 bool SketchPlugin_ProjectionValidator::isValid(const AttributePtr& theAttribute,
913                                                const std::list<std::string>& theArguments,
914                                                Events_InfoMessage& theError) const
915 {
916   if (theAttribute->attributeType() != ModelAPI_AttributeSelection::typeId()) {
917     theError = "The attribute with the %1 type is not processed";
918     theError.arg(theAttribute->attributeType());
919     return false;
920   }
921
922   AttributeSelectionPtr aFeatureAttr =
923       std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(theAttribute);
924   std::shared_ptr<GeomAPI_Edge> anEdge;
925   if (aFeatureAttr.get()) {
926     GeomShapePtr aVal = aFeatureAttr->value();
927     ResultPtr aRes = aFeatureAttr->context();
928     if(aFeatureAttr->value() && aFeatureAttr->value()->isEdge()) {
929       anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(aFeatureAttr->value()));
930     } else if(aFeatureAttr->context() && aFeatureAttr->context()->shape() &&
931               aFeatureAttr->context()->shape()->isEdge()) {
932       anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(aFeatureAttr->context()->shape()));
933     }
934   }
935   if (!anEdge) {
936     theError = "The attribute %1 should be an edge";
937     theError.arg(theAttribute->id());
938     return false;
939   }
940
941   // find a sketch
942   std::shared_ptr<SketchPlugin_Sketch> aSketch;
943   std::set<AttributePtr> aRefs = theAttribute->owner()->data()->refsToMe();
944   std::set<AttributePtr>::const_iterator anIt = aRefs.begin();
945   for (; anIt != aRefs.end(); ++anIt) {
946     CompositeFeaturePtr aComp =
947         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>((*anIt)->owner());
948     if (aComp && aComp->getKind() == SketchPlugin_Sketch::ID()) {
949       aSketch = std::dynamic_pointer_cast<SketchPlugin_Sketch>(aComp);
950       break;
951     }
952   }
953   if (!aSketch) {
954     theError = "There is no sketch referring to the current feature";
955     return false;
956   }
957
958   std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
959   std::shared_ptr<GeomAPI_Dir> aNormal = aPlane->direction();
960   std::shared_ptr<GeomAPI_Pnt> anOrigin = aPlane->location();
961
962   if (anEdge->isLine()) {
963     std::shared_ptr<GeomAPI_Lin> aLine = anEdge->line();
964     std::shared_ptr<GeomAPI_Dir> aLineDir = aLine->direction();
965     std::shared_ptr<GeomAPI_Pnt> aLineLoc = aLine->location();
966     double aDot = aNormal->dot(aLineDir);
967     double aDist = aLineLoc->xyz()->decreased(anOrigin->xyz())->dot(aNormal->xyz());
968     bool aValid = (fabs(aDot) >= tolerance && fabs(aDot) < 1.0 - tolerance) ||
969            (fabs(aDot) < tolerance && fabs(aDist) > tolerance);
970     if (!aValid)
971       theError = "Error: Edge is already in the sketch plane.";
972     return aValid;
973   }
974   else if (anEdge->isCircle() || anEdge->isArc()) {
975     std::shared_ptr<GeomAPI_Circ> aCircle = anEdge->circle();
976     std::shared_ptr<GeomAPI_Dir> aCircNormal = aCircle->normal();
977     std::shared_ptr<GeomAPI_Pnt> aCircCenter = aCircle->center();
978     double aDot = fabs(aNormal->dot(aCircNormal));
979     double aDist = aCircCenter->xyz()->decreased(anOrigin->xyz())->dot(aNormal->xyz());
980     bool aValid = fabs(aDot - 1.0) < tolerance * tolerance && fabs(aDist) > tolerance;
981     if (!aValid)
982       theError.arg(anEdge->isCircle() ? "Error: Cirlce is already in the sketch plane."
983                                       : "Error: Arc is already in the sketch plane.");
984     return aValid;
985   }
986
987   theError = "Error: Selected object is not line, circle or arc.";
988   return false;
989 }
990
991
992 static std::set<FeaturePtr> common(const std::set<FeaturePtr>& theSet1,
993                                    const std::set<FeaturePtr>& theSet2)
994 {
995   std::set<FeaturePtr> aCommon;
996   if (theSet1.empty() || theSet2.empty())
997     return aCommon;
998
999   std::set<FeaturePtr>::const_iterator anIt2 = theSet2.begin();
1000   for (; anIt2 != theSet2.end(); ++anIt2)
1001     if (theSet1.find(*anIt2) != theSet1.end())
1002       aCommon.insert(*anIt2);
1003   return aCommon;
1004 }
1005
1006 bool SketchPlugin_DifferentReferenceValidator::isValid(
1007     const AttributePtr& theAttribute,
1008     const std::list<std::string>& theArguments,
1009     Events_InfoMessage& theError) const
1010 {
1011   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner());
1012
1013   int aNbFeaturesReferred = 0;
1014   int aNbAttributesReferred = 0;
1015   std::set<FeaturePtr> aCommonReferredFeatures;
1016
1017   // find all features referred by attributes listed in theArguments
1018   std::list<std::string>::const_iterator anArgIt = theArguments.begin();
1019   for (; anArgIt != theArguments.end(); ++anArgIt) {
1020     AttributeRefAttrPtr aRefAttr = anOwner->refattr(*anArgIt);
1021     if (!aRefAttr)
1022       continue;
1023
1024     std::set<FeaturePtr> aCoincidentFeatures;
1025     if (aRefAttr->isObject()) {
1026       FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object());
1027       if (aFeature) {
1028         aCoincidentFeatures.insert(aFeature);
1029         aNbFeaturesReferred += 1;
1030       }
1031     } else {
1032       AttributePoint2DPtr aPoint =
1033           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aRefAttr->attr());
1034       if (aPoint) {
1035         aCoincidentFeatures = SketchPlugin_Tools::findFeaturesCoincidentToPoint(aPoint);
1036         aNbAttributesReferred += 1;
1037       }
1038     }
1039
1040     if (aCommonReferredFeatures.empty())
1041       aCommonReferredFeatures = aCoincidentFeatures;
1042     else
1043       aCommonReferredFeatures = common(aCommonReferredFeatures, aCoincidentFeatures);
1044
1045     if (aCommonReferredFeatures.empty())
1046       return true;
1047   }
1048
1049   bool isOk = aNbFeaturesReferred < 1;
1050   if (aNbFeaturesReferred == 1) {
1051     if (aCommonReferredFeatures.size() == 1) {
1052       FeaturePtr aFeature = *aCommonReferredFeatures.begin();
1053       isOk = aNbAttributesReferred <= 1 ||
1054              aFeature->getKind() == SketchPlugin_Circle::ID() ||
1055              aFeature->getKind() == SketchPlugin_Arc::ID();
1056     } else
1057       isOk = false;
1058   }
1059
1060   if (!isOk)
1061     theError = "Attributes are referred to the same feature";
1062   return isOk;
1063 }
1064
1065 bool SketchPlugin_CirclePassedPointValidator::isValid(
1066     const AttributePtr& theAttribute,
1067     const std::list<std::string>&,
1068     Events_InfoMessage& theError) const
1069 {
1070   static const std::string aErrorMessage(
1071       "Passed point refers to the same feature as a center point");
1072
1073   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner());
1074
1075   AttributeRefAttrPtr aCenterRef =
1076       anOwner->refattr(SketchPlugin_MacroCircle::CENTER_POINT_REF_ID());
1077   AttributeRefAttrPtr aPassedRef =
1078       anOwner->refattr(SketchPlugin_MacroCircle::PASSED_POINT_REF_ID());
1079
1080   if (!aPassedRef->isObject())
1081     return true;
1082
1083   FeaturePtr aPassedFeature = ModelAPI_Feature::feature(aPassedRef->object());
1084   if (!aPassedFeature)
1085     return true;
1086
1087   if (aCenterRef->isObject()) {
1088     FeaturePtr aCenterFeature = ModelAPI_Feature::feature(aCenterRef->object());
1089     if (aCenterFeature == aPassedFeature) {
1090       theError = aErrorMessage;
1091       return false;
1092     }
1093   } else {
1094     AttributePoint2DPtr aCenterPoint =
1095         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aCenterRef->attr());
1096     if (aCenterPoint) {
1097       std::set<FeaturePtr> aCoincidentFeatures =
1098           SketchPlugin_Tools::findFeaturesCoincidentToPoint(aCenterPoint);
1099       // check one of coincident features is a feature referred by passed point
1100       std::set<FeaturePtr>::const_iterator anIt = aCoincidentFeatures.begin();
1101       for(; anIt != aCoincidentFeatures.end(); ++anIt)
1102         if (*anIt == aPassedFeature) {
1103           theError = aErrorMessage;
1104           return false;
1105         }
1106     }
1107   }
1108   return true;
1109 }
1110
1111 bool SketchPlugin_ThirdPointValidator::isValid(
1112     const AttributePtr& theAttribute,
1113     const std::list<std::string>&,
1114     Events_InfoMessage& theError) const
1115 {
1116   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner());
1117   return arePointsNotOnLine(anOwner, theError) && arePointsNotSeparated(anOwner, theError);
1118 }
1119
1120 static std::shared_ptr<GeomAPI_Pnt2d> toPoint(const FeaturePtr& theMacroCircle,
1121                                               const std::string& thePointAttrName,
1122                                               const std::string& theRefPointAttrName)
1123 {
1124   AttributePoint2DPtr aPointAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1125       theMacroCircle->attribute(thePointAttrName));
1126   AttributeRefAttrPtr aRefAttr = theMacroCircle->refattr(theRefPointAttrName);
1127
1128   std::shared_ptr<GeomAPI_Pnt2d> aPoint = aPointAttr->pnt();
1129   if (aRefAttr) {
1130     if (aRefAttr->isObject()) {
1131       // project a point onto selected feature
1132       std::shared_ptr<SketchPlugin_Feature> aFeature =
1133           std::dynamic_pointer_cast<SketchPlugin_Feature>(
1134           ModelAPI_Feature::feature(aRefAttr->object()));
1135       if (aFeature) {
1136         SketchPlugin_Sketch* aSketch = aFeature->sketch();
1137         std::shared_ptr<GeomAPI_Edge> anEdge =
1138             std::dynamic_pointer_cast<GeomAPI_Edge>(aFeature->lastResult()->shape());
1139         if (anEdge) {
1140           std::shared_ptr<GeomAPI_Pnt> aPoint3D = aSketch->to3D(aPoint->x(), aPoint->y());
1141           if (anEdge->isLine())
1142             aPoint3D = anEdge->line()->project(aPoint3D);
1143           else if (anEdge->isCircle())
1144             aPoint3D = anEdge->circle()->project(aPoint3D);
1145           aPoint = aSketch->to2D(aPoint3D);
1146         }
1147       }
1148     } else {
1149       AttributePoint2DPtr anOtherPoint =
1150           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aRefAttr->attr());
1151       if (anOtherPoint)
1152         aPoint = anOtherPoint->pnt(); // the reference point is much more precise, use it
1153     }
1154   }
1155
1156   return aPoint;
1157 }
1158
1159 static bool isPointsOnLine(const std::shared_ptr<GeomAPI_Pnt2d>& thePoint1,
1160                            const std::shared_ptr<GeomAPI_Pnt2d>& thePoint2,
1161                            const std::shared_ptr<GeomAPI_Pnt2d>& thePoint3)
1162 {
1163   static const double aTolerance = 1.e-7;
1164   if (thePoint1->distance(thePoint2) < aTolerance ||
1165       thePoint1->distance(thePoint3) < aTolerance)
1166     return true;
1167
1168   std::shared_ptr<GeomAPI_Dir2d> aDirP1P2(new GeomAPI_Dir2d(thePoint2->x() - thePoint1->x(),
1169                                                             thePoint2->y() - thePoint1->y()));
1170   std::shared_ptr<GeomAPI_Dir2d> aDirP1P3(new GeomAPI_Dir2d(thePoint3->x() - thePoint1->x(),
1171                                                             thePoint3->y() - thePoint1->y()));
1172   return fabs(aDirP1P2->cross(aDirP1P3)) < aTolerance;
1173 }
1174
1175 static bool isOnSameSide(const std::shared_ptr<GeomAPI_Lin>& theLine,
1176                          const std::shared_ptr<GeomAPI_Pnt>& thePoint1,
1177                          const std::shared_ptr<GeomAPI_Pnt>& thePoint2)
1178 {
1179   static const double aTolerance = 1.e-7;
1180   std::shared_ptr<GeomAPI_Dir> aLineDir = theLine->direction();
1181   std::shared_ptr<GeomAPI_XYZ> aLineLoc = theLine->location()->xyz();
1182   std::shared_ptr<GeomAPI_Dir> aDirP1L(new GeomAPI_Dir(thePoint1->xyz()->decreased(aLineLoc)));
1183   std::shared_ptr<GeomAPI_Dir> aDirP2L(new GeomAPI_Dir(thePoint2->xyz()->decreased(aLineLoc)));
1184   return aLineDir->cross(aDirP1L)->dot(aLineDir->cross(aDirP2L)) > -aTolerance;
1185 }
1186
1187 static bool isOnSameSide(const std::shared_ptr<GeomAPI_Circ>& theCircle,
1188                          const std::shared_ptr<GeomAPI_Pnt>&  thePoint1,
1189                          const std::shared_ptr<GeomAPI_Pnt>&  thePoint2)
1190 {
1191   static const double aTolerance = 1.e-7;
1192   std::shared_ptr<GeomAPI_Pnt> aCenter = theCircle->center();
1193   double aDistP1C = thePoint1->distance(aCenter);
1194   double aDistP2C = thePoint2->distance(aCenter);
1195   return (aDistP1C - theCircle->radius()) * (aDistP2C - theCircle->radius()) > -aTolerance;
1196 }
1197
1198 bool SketchPlugin_ThirdPointValidator::arePointsNotOnLine(
1199     const FeaturePtr& theMacroCircle,
1200     Events_InfoMessage& theError) const
1201 {
1202   static const std::string aErrorPointsOnLine(
1203       "Selected points are on the same line");
1204
1205   std::shared_ptr<GeomAPI_Pnt2d> aFirstPoint = toPoint(theMacroCircle,
1206         SketchPlugin_MacroCircle::FIRST_POINT_ID(),
1207         SketchPlugin_MacroCircle::FIRST_POINT_REF_ID());
1208   std::shared_ptr<GeomAPI_Pnt2d> aSecondPoint = toPoint(theMacroCircle,
1209         SketchPlugin_MacroCircle::SECOND_POINT_ID(),
1210         SketchPlugin_MacroCircle::SECOND_POINT_REF_ID());
1211   std::shared_ptr<GeomAPI_Pnt2d> aThirdPoint = toPoint(theMacroCircle,
1212         SketchPlugin_MacroCircle::THIRD_POINT_ID(),
1213         SketchPlugin_MacroCircle::THIRD_POINT_REF_ID());
1214
1215   if (isPointsOnLine(aFirstPoint, aSecondPoint, aThirdPoint)) {
1216     theError = aErrorPointsOnLine;
1217     return false;
1218   }
1219   return true;
1220 }
1221
1222 bool SketchPlugin_ThirdPointValidator::arePointsNotSeparated(
1223     const FeaturePtr& theMacroCircle,
1224     Events_InfoMessage& theError) const
1225 {
1226   static const std::string aErrorPointsApart(
1227       "Selected entity is lying between first two points");
1228
1229   AttributeRefAttrPtr aThirdPointRef =
1230       theMacroCircle->refattr(SketchPlugin_MacroCircle::THIRD_POINT_REF_ID());
1231   FeaturePtr aRefByThird;
1232   if (aThirdPointRef->isObject())
1233     aRefByThird = ModelAPI_Feature::feature(aThirdPointRef->object());
1234   if (!aRefByThird)
1235     return true;
1236
1237   std::shared_ptr<GeomAPI_Pnt2d> aFirstPoint = toPoint(theMacroCircle,
1238         SketchPlugin_MacroCircle::FIRST_POINT_ID(),
1239         SketchPlugin_MacroCircle::FIRST_POINT_REF_ID());
1240   std::shared_ptr<GeomAPI_Pnt2d> aSecondPoint = toPoint(theMacroCircle,
1241         SketchPlugin_MacroCircle::SECOND_POINT_ID(),
1242         SketchPlugin_MacroCircle::SECOND_POINT_REF_ID());
1243
1244   std::shared_ptr<GeomAPI_Edge> aThirdShape =
1245       std::dynamic_pointer_cast<GeomAPI_Edge>(aRefByThird->lastResult()->shape());
1246   if (!aThirdShape)
1247     return true;
1248
1249   SketchPlugin_Sketch* aSketch =
1250       std::dynamic_pointer_cast<SketchPlugin_Feature>(theMacroCircle)->sketch();
1251   std::shared_ptr<GeomAPI_Pnt> aFirstPnt3D = aSketch->to3D(aFirstPoint->x(), aFirstPoint->y());
1252   std::shared_ptr<GeomAPI_Pnt> aSecondPnt3D = aSketch->to3D(aSecondPoint->x(), aSecondPoint->y());
1253
1254   bool isOk = true;
1255   if (aThirdShape->isLine())
1256     isOk = isOnSameSide(aThirdShape->line(), aFirstPnt3D, aSecondPnt3D);
1257   else if (aThirdShape->isCircle() || aThirdShape->isArc())
1258     isOk = isOnSameSide(aThirdShape->circle(), aFirstPnt3D, aSecondPnt3D);
1259
1260   if (!isOk)
1261     theError = aErrorPointsApart;
1262   return isOk;
1263 }