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