Salome HOME
Issue #2109 trim - wrong result due to constarints
[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 that lines not collinear
617   if(aFirstFeature->getKind() == SketchPlugin_Line::ID() &&
618       aSecondFeature->getKind() == SketchPlugin_Line::ID()) {
619     std::string aStartAttr = SketchPlugin_Line::START_ID();
620     std::string anEndAttr = SketchPlugin_Line::END_ID();
621     std::shared_ptr<GeomAPI_Pnt2d> aFirstStartPnt, aFirstEndPnt, aSecondStartPnt, aSecondEndPnt;
622     aFirstStartPnt =
623       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
624       aFirstFeature->attribute(aStartAttr))->pnt();
625     aFirstEndPnt =
626       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aFirstFeature->attribute(anEndAttr))->pnt();
627     aSecondStartPnt =
628       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
629       aSecondFeature->attribute(aStartAttr))->pnt();
630     aSecondEndPnt =
631       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
632       aSecondFeature->attribute(anEndAttr))->pnt();
633     double aCheck1 =
634       fabs((aFirstEndPnt->x() - aFirstStartPnt->x()) *
635       (aSecondStartPnt->y() - aFirstStartPnt->y()) -
636       (aSecondStartPnt->x() - aFirstStartPnt->x()) * (aFirstEndPnt->y() - aFirstStartPnt->y()));
637     double aCheck2 =
638       fabs((aFirstEndPnt->x() - aFirstStartPnt->x()) *
639       (aSecondEndPnt->y() - aFirstStartPnt->y()) -
640       (aSecondEndPnt->x() - aFirstStartPnt->x()) * (aFirstEndPnt->y() - aFirstStartPnt->y()));
641     if(aCheck1 < 1.e-7 && aCheck2 < 1.e-7) {
642       return false;
643     }
644   }
645
646
647   return true;
648 }
649
650 bool SketchPlugin_MiddlePointAttrValidator::isValid(const AttributePtr& theAttribute,
651                                                     const std::list<std::string>& theArguments,
652                                                     Events_InfoMessage& theError) const
653 {
654   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
655     theError = "The attribute with the %1 type is not processed";
656     theError.arg(theAttribute->attributeType());
657     return false;
658   }
659
660   // there is a check whether the feature contains a point and a linear edge or two point values
661   std::string aParamA = theArguments.front();
662   SessionPtr aMgr = ModelAPI_Session::get();
663   ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
664
665   FeaturePtr anAttributeFeature =
666     std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
667   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
668   AttributeRefAttrPtr anOtherAttr = anAttributeFeature->data()->refattr(aParamA);
669
670   AttributeRefAttrPtr aRefAttrs[2] = {aRefAttr, anOtherAttr};
671   int aNbPoints = 0;
672   int aNbLines = 0;
673   for (int i = 0; i < 2; ++i) {
674     if (!aRefAttrs[i]->isObject())
675       ++aNbPoints;
676     else {
677       FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttrs[i]->object());
678       if (!aFeature) {
679         if (aNbPoints + aNbLines != 0)
680           return true;
681         else continue;
682       }
683
684       if (aFeature->getKind() == SketchPlugin_Point::ID())
685         ++aNbPoints;
686       else if (aFeature->getKind() == SketchPlugin_Line::ID())
687         ++aNbLines;
688     }
689   }
690
691   if (aNbPoints != 1 || aNbLines != 1) {
692     theError = "Middle point constraint allows points and lines only";
693     return false;
694   }
695   return true;
696 }
697
698 bool SketchPlugin_ArcTangentPointValidator::isValid(const AttributePtr& theAttribute,
699                                                     const std::list<std::string>& /*theArguments*/,
700                                                     Events_InfoMessage& theError) const
701 {
702   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
703     theError = "The attribute with the %1 type is not processed";
704     theError.arg(theAttribute->attributeType());
705     return false;
706   }
707   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
708   AttributePtr anAttr = aRefAttr->attr();
709   if (!anAttr) {
710     theError = "The attribute %1 should be a point";
711     theError.arg(theAttribute->id());
712     return false;
713   }
714
715   FeaturePtr anAttrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anAttr->owner());
716   const std::string& aFeatureType = anAttrFeature->getKind();
717   if (aFeatureType == SketchPlugin_Arc::ID()) {
718     // selected point should not be a center of arc
719     const std::string& aPntId = anAttr->id();
720     if (aPntId != SketchPlugin_Arc::START_ID() && aPntId != SketchPlugin_Arc::END_ID()) {
721       theError = "The attribute %1 is not supported";
722       theError.arg(aPntId);
723       return false;
724     }
725   }
726   else if (aFeatureType == SketchPlugin_Line::ID()) {
727     // selected point should be bound point of line
728     const std::string& aPntId = anAttr->id();
729     if (aPntId != SketchPlugin_Line::START_ID() && aPntId != SketchPlugin_Line::END_ID()) {
730       theError = "The attribute %1 is not supported";
731       theError.arg(aPntId);
732       return false;
733     }
734   }
735   else {
736     theError = "Unable to build tangent arc on %1";
737     theError.arg(anAttrFeature->getKind());
738     return false;
739   }
740
741   return true;
742 }
743
744 bool SketchPlugin_IntersectionValidator::isValid(const AttributePtr& theAttribute,
745                                                  const std::list<std::string>& theArguments,
746                                                  Events_InfoMessage& theError) const
747 {
748   if (theAttribute->attributeType() != ModelAPI_AttributeSelection::typeId()) {
749     theError = "The attribute with the %1 type is not processed";
750     theError.arg(theAttribute->attributeType());
751     return false;
752   }
753   AttributeSelectionPtr aLineAttr =
754                        std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(theAttribute);
755   std::shared_ptr<GeomAPI_Edge> anEdge;
756   if(aLineAttr && aLineAttr->value() && aLineAttr->value()->isEdge()) {
757     anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(aLineAttr->value()));
758   } else if(aLineAttr->context() &&
759             aLineAttr->context()->shape() && aLineAttr->context()->shape()->isEdge()) {
760     anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(aLineAttr->context()->shape()));
761   }
762
763   if (!anEdge || !anEdge->isLine()) {
764     theError = "The attribute %1 should be a line";
765     theError.arg(theAttribute->id());
766     return false;
767   }
768
769   std::shared_ptr<GeomAPI_Dir> aLineDir = anEdge->line()->direction();
770
771   // find a sketch
772   std::shared_ptr<SketchPlugin_Sketch> aSketch;
773   std::set<AttributePtr> aRefs = aLineAttr->owner()->data()->refsToMe();
774   std::set<AttributePtr>::const_iterator anIt = aRefs.begin();
775   for (; anIt != aRefs.end(); ++anIt) {
776     CompositeFeaturePtr aComp =
777         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>((*anIt)->owner());
778     if (aComp && aComp->getKind() == SketchPlugin_Sketch::ID()) {
779       aSketch = std::dynamic_pointer_cast<SketchPlugin_Sketch>(aComp);
780       break;
781     }
782   }
783   if (!aSketch) {
784     theError = "There is no sketch referring to the current feature";
785     return false;
786   }
787
788   std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
789   std::shared_ptr<GeomAPI_Dir> aNormal = aPlane->direction();
790   return fabs(aNormal->dot(aLineDir)) > tolerance * tolerance;
791 }
792
793 bool SketchPlugin_SplitValidator::isValid(const AttributePtr& theAttribute,
794                                           const std::list<std::string>& theArguments,
795                                           Events_InfoMessage& theError) const
796 {
797   bool aValid = false;
798
799   if (theAttribute->attributeType() != ModelAPI_AttributeReference::typeId()) {
800     theError = "The attribute with the %1 type is not processed";
801     theError.arg(theAttribute->attributeType());
802     return aValid;
803   }
804   AttributeReferencePtr aFeatureAttr =
805             std::dynamic_pointer_cast<ModelAPI_AttributeReference>(theAttribute);
806
807   ObjectPtr anAttrObject = aFeatureAttr->value();
808   FeaturePtr anAttrFeature = ModelAPI_Feature::feature(anAttrObject);
809   if (!anAttrFeature)
810     return aValid;
811
812   std::string aKind = anAttrFeature->getKind();
813   if (aKind == SketchPlugin_Line::ID() ||
814       aKind == SketchPlugin_Arc::ID() ||
815       aKind == SketchPlugin_Circle::ID()) {
816
817     std::set<ResultPtr> anEdgeShapes;
818     ModelGeomAlgo_Shape::shapesOfType(anAttrFeature, GeomAPI_Shape::EDGE, anEdgeShapes);
819     if (anEdgeShapes.empty() || anEdgeShapes.size() > 1 /*there case has not existed yet*/)
820       return aValid;
821
822     // coincidences to the feature
823     std::set<std::shared_ptr<GeomDataAPI_Point2D> > aRefAttributes;
824     ModelGeomAlgo_Point2D::getPointsOfReference(anAttrFeature,
825                         SketchPlugin_ConstraintCoincidence::ID(),
826                         aRefAttributes, SketchPlugin_Point::ID(), SketchPlugin_Point::COORD_ID());
827
828     GeomShapePtr anAttrShape = (*anEdgeShapes.begin())->shape();
829     std::shared_ptr<SketchPlugin_Feature> aSFeature =
830                                  std::dynamic_pointer_cast<SketchPlugin_Feature>(anAttrFeature);
831     SketchPlugin_Sketch* aSketch = aSFeature->sketch();
832
833     std::shared_ptr<ModelAPI_Data> aData = aSketch->data();
834     std::shared_ptr<GeomDataAPI_Point> aC = std::dynamic_pointer_cast<GeomDataAPI_Point>(
835         aData->attribute(SketchPlugin_Sketch::ORIGIN_ID()));
836     std::shared_ptr<GeomDataAPI_Dir> aX = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
837         aData->attribute(SketchPlugin_Sketch::DIRX_ID()));
838     std::shared_ptr<GeomDataAPI_Dir> aNorm = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
839         aData->attribute(SketchPlugin_Sketch::NORM_ID()));
840     std::shared_ptr<GeomAPI_Dir> aDirY(new GeomAPI_Dir(aNorm->dir()->cross(aX->dir())));
841
842     typedef std::map<std::shared_ptr<GeomAPI_Pnt>,
843                      std::pair<std::list<std::shared_ptr<GeomDataAPI_Point2D> >,
844                                std::list<std::shared_ptr<ModelAPI_Object> > > > PointToRefsMap;
845     PointToRefsMap aPointsInfo;
846
847     ModelGeomAlgo_Point2D::getPointsInsideShape(anAttrShape, aRefAttributes, aC->pnt(),
848                                                 aX->dir(), aDirY, aPointsInfo);
849     int aCoincidentToFeature = (int)aPointsInfo.size();
850     if (aKind == SketchPlugin_Circle::ID())
851       aValid = aCoincidentToFeature >= 2;
852     else
853       aValid = aCoincidentToFeature >= 1;
854   }
855
856   return aValid;
857 }
858
859 bool SketchPlugin_TrimValidator::isValid(const AttributePtr& theAttribute,
860                                          const std::list<std::string>& theArguments,
861                                          Events_InfoMessage& theError) const
862 {
863   bool aValid = false;
864
865   if (theAttribute->attributeType() != ModelAPI_AttributeReference::typeId()) {
866     theError = "The attribute with the %1 type is not processed";
867     theError.arg(theAttribute->attributeType());
868     return aValid;
869   }
870   AttributeReferencePtr aBaseObjectAttr =
871             std::dynamic_pointer_cast<ModelAPI_AttributeReference>(theAttribute);
872
873   std::shared_ptr<SketchPlugin_Feature> aTrimFeature =
874                  std::dynamic_pointer_cast<SketchPlugin_Feature>(theAttribute->owner());
875
876   ObjectPtr aBaseObject = aBaseObjectAttr->value();
877   if (!aBaseObject) {
878     AttributePtr aPreviewAttr = aTrimFeature->attribute(SketchPlugin_Trim::PREVIEW_OBJECT());
879     aBaseObjectAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(aPreviewAttr);
880     aBaseObject = aBaseObjectAttr->value();
881   }
882
883   FeaturePtr aBaseFeature = ModelAPI_Feature::feature(aBaseObject);
884   if (!aBaseFeature)
885     return aValid;
886
887   std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
888                                  std::dynamic_pointer_cast<SketchPlugin_Feature>(aBaseFeature);
889   if (!aSketchFeature.get() || aSketchFeature->isCopy())
890     return aValid;
891
892   std::string aKind = aBaseFeature->getKind();
893   if (aKind != SketchPlugin_Line::ID() &&
894       aKind != SketchPlugin_Arc::ID() &&
895       aKind != SketchPlugin_Circle::ID())
896     return aValid;
897
898   // point on feature
899   AttributePoint2DPtr aPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
900                        aTrimFeature->data()->attribute(SketchPlugin_Trim::PREVIEW_POINT()));
901
902   SketchPlugin_Sketch* aSketch = aTrimFeature->sketch();
903
904   std::shared_ptr<GeomAPI_Pnt2d> anAttributePnt2d = aPoint->pnt();
905   std::shared_ptr<GeomAPI_Pnt> anAttributePnt = aSketch->to3D(anAttributePnt2d->x(),
906                                                               anAttributePnt2d->y());
907
908   std::map<ObjectPtr, std::set<GeomShapePtr> > aCashedShapes;
909   std::map<ObjectPtr, std::map<std::shared_ptr<GeomAPI_Pnt>,
910            std::pair<std::list<std::shared_ptr<GeomDataAPI_Point2D> >,
911                      std::list<std::shared_ptr<ModelAPI_Object> > > > > anObjectToPoints;
912   SketchPlugin_Trim::fillObjectShapes(aBaseObject, aSketch->data()->owner(),
913                                       aCashedShapes, anObjectToPoints);
914   const std::set<GeomShapePtr>& aShapes = aCashedShapes[aBaseObject];
915
916   return aShapes.size() > 1;
917 }
918
919 bool SketchPlugin_ProjectionValidator::isValid(const AttributePtr& theAttribute,
920                                                const std::list<std::string>& theArguments,
921                                                Events_InfoMessage& theError) const
922 {
923   if (theAttribute->attributeType() != ModelAPI_AttributeSelection::typeId()) {
924     theError = "The attribute with the %1 type is not processed";
925     theError.arg(theAttribute->attributeType());
926     return false;
927   }
928
929   AttributeSelectionPtr aFeatureAttr =
930       std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(theAttribute);
931   std::shared_ptr<GeomAPI_Edge> anEdge;
932   if (aFeatureAttr.get()) {
933     GeomShapePtr aVal = aFeatureAttr->value();
934     ResultPtr aRes = aFeatureAttr->context();
935     if(aFeatureAttr->value() && aFeatureAttr->value()->isEdge()) {
936       anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(aFeatureAttr->value()));
937     } else if(aFeatureAttr->context() && aFeatureAttr->context()->shape() &&
938               aFeatureAttr->context()->shape()->isEdge()) {
939       anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(aFeatureAttr->context()->shape()));
940     }
941   }
942   if (!anEdge) {
943     theError = "The attribute %1 should be an edge";
944     theError.arg(theAttribute->id());
945     return false;
946   }
947
948   // find a sketch
949   std::shared_ptr<SketchPlugin_Sketch> aSketch;
950   std::set<AttributePtr> aRefs = theAttribute->owner()->data()->refsToMe();
951   std::set<AttributePtr>::const_iterator anIt = aRefs.begin();
952   for (; anIt != aRefs.end(); ++anIt) {
953     CompositeFeaturePtr aComp =
954         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>((*anIt)->owner());
955     if (aComp && aComp->getKind() == SketchPlugin_Sketch::ID()) {
956       aSketch = std::dynamic_pointer_cast<SketchPlugin_Sketch>(aComp);
957       break;
958     }
959   }
960   if (!aSketch) {
961     theError = "There is no sketch referring to the current feature";
962     return false;
963   }
964
965   std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
966   std::shared_ptr<GeomAPI_Dir> aNormal = aPlane->direction();
967   std::shared_ptr<GeomAPI_Pnt> anOrigin = aPlane->location();
968
969   if (anEdge->isLine()) {
970     std::shared_ptr<GeomAPI_Lin> aLine = anEdge->line();
971     std::shared_ptr<GeomAPI_Dir> aLineDir = aLine->direction();
972     std::shared_ptr<GeomAPI_Pnt> aLineLoc = aLine->location();
973     double aDot = aNormal->dot(aLineDir);
974     double aDist = aLineLoc->xyz()->decreased(anOrigin->xyz())->dot(aNormal->xyz());
975     bool aValid = (fabs(aDot) >= tolerance && fabs(aDot) < 1.0 - tolerance) ||
976            (fabs(aDot) < tolerance && fabs(aDist) > tolerance);
977     if (!aValid)
978       theError = "Error: Edge is already in the sketch plane.";
979     return aValid;
980   }
981   else if (anEdge->isCircle() || anEdge->isArc()) {
982     std::shared_ptr<GeomAPI_Circ> aCircle = anEdge->circle();
983     std::shared_ptr<GeomAPI_Dir> aCircNormal = aCircle->normal();
984     std::shared_ptr<GeomAPI_Pnt> aCircCenter = aCircle->center();
985     double aDot = fabs(aNormal->dot(aCircNormal));
986     double aDist = aCircCenter->xyz()->decreased(anOrigin->xyz())->dot(aNormal->xyz());
987     bool aValid = fabs(aDot - 1.0) < tolerance * tolerance && fabs(aDist) > tolerance;
988     if (!aValid)
989       theError.arg(anEdge->isCircle() ? "Error: Cirlce is already in the sketch plane."
990                                       : "Error: Arc is already in the sketch plane.");
991     return aValid;
992   }
993
994   theError = "Error: Selected object is not line, circle or arc.";
995   return false;
996 }
997
998
999 static std::set<FeaturePtr> common(const std::set<FeaturePtr>& theSet1,
1000                                    const std::set<FeaturePtr>& theSet2)
1001 {
1002   std::set<FeaturePtr> aCommon;
1003   if (theSet1.empty() || theSet2.empty())
1004     return aCommon;
1005
1006   std::set<FeaturePtr>::const_iterator anIt2 = theSet2.begin();
1007   for (; anIt2 != theSet2.end(); ++anIt2)
1008     if (theSet1.find(*anIt2) != theSet1.end())
1009       aCommon.insert(*anIt2);
1010   return aCommon;
1011 }
1012
1013 bool SketchPlugin_DifferentReferenceValidator::isValid(
1014     const AttributePtr& theAttribute,
1015     const std::list<std::string>& theArguments,
1016     Events_InfoMessage& theError) const
1017 {
1018   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner());
1019
1020   int aNbFeaturesReferred = 0;
1021   int aNbAttributesReferred = 0;
1022   std::set<FeaturePtr> aCommonReferredFeatures;
1023
1024   // find all features referred by attributes listed in theArguments
1025   std::list<std::string>::const_iterator anArgIt = theArguments.begin();
1026   for (; anArgIt != theArguments.end(); ++anArgIt) {
1027     AttributeRefAttrPtr aRefAttr = anOwner->refattr(*anArgIt);
1028     if (!aRefAttr)
1029       continue;
1030
1031     std::set<FeaturePtr> aCoincidentFeatures;
1032     if (aRefAttr->isObject()) {
1033       FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object());
1034       if (aFeature) {
1035         aCoincidentFeatures.insert(aFeature);
1036         aNbFeaturesReferred += 1;
1037       }
1038     } else {
1039       AttributePoint2DPtr aPoint =
1040           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aRefAttr->attr());
1041       if (aPoint) {
1042         aCoincidentFeatures = SketchPlugin_Tools::findFeaturesCoincidentToPoint(aPoint);
1043         aNbAttributesReferred += 1;
1044       }
1045     }
1046
1047     if (aCommonReferredFeatures.empty())
1048       aCommonReferredFeatures = aCoincidentFeatures;
1049     else
1050       aCommonReferredFeatures = common(aCommonReferredFeatures, aCoincidentFeatures);
1051
1052     if (aCommonReferredFeatures.empty())
1053       return true;
1054   }
1055
1056   bool isOk = aNbFeaturesReferred < 1;
1057   if (aNbFeaturesReferred == 1) {
1058     if (aCommonReferredFeatures.size() == 1) {
1059       FeaturePtr aFeature = *aCommonReferredFeatures.begin();
1060       isOk = aNbAttributesReferred <= 1 ||
1061              aFeature->getKind() == SketchPlugin_Circle::ID() ||
1062              aFeature->getKind() == SketchPlugin_Arc::ID();
1063     } else
1064       isOk = false;
1065   }
1066
1067   if (!isOk)
1068     theError = "Attributes are referred to the same feature";
1069   return isOk;
1070 }
1071
1072 bool SketchPlugin_CirclePassedPointValidator::isValid(
1073     const AttributePtr& theAttribute,
1074     const std::list<std::string>&,
1075     Events_InfoMessage& theError) const
1076 {
1077   static const std::string aErrorMessage(
1078       "Passed point refers to the same feature as a center point");
1079
1080   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner());
1081
1082   AttributeRefAttrPtr aCenterRef =
1083       anOwner->refattr(SketchPlugin_MacroCircle::CENTER_POINT_REF_ID());
1084   AttributeRefAttrPtr aPassedRef =
1085       anOwner->refattr(SketchPlugin_MacroCircle::PASSED_POINT_REF_ID());
1086
1087   if (!aPassedRef->isObject())
1088     return true;
1089
1090   FeaturePtr aPassedFeature = ModelAPI_Feature::feature(aPassedRef->object());
1091   if (!aPassedFeature)
1092     return true;
1093
1094   if (aCenterRef->isObject()) {
1095     FeaturePtr aCenterFeature = ModelAPI_Feature::feature(aCenterRef->object());
1096     if (aCenterFeature == aPassedFeature) {
1097       theError = aErrorMessage;
1098       return false;
1099     }
1100   } else {
1101     AttributePoint2DPtr aCenterPoint =
1102         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aCenterRef->attr());
1103     if (aCenterPoint) {
1104       std::set<FeaturePtr> aCoincidentFeatures =
1105           SketchPlugin_Tools::findFeaturesCoincidentToPoint(aCenterPoint);
1106       // check one of coincident features is a feature referred by passed point
1107       std::set<FeaturePtr>::const_iterator anIt = aCoincidentFeatures.begin();
1108       for(; anIt != aCoincidentFeatures.end(); ++anIt)
1109         if (*anIt == aPassedFeature) {
1110           theError = aErrorMessage;
1111           return false;
1112         }
1113     }
1114   }
1115   return true;
1116 }
1117
1118 bool SketchPlugin_ThirdPointValidator::isValid(
1119     const AttributePtr& theAttribute,
1120     const std::list<std::string>& theArguments,
1121     Events_InfoMessage& theError) const
1122 {
1123   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner());
1124   return arePointsNotOnLine(anOwner, theError) &&
1125          arePointsNotSeparated(anOwner, theArguments, theError);
1126 }
1127
1128 static std::shared_ptr<GeomAPI_Pnt2d> toPoint(const FeaturePtr& theMacroCircle,
1129                                               const std::string& thePointAttrName,
1130                                               const std::string& theRefPointAttrName)
1131 {
1132   AttributePoint2DPtr aPointAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1133       theMacroCircle->attribute(thePointAttrName));
1134   AttributeRefAttrPtr aRefAttr = theMacroCircle->refattr(theRefPointAttrName);
1135
1136   std::shared_ptr<GeomAPI_Pnt2d> aPoint = aPointAttr->pnt();
1137   if (aRefAttr) {
1138     if (aRefAttr->isObject()) {
1139       // project a point onto selected feature
1140       std::shared_ptr<SketchPlugin_Feature> aFeature =
1141           std::dynamic_pointer_cast<SketchPlugin_Feature>(
1142           ModelAPI_Feature::feature(aRefAttr->object()));
1143       if (aFeature) {
1144         SketchPlugin_Sketch* aSketch = aFeature->sketch();
1145         std::shared_ptr<GeomAPI_Edge> anEdge =
1146             std::dynamic_pointer_cast<GeomAPI_Edge>(aFeature->lastResult()->shape());
1147         if (anEdge) {
1148           std::shared_ptr<GeomAPI_Pnt> aPoint3D = aSketch->to3D(aPoint->x(), aPoint->y());
1149           if (anEdge->isLine())
1150             aPoint3D = anEdge->line()->project(aPoint3D);
1151           else if (anEdge->isCircle())
1152             aPoint3D = anEdge->circle()->project(aPoint3D);
1153           if(aPoint3D)
1154             aPoint = aSketch->to2D(aPoint3D);
1155         }
1156       }
1157     } else {
1158       AttributePoint2DPtr anOtherPoint =
1159           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aRefAttr->attr());
1160       if (anOtherPoint)
1161         aPoint = anOtherPoint->pnt(); // the reference point is much more precise, use it
1162     }
1163   }
1164
1165   return aPoint;
1166 }
1167
1168 static void threePointsOfFeature(const FeaturePtr& theMacroFeature,
1169                                  std::shared_ptr<GeomAPI_Pnt2d> thePoints[3])
1170 {
1171   if (theMacroFeature->getKind() == SketchPlugin_MacroCircle::ID()) {
1172     thePoints[0] = toPoint(theMacroFeature,
1173           SketchPlugin_MacroCircle::FIRST_POINT_ID(),
1174           SketchPlugin_MacroCircle::FIRST_POINT_REF_ID());
1175     thePoints[1] = toPoint(theMacroFeature,
1176           SketchPlugin_MacroCircle::SECOND_POINT_ID(),
1177           SketchPlugin_MacroCircle::SECOND_POINT_REF_ID());
1178     thePoints[2] = toPoint(theMacroFeature,
1179           SketchPlugin_MacroCircle::THIRD_POINT_ID(),
1180           SketchPlugin_MacroCircle::THIRD_POINT_REF_ID());
1181   } else if (theMacroFeature->getKind() == SketchPlugin_MacroArc::ID()) {
1182     thePoints[0] = toPoint(theMacroFeature,
1183           SketchPlugin_MacroArc::START_POINT_2_ID(),
1184           SketchPlugin_MacroArc::START_POINT_REF_ID());
1185     thePoints[1] = toPoint(theMacroFeature,
1186           SketchPlugin_MacroArc::END_POINT_2_ID(),
1187           SketchPlugin_MacroArc::END_POINT_REF_ID());
1188     thePoints[2] = toPoint(theMacroFeature,
1189           SketchPlugin_MacroArc::PASSED_POINT_ID(),
1190           SketchPlugin_MacroArc::PASSED_POINT_REF_ID());
1191   }
1192 }
1193
1194 static bool isPointsOnLine(const std::shared_ptr<GeomAPI_Pnt2d>& thePoint1,
1195                            const std::shared_ptr<GeomAPI_Pnt2d>& thePoint2,
1196                            const std::shared_ptr<GeomAPI_Pnt2d>& thePoint3)
1197 {
1198   static const double aTolerance = 1.e-7;
1199   if (thePoint1->distance(thePoint2) < aTolerance ||
1200       thePoint1->distance(thePoint3) < aTolerance)
1201     return true;
1202
1203   std::shared_ptr<GeomAPI_Dir2d> aDirP1P2(new GeomAPI_Dir2d(thePoint2->x() - thePoint1->x(),
1204                                                             thePoint2->y() - thePoint1->y()));
1205   std::shared_ptr<GeomAPI_Dir2d> aDirP1P3(new GeomAPI_Dir2d(thePoint3->x() - thePoint1->x(),
1206                                                             thePoint3->y() - thePoint1->y()));
1207   return fabs(aDirP1P2->cross(aDirP1P3)) < aTolerance;
1208 }
1209
1210 static bool isOnSameSide(const std::shared_ptr<GeomAPI_Lin>& theLine,
1211                          const std::shared_ptr<GeomAPI_Pnt>& thePoint1,
1212                          const std::shared_ptr<GeomAPI_Pnt>& thePoint2)
1213 {
1214   static const double aTolerance = 1.e-7;
1215   std::shared_ptr<GeomAPI_Dir> aLineDir = theLine->direction();
1216   std::shared_ptr<GeomAPI_XYZ> aLineLoc = theLine->location()->xyz();
1217   std::shared_ptr<GeomAPI_Dir> aDirP1L(new GeomAPI_Dir(thePoint1->xyz()->decreased(aLineLoc)));
1218   std::shared_ptr<GeomAPI_Dir> aDirP2L(new GeomAPI_Dir(thePoint2->xyz()->decreased(aLineLoc)));
1219   return aLineDir->cross(aDirP1L)->dot(aLineDir->cross(aDirP2L)) > -aTolerance;
1220 }
1221
1222 static bool isOnSameSide(const std::shared_ptr<GeomAPI_Circ>& theCircle,
1223                          const std::shared_ptr<GeomAPI_Pnt>&  thePoint1,
1224                          const std::shared_ptr<GeomAPI_Pnt>&  thePoint2)
1225 {
1226   static const double aTolerance = 1.e-7;
1227   std::shared_ptr<GeomAPI_Pnt> aCenter = theCircle->center();
1228   double aDistP1C = thePoint1->distance(aCenter);
1229   double aDistP2C = thePoint2->distance(aCenter);
1230   return (aDistP1C - theCircle->radius()) * (aDistP2C - theCircle->radius()) > -aTolerance;
1231 }
1232
1233 bool SketchPlugin_ThirdPointValidator::arePointsNotOnLine(
1234     const FeaturePtr& theMacroFeature,
1235     Events_InfoMessage& theError) const
1236 {
1237   static const std::string aErrorPointsOnLine(
1238       "Selected points are on the same line");
1239
1240   std::shared_ptr<GeomAPI_Pnt2d> aPoints[3];
1241   threePointsOfFeature(theMacroFeature, aPoints);
1242
1243   if (isPointsOnLine(aPoints[0], aPoints[1], aPoints[2])) {
1244     theError = aErrorPointsOnLine;
1245     return false;
1246   }
1247   return true;
1248 }
1249
1250 bool SketchPlugin_ThirdPointValidator::arePointsNotSeparated(
1251     const FeaturePtr& theMacroFeature,
1252     const std::list<std::string>& theArguments,
1253     Events_InfoMessage& theError) const
1254 {
1255   static const std::string aErrorPointsApart(
1256       "Selected entity is lying between first two points");
1257
1258   AttributeRefAttrPtr aThirdPointRef = theMacroFeature->refattr(theArguments.front());
1259   FeaturePtr aRefByThird;
1260   if (aThirdPointRef->isObject())
1261     aRefByThird = ModelAPI_Feature::feature(aThirdPointRef->object());
1262   if (!aRefByThird)
1263     return true;
1264
1265   std::shared_ptr<GeomAPI_Pnt2d> aPoints[3];
1266   threePointsOfFeature(theMacroFeature, aPoints);
1267
1268   std::shared_ptr<GeomAPI_Edge> aThirdShape =
1269       std::dynamic_pointer_cast<GeomAPI_Edge>(aRefByThird->lastResult()->shape());
1270   if (!aThirdShape)
1271     return true;
1272
1273   SketchPlugin_Sketch* aSketch =
1274       std::dynamic_pointer_cast<SketchPlugin_Feature>(theMacroFeature)->sketch();
1275   std::shared_ptr<GeomAPI_Pnt> aFirstPnt3D = aSketch->to3D(aPoints[0]->x(), aPoints[0]->y());
1276   std::shared_ptr<GeomAPI_Pnt> aSecondPnt3D = aSketch->to3D(aPoints[1]->x(), aPoints[1]->y());
1277
1278   bool isOk = true;
1279   if (aThirdShape->isLine())
1280     isOk = isOnSameSide(aThirdShape->line(), aFirstPnt3D, aSecondPnt3D);
1281   else if (aThirdShape->isCircle() || aThirdShape->isArc())
1282     isOk = isOnSameSide(aThirdShape->circle(), aFirstPnt3D, aSecondPnt3D);
1283
1284   if (!isOk)
1285     theError = aErrorPointsApart;
1286   return isOk;
1287 }
1288
1289 bool SketchPlugin_ArcEndPointValidator::isValid(
1290     const AttributePtr& theAttribute,
1291     const std::list<std::string>& theArguments,
1292     Events_InfoMessage& theError) const
1293 {
1294   FeaturePtr aFeature = ModelAPI_Feature::feature(theAttribute->owner());
1295   AttributeRefAttrPtr anEndPointRef = aFeature->refattr(theArguments.front());
1296
1297   if(!anEndPointRef.get()) {
1298     return true;
1299   }
1300
1301   ObjectPtr anObject = anEndPointRef->object();
1302   AttributePtr anAttr = anEndPointRef->attr();
1303   if(!anObject.get() && !anAttr.get()) {
1304     return true;
1305   }
1306
1307   if(anEndPointRef->attr().get()) {
1308     return false;
1309   }
1310
1311   ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(anObject);
1312   if(aResult.get()) {
1313     GeomShapePtr aShape = aResult->shape();
1314     if(aShape.get() && aShape->isVertex()) {
1315       return false;
1316     }
1317   }
1318
1319   aFeature = ModelAPI_Feature::feature(anObject);
1320   if(aFeature.get()) {
1321     if(aFeature->getKind() == SketchPlugin_Point::ID()) {
1322       return false;
1323     }
1324   }
1325
1326   return true;
1327 }
1328
1329 static GeomShapePtr toInfiniteEdge(const GeomShapePtr theShape)
1330 {
1331   if(!theShape.get()) {
1332     return theShape;
1333   }
1334
1335   if(!theShape->isEdge()) {
1336     return theShape;
1337   }
1338
1339   std::shared_ptr<GeomAPI_Edge> anEdge(new GeomAPI_Edge(theShape));
1340
1341   if(!anEdge.get()) {
1342     return theShape;
1343   }
1344
1345   if(anEdge->isLine()) {
1346     std::shared_ptr<GeomAPI_Lin> aLine = anEdge->line();
1347     GeomShapePtr aShape = GeomAlgoAPI_EdgeBuilder::line(aLine);
1348     return aShape;
1349   }
1350
1351   if(anEdge->isCircle() || anEdge->isArc()) {
1352     std::shared_ptr<GeomAPI_Circ> aCircle = anEdge->circle();
1353     GeomShapePtr aShape = GeomAlgoAPI_EdgeBuilder::lineCircle(aCircle);
1354     return aShape;
1355   }
1356
1357   return theShape;
1358 }
1359
1360 bool SketchPlugin_ArcEndPointIntersectionValidator::isValid(
1361     const AttributePtr& theAttribute,
1362     const std::list<std::string>& theArguments,
1363     Events_InfoMessage& theError) const
1364 {
1365   std::shared_ptr<SketchPlugin_MacroArc> anArcFeature =
1366       std::dynamic_pointer_cast<SketchPlugin_MacroArc>(theAttribute->owner());
1367   AttributeRefAttrPtr anEndPointRef = anArcFeature->refattr(theArguments.front());
1368
1369   if(!anEndPointRef.get()) {
1370     return true;
1371   }
1372
1373   GeomShapePtr anArcShape = toInfiniteEdge(anArcFeature->getArcShape(false));
1374
1375   if(!anArcShape.get() || anArcShape->isNull()) {
1376     return true;
1377   }
1378
1379   ObjectPtr anObject = anEndPointRef->object();
1380   AttributePtr anAttr = anEndPointRef->attr();
1381   if(!anObject.get() && !anAttr.get()) {
1382     return true;
1383   }
1384
1385   ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(anObject);
1386   if(aResult.get()) {
1387     GeomShapePtr aShape = toInfiniteEdge(aResult->shape());
1388     if(aShape.get() && !aShape->isNull()) {
1389       if(anArcShape->isIntersect(aShape)) {
1390         return true;
1391       }
1392     }
1393   }
1394
1395   FeaturePtr aSelectedFeature = ModelAPI_Feature::feature(anObject);
1396   if(aSelectedFeature.get()) {
1397     std::list<ResultPtr> aResults = aSelectedFeature->results();
1398     for(std::list<ResultPtr>::const_iterator anIt = aResults.cbegin();
1399         anIt != aResults.cend();
1400         ++anIt)
1401     {
1402       GeomShapePtr aShape = toInfiniteEdge((*anIt)->shape());
1403       if(aShape.get() && !aShape->isNull()) {
1404         if(anArcShape->isIntersect(aShape)) {
1405           return true;
1406         }
1407       }
1408     }
1409   }
1410
1411   return false;
1412 }