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