Salome HOME
Fix crash on 2D fillet, when sketch created at the end of the folder
[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)
472       continue;
473     if (aFeature->getKind() == SketchPlugin_ConstraintTangent::ID()) {
474       AttributeRefAttrPtr anAttrRefA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
475         aFeature->attribute(SketchPlugin_ConstraintTangent::ENTITY_A()));
476       AttributeRefAttrPtr anAttrRefB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
477         aFeature->attribute(SketchPlugin_ConstraintTangent::ENTITY_B()));
478       if(anAttrRefA.get()) {
479         ResultPtr aResA = std::dynamic_pointer_cast<ModelAPI_Result>(anAttrRefA->object());
480         if(aResA.get()) {
481           DocumentPtr aDoc = aResA->document();
482           if(aDoc.get()) {
483             FeaturePtr aFeatureA = aDoc->feature(aResA);
484             if(aFeatureA.get() && aFeatureA == theFeature) {
485               return true;
486             }
487           }
488         }
489       }
490       if(anAttrRefB.get()) {
491         ResultPtr aResB = std::dynamic_pointer_cast<ModelAPI_Result>(anAttrRefB->object());
492         if(aResB.get()) {
493           DocumentPtr aDoc = aResB->document();
494           if(aDoc.get()) {
495             FeaturePtr aFeatureB = aDoc->feature(aResB);
496             if(aFeatureB.get() && aFeatureB == theFeature) {
497               return true;
498             }
499           }
500         }
501       }
502     }
503   }
504   return false;
505 }
506
507 static bool isPointPointCoincidence(const FeaturePtr& theCoincidence)
508 {
509   AttributeRefAttrPtr aRefAttr[2] = {
510       theCoincidence->refattr(SketchPlugin_Constraint::ENTITY_A()),
511       theCoincidence->refattr(SketchPlugin_Constraint::ENTITY_B())
512   };
513
514   bool arePoints = true;
515   for (int i = 0; i < 2 && arePoints; ++i) {
516     if (aRefAttr[i]->isObject()) {
517       FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr[i]->object());
518       arePoints = aFeature.get() && aFeature->getKind() == SketchPlugin_Point::ID();
519     } else
520       arePoints = aRefAttr[i]->attr().get() != NULL;
521   }
522   return arePoints;
523 }
524
525 bool SketchPlugin_FilletVertexValidator::isValid(const AttributePtr& theAttribute,
526                                                  const std::list<std::string>& theArguments,
527                                                  Events_InfoMessage& theError) const
528 {
529   AttributeRefAttrPtr aPointRefAttr =
530     std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
531   if(!aPointRefAttr.get()) {
532     theError = "Error: Point not selected.";
533     return false;
534   }
535
536   AttributePtr aPointAttribute = aPointRefAttr->attr();
537   if (!aPointAttribute.get()) {
538     theError = "Error: Bad point selected.";
539     return false;
540   }
541   std::shared_ptr<GeomAPI_Pnt2d> aSelectedPnt =
542     std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aPointAttribute)->pnt();
543
544   // Obtain constraint coincidence for the fillet point.
545   const std::set<AttributePtr>& aRefsList = aPointAttribute->owner()->data()->refsToMe();
546   FeaturePtr aConstraintCoincidence;
547   for(std::set<AttributePtr>::const_iterator anIt = aRefsList.cbegin();
548       anIt != aRefsList.cend(); ++anIt) {
549     std::shared_ptr<ModelAPI_Attribute> aAttr = (*anIt);
550     FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
551     if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
552       if (!isPointPointCoincidence(aConstrFeature))
553         continue;
554
555       AttributeRefAttrPtr anAttrRefA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
556         aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_A()));
557       AttributeRefAttrPtr anAttrRefB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
558         aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_B()));
559
560       AttributePtr anAttrA = anAttrRefA->attr();
561       if(aPointAttribute == anAttrA) {
562         aConstraintCoincidence = aConstrFeature;
563         break;
564       }
565
566       AttributePtr anAttrB = anAttrRefB->attr();
567       if(aPointAttribute == anAttrB) {
568         aConstraintCoincidence = aConstrFeature;
569         break;
570       }
571     }
572   }
573
574   if(!aConstraintCoincidence.get()) {
575     theError = "Error: one of the selected point does not have coicidence.";
576     return false;
577   }
578
579   // Get coincides from constraint.
580   std::set<FeaturePtr> aCoinsides;
581   SketchPlugin_Tools::findCoincidences(aConstraintCoincidence,
582                                         SketchPlugin_ConstraintCoincidence::ENTITY_A(),
583                                         aCoinsides,
584                                         true);
585   SketchPlugin_Tools::findCoincidences(aConstraintCoincidence,
586                                         SketchPlugin_ConstraintCoincidence::ENTITY_B(),
587                                         aCoinsides,
588                                         true);
589
590   // Remove points and external lines from set of coincides.
591   std::set<FeaturePtr> aNewSetOfCoincides;
592   for(std::set<FeaturePtr>::iterator anIt = aCoinsides.begin();
593       anIt != aCoinsides.end(); ++anIt) {
594     std::shared_ptr<SketchPlugin_SketchEntity> aSketchEntity =
595       std::dynamic_pointer_cast<SketchPlugin_SketchEntity>(*anIt);
596     if(aSketchEntity.get() && (aSketchEntity->isCopy() || aSketchEntity->isExternal())) {
597       continue;
598     }
599     if((*anIt)->getKind() != SketchPlugin_Line::ID() &&
600         (*anIt)->getKind() != SketchPlugin_Arc::ID()) {
601           continue;
602     }
603     if((*anIt)->getKind() == SketchPlugin_Arc::ID()) {
604       AttributePtr anArcCenter = (*anIt)->attribute(SketchPlugin_Arc::CENTER_ID());
605       std::shared_ptr<GeomAPI_Pnt2d> anArcCenterPnt =
606         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anArcCenter)->pnt();
607       double aDistSelectedArcCenter = aSelectedPnt->distance(anArcCenterPnt);
608       if(aDistSelectedArcCenter < tolerance) {
609         continue;
610       }
611     }
612     aNewSetOfCoincides.insert(*anIt);
613   }
614   aCoinsides = aNewSetOfCoincides;
615
616   // If we still have more than two coincides remove auxilary entities from set of coincides.
617   if(aCoinsides.size() > 2) {
618     aNewSetOfCoincides.clear();
619     for(std::set<FeaturePtr>::iterator anIt = aCoinsides.begin();
620         anIt != aCoinsides.end(); ++anIt) {
621       if(!(*anIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) {
622         aNewSetOfCoincides.insert(*anIt);
623       }
624     }
625     aCoinsides = aNewSetOfCoincides;
626   }
627
628   if(aCoinsides.size() != 2) {
629     theError = "Error: One of the selected points does not have two suitable edges for fillet.";
630     return false;
631   }
632
633   // Check that selected edges don't have tangent constraint.
634   std::set<FeaturePtr>::iterator anIt = aCoinsides.begin();
635   FeaturePtr aFirstFeature = *anIt++;
636   FeaturePtr aSecondFeature = *anIt;
637   const std::set<AttributePtr>& aFirstFeatureRefsList = aFirstFeature->data()->refsToMe();
638   if(hasSameTangentFeature(aFirstFeatureRefsList, aSecondFeature)) {
639     theError = "Error: Edges in selected point has tangent constraint.";
640     return false;
641   }
642
643   std::list<ResultPtr> aFirstResults = aFirstFeature->results();
644   for(std::list<ResultPtr>::iterator aResIt = aFirstResults.begin();
645       aResIt != aFirstResults.end(); ++aResIt) {
646     ResultPtr aRes = *aResIt;
647     const std::set<AttributePtr>& aResRefsList = aRes->data()->refsToMe();
648     if(hasSameTangentFeature(aResRefsList, aSecondFeature)) {
649       theError = "Error: Edges in selected point has tangent constraint.";
650       return false;
651     }
652   }
653
654   // Check the features are not tangent
655   std::shared_ptr<GeomAPI_Shape> aFirstShape = aFirstFeature->lastResult()->shape();
656   std::shared_ptr<GeomAPI_Shape> aSecondShape = aSecondFeature->lastResult()->shape();
657   if (!aFirstShape || !aFirstShape->isEdge() ||
658       !aSecondShape || !aSecondShape->isEdge()) {
659     theError = "Error: At least on of the features is not an edge";
660     return false;
661   }
662
663   std::shared_ptr<GeomAPI_Edge> anEdge1 = std::dynamic_pointer_cast<GeomAPI_Edge>(aFirstShape);
664   std::shared_ptr<GeomAPI_Edge> anEdge2 = std::dynamic_pointer_cast<GeomAPI_Edge>(aSecondShape);
665
666   static const double TOL = 1.e-7;
667   if (anEdge1->isLine() && anEdge2->isLine()) {
668     // Check that lines not collinear
669     std::shared_ptr<GeomAPI_Dir> aDir1 = anEdge1->line()->direction();
670     std::shared_ptr<GeomAPI_Dir> aDir2 = anEdge2->line()->direction();
671     double aCross = aDir1->cross(aDir2)->squareModulus();
672     if (aCross < TOL * TOL)
673       return false;
674   } else if (anEdge1->isArc() && anEdge2->isArc()) {
675     // check the circles are not tangent
676     std::shared_ptr<GeomAPI_Circ> aCirc1 = anEdge1->circle();
677     std::shared_ptr<GeomAPI_Circ> aCirc2 = anEdge2->circle();
678     double aDistCC = aCirc1->center()->distance(aCirc2->center());
679     double aRadSum = aCirc1->radius() + aCirc2->radius();
680     double aRadDiff = fabs(aCirc1->radius() - aCirc2->radius());
681     if (fabs(aDistCC - aRadSum) < TOL || fabs(aDistCC - aRadDiff) < TOL)
682       return false;
683   } else {
684     // check whether line and arc are tangent
685     std::shared_ptr<GeomAPI_Circ> aCirc;
686     std::shared_ptr<GeomAPI_Lin> aLine;
687     if (anEdge1->isLine()) {
688       aLine = anEdge1->line();
689       aCirc = anEdge2->circle();
690     } else {
691       aCirc = anEdge1->circle();
692       aLine = anEdge2->line();
693     }
694
695     double aDistCL = aLine->distance(aCirc->center());
696     if (fabs(aDistCL - aCirc->radius()) < TOL)
697       return false;
698   }
699
700   return true;
701 }
702
703 bool SketchPlugin_MiddlePointAttrValidator::isValid(const AttributePtr& theAttribute,
704                                                     const std::list<std::string>& theArguments,
705                                                     Events_InfoMessage& theError) const
706 {
707   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
708     theError = "The attribute with the %1 type is not processed";
709     theError.arg(theAttribute->attributeType());
710     return false;
711   }
712
713   // there is a check whether the feature contains a point and a linear edge or two point values
714   std::string aParamA = theArguments.front();
715   SessionPtr aMgr = ModelAPI_Session::get();
716   ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
717
718   FeaturePtr anAttributeFeature =
719     std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
720   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
721   AttributeRefAttrPtr anOtherAttr = anAttributeFeature->data()->refattr(aParamA);
722
723   AttributeRefAttrPtr aRefAttrs[2] = {aRefAttr, anOtherAttr};
724   int aNbPoints = 0;
725   int aNbLines = 0;
726   for (int i = 0; i < 2; ++i) {
727     if (!aRefAttrs[i]->isObject())
728       ++aNbPoints;
729     else {
730       FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttrs[i]->object());
731       if (!aFeature) {
732         if (aNbPoints + aNbLines != 0)
733           return true;
734         else continue;
735       }
736
737       if (aFeature->getKind() == SketchPlugin_Point::ID())
738         ++aNbPoints;
739       else if (aFeature->getKind() == SketchPlugin_Line::ID())
740         ++aNbLines;
741     }
742   }
743
744   if (aNbPoints != 1 || aNbLines != 1) {
745     theError = "Middle point constraint allows points and lines only";
746     return false;
747   }
748   return true;
749 }
750
751 bool SketchPlugin_ArcTangentPointValidator::isValid(const AttributePtr& theAttribute,
752                                                     const std::list<std::string>& /*theArguments*/,
753                                                     Events_InfoMessage& theError) const
754 {
755   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
756     theError = "The attribute with the %1 type is not processed";
757     theError.arg(theAttribute->attributeType());
758     return false;
759   }
760   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
761   AttributePtr anAttr = aRefAttr->attr();
762   if (!anAttr) {
763     theError = "The attribute %1 should be a point";
764     theError.arg(theAttribute->id());
765     return false;
766   }
767
768   FeaturePtr anAttrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anAttr->owner());
769   const std::string& aFeatureType = anAttrFeature->getKind();
770   if (aFeatureType == SketchPlugin_Arc::ID()) {
771     // selected point should not be a center of arc
772     const std::string& aPntId = anAttr->id();
773     if (aPntId != SketchPlugin_Arc::START_ID() && aPntId != SketchPlugin_Arc::END_ID()) {
774       theError = "The attribute %1 is not supported";
775       theError.arg(aPntId);
776       return false;
777     }
778   }
779   else if (aFeatureType == SketchPlugin_Line::ID()) {
780     // selected point should be bound point of line
781     const std::string& aPntId = anAttr->id();
782     if (aPntId != SketchPlugin_Line::START_ID() && aPntId != SketchPlugin_Line::END_ID()) {
783       theError = "The attribute %1 is not supported";
784       theError.arg(aPntId);
785       return false;
786     }
787   }
788   else {
789     theError = "Unable to build tangent arc on %1";
790     theError.arg(anAttrFeature->getKind());
791     return false;
792   }
793
794   return true;
795 }
796
797 bool SketchPlugin_IntersectionValidator::isValid(const AttributePtr& theAttribute,
798                                                  const std::list<std::string>& theArguments,
799                                                  Events_InfoMessage& theError) const
800 {
801   if (theAttribute->attributeType() != ModelAPI_AttributeSelection::typeId()) {
802     theError = "The attribute with the %1 type is not processed";
803     theError.arg(theAttribute->attributeType());
804     return false;
805   }
806   AttributeSelectionPtr anExternalAttr =
807       std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(theAttribute);
808   std::shared_ptr<GeomAPI_Edge> anEdge;
809   if (anExternalAttr && anExternalAttr->value() && anExternalAttr->value()->isEdge()) {
810     anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(anExternalAttr->value()));
811   } else if(anExternalAttr->context() && anExternalAttr->context()->shape() &&
812             anExternalAttr->context()->shape()->isEdge()) {
813     anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(anExternalAttr->context()->shape()));
814   }
815
816   if (!anEdge) {
817     theError = "The attribute %1 should be an edge";
818     theError.arg(theAttribute->id());
819     return false;
820   }
821
822   // find a sketch
823   std::shared_ptr<SketchPlugin_Sketch> aSketch;
824   std::set<AttributePtr> aRefs = anExternalAttr->owner()->data()->refsToMe();
825   std::set<AttributePtr>::const_iterator anIt = aRefs.begin();
826   for (; anIt != aRefs.end(); ++anIt) {
827     CompositeFeaturePtr aComp =
828         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>((*anIt)->owner());
829     if (aComp && aComp->getKind() == SketchPlugin_Sketch::ID()) {
830       aSketch = std::dynamic_pointer_cast<SketchPlugin_Sketch>(aComp);
831       break;
832     }
833   }
834   if (!aSketch) {
835     theError = "There is no sketch referring to the current feature";
836     return false;
837   }
838
839   // check the edge is intersected with sketch plane
840   std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
841
842   std::list<GeomPointPtr> anIntersectionsPoints;
843   anEdge->intersectWithPlane(aPlane, anIntersectionsPoints);
844   if (anIntersectionsPoints.empty()) {
845     theError = "The edge is not intersected with sketch plane";
846     return false;
847   }
848   return true;
849 }
850
851 bool SketchPlugin_SplitValidator::isValid(const AttributePtr& theAttribute,
852                                           const std::list<std::string>& theArguments,
853                                           Events_InfoMessage& theError) const
854 {
855   bool aValid = false;
856
857   if (theAttribute->attributeType() != ModelAPI_AttributeReference::typeId()) {
858     theError = "The attribute with the %1 type is not processed";
859     theError.arg(theAttribute->attributeType());
860     return aValid;
861   }
862   AttributeReferencePtr aFeatureAttr =
863             std::dynamic_pointer_cast<ModelAPI_AttributeReference>(theAttribute);
864
865   ObjectPtr anAttrObject = aFeatureAttr->value();
866   FeaturePtr anAttrFeature = ModelAPI_Feature::feature(anAttrObject);
867   if (!anAttrFeature)
868     return aValid;
869
870   std::string aKind = anAttrFeature->getKind();
871   if (aKind == SketchPlugin_Line::ID() ||
872       aKind == SketchPlugin_Arc::ID() ||
873       aKind == SketchPlugin_Circle::ID()) {
874
875     std::set<ResultPtr> anEdgeShapes;
876     ModelGeomAlgo_Shape::shapesOfType(anAttrFeature, GeomAPI_Shape::EDGE, anEdgeShapes);
877     if (anEdgeShapes.empty() || anEdgeShapes.size() > 1 /*there case has not existed yet*/)
878       return aValid;
879
880     // coincidences to the feature
881     std::set<std::shared_ptr<GeomDataAPI_Point2D> > aRefAttributes;
882     ModelGeomAlgo_Point2D::getPointsOfReference(anAttrFeature,
883                         SketchPlugin_ConstraintCoincidence::ID(),
884                         aRefAttributes, SketchPlugin_Point::ID(), SketchPlugin_Point::COORD_ID());
885
886     GeomShapePtr anAttrShape = (*anEdgeShapes.begin())->shape();
887     std::shared_ptr<SketchPlugin_Feature> aSFeature =
888                                  std::dynamic_pointer_cast<SketchPlugin_Feature>(anAttrFeature);
889     SketchPlugin_Sketch* aSketch = aSFeature->sketch();
890
891     std::shared_ptr<ModelAPI_Data> aData = aSketch->data();
892     std::shared_ptr<GeomDataAPI_Point> aC = std::dynamic_pointer_cast<GeomDataAPI_Point>(
893         aData->attribute(SketchPlugin_Sketch::ORIGIN_ID()));
894     std::shared_ptr<GeomDataAPI_Dir> aX = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
895         aData->attribute(SketchPlugin_Sketch::DIRX_ID()));
896     std::shared_ptr<GeomDataAPI_Dir> aNorm = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
897         aData->attribute(SketchPlugin_Sketch::NORM_ID()));
898     std::shared_ptr<GeomAPI_Dir> aDirY(new GeomAPI_Dir(aNorm->dir()->cross(aX->dir())));
899
900     typedef std::map<std::shared_ptr<GeomAPI_Pnt>,
901                      std::pair<std::list<std::shared_ptr<GeomDataAPI_Point2D> >,
902                                std::list<std::shared_ptr<ModelAPI_Object> > > > PointToRefsMap;
903     PointToRefsMap aPointsInfo;
904
905     ModelGeomAlgo_Point2D::getPointsInsideShape(anAttrShape, aRefAttributes, aC->pnt(),
906                                                 aX->dir(), aDirY, aPointsInfo);
907     int aCoincidentToFeature = (int)aPointsInfo.size();
908     if (aKind == SketchPlugin_Circle::ID())
909       aValid = aCoincidentToFeature >= 2;
910     else
911       aValid = aCoincidentToFeature >= 1;
912   }
913
914   return aValid;
915 }
916
917 bool SketchPlugin_TrimValidator::isValid(const AttributePtr& theAttribute,
918                                          const std::list<std::string>& theArguments,
919                                          Events_InfoMessage& theError) const
920 {
921   bool aValid = false;
922
923   if (theAttribute->attributeType() != ModelAPI_AttributeReference::typeId()) {
924     theError = "The attribute with the %1 type is not processed";
925     theError.arg(theAttribute->attributeType());
926     return aValid;
927   }
928   AttributeReferencePtr aBaseObjectAttr =
929             std::dynamic_pointer_cast<ModelAPI_AttributeReference>(theAttribute);
930
931   std::shared_ptr<SketchPlugin_Feature> aTrimFeature =
932                  std::dynamic_pointer_cast<SketchPlugin_Feature>(theAttribute->owner());
933
934   ObjectPtr aBaseObject = aBaseObjectAttr->value();
935   if (!aBaseObject) {
936     AttributePtr aPreviewAttr = aTrimFeature->attribute(SketchPlugin_Trim::PREVIEW_OBJECT());
937     aBaseObjectAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(aPreviewAttr);
938     aBaseObject = aBaseObjectAttr->value();
939   }
940
941   FeaturePtr aBaseFeature = ModelAPI_Feature::feature(aBaseObject);
942   if (!aBaseFeature)
943     return aValid;
944
945   std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
946                                  std::dynamic_pointer_cast<SketchPlugin_Feature>(aBaseFeature);
947   if (!aSketchFeature.get() || aSketchFeature->isCopy())
948     return aValid;
949
950   std::string aKind = aBaseFeature->getKind();
951   if (aKind != SketchPlugin_Line::ID() &&
952       aKind != SketchPlugin_Arc::ID() &&
953       aKind != SketchPlugin_Circle::ID())
954     return aValid;
955
956   // point on feature
957   AttributePoint2DPtr aPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
958                        aTrimFeature->data()->attribute(SketchPlugin_Trim::PREVIEW_POINT()));
959
960   SketchPlugin_Sketch* aSketch = aTrimFeature->sketch();
961
962   std::shared_ptr<GeomAPI_Pnt2d> anAttributePnt2d = aPoint->pnt();
963   std::shared_ptr<GeomAPI_Pnt> anAttributePnt = aSketch->to3D(anAttributePnt2d->x(),
964                                                               anAttributePnt2d->y());
965
966   std::map<ObjectPtr, std::set<GeomShapePtr> > aCashedShapes;
967   std::map<ObjectPtr, std::map<std::shared_ptr<GeomAPI_Pnt>,
968            std::pair<std::list<std::shared_ptr<GeomDataAPI_Point2D> >,
969                      std::list<std::shared_ptr<ModelAPI_Object> > > > > anObjectToPoints;
970   SketchPlugin_Trim::fillObjectShapes(aBaseObject, aSketch->data()->owner(),
971                                       aCashedShapes, anObjectToPoints);
972   const std::set<GeomShapePtr>& aShapes = aCashedShapes[aBaseObject];
973
974   return aShapes.size() > 1;
975 }
976
977 bool SketchPlugin_ProjectionValidator::isValid(const AttributePtr& theAttribute,
978                                                const std::list<std::string>& theArguments,
979                                                Events_InfoMessage& theError) const
980 {
981   if (theAttribute->attributeType() != ModelAPI_AttributeSelection::typeId()) {
982     theError = "The attribute with the %1 type is not processed";
983     theError.arg(theAttribute->attributeType());
984     return false;
985   }
986
987   AttributeSelectionPtr aFeatureAttr =
988       std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(theAttribute);
989   std::shared_ptr<GeomAPI_Edge> anEdge;
990   std::shared_ptr<SketchPlugin_Feature> aSketchFeature;
991   if (aFeatureAttr.get()) {
992     GeomShapePtr aVal = aFeatureAttr->value();
993     ResultPtr aRes = aFeatureAttr->context();
994     if (aVal && aVal->isVertex())
995       return true; // vertex is always could be projected
996     if (aVal && aVal->isEdge()) {
997       anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(aFeatureAttr->value()));
998     } else if(aRes && aRes->shape()) {
999       if (aRes->shape()->isVertex())
1000         return true; // vertex is always could be projected
1001       else if (aRes->shape()->isEdge())
1002         anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(aFeatureAttr->context()->shape()));
1003     }
1004
1005     // try to convert result to sketch feature
1006     if (aRes) {
1007       aSketchFeature =
1008         std::dynamic_pointer_cast<SketchPlugin_Feature>(ModelAPI_Feature::feature(aRes));
1009     }
1010   }
1011   if (!anEdge) {
1012     theError = "The attribute %1 should be an edge or vertex";
1013     theError.arg(theAttribute->id());
1014     return false;
1015   }
1016
1017   // find a sketch
1018   std::shared_ptr<SketchPlugin_Sketch> aSketch;
1019   std::set<AttributePtr> aRefs = theAttribute->owner()->data()->refsToMe();
1020   std::set<AttributePtr>::const_iterator anIt = aRefs.begin();
1021   for (; anIt != aRefs.end(); ++anIt) {
1022     CompositeFeaturePtr aComp =
1023         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>((*anIt)->owner());
1024     if (aComp && aComp->getKind() == SketchPlugin_Sketch::ID()) {
1025       aSketch = std::dynamic_pointer_cast<SketchPlugin_Sketch>(aComp);
1026       break;
1027     }
1028   }
1029   if (!aSketch) {
1030     theError = "There is no sketch referring to the current feature";
1031     return false;
1032   }
1033   if (aSketchFeature && aSketch.get() == aSketchFeature->sketch()) {
1034     theError = "Unable to project feature from the same sketch";
1035     return false;
1036   }
1037
1038   std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
1039   std::shared_ptr<GeomAPI_Dir> aNormal = aPlane->direction();
1040   std::shared_ptr<GeomAPI_Pnt> anOrigin = aPlane->location();
1041
1042   if (anEdge->isLine()) {
1043     std::shared_ptr<GeomAPI_Lin> aLine = anEdge->line();
1044     std::shared_ptr<GeomAPI_Dir> aLineDir = aLine->direction();
1045     double aDot = fabs(aNormal->dot(aLineDir));
1046     bool aValid = fabs(aDot - 1.0) >= tolerance * tolerance;
1047     if (!aValid)
1048       theError = "Error: Edge is already in the sketch plane.";
1049     return aValid;
1050   }
1051   else if (anEdge->isCircle() || anEdge->isArc()) {
1052     std::shared_ptr<GeomAPI_Circ> aCircle = anEdge->circle();
1053     std::shared_ptr<GeomAPI_Dir> aCircNormal = aCircle->normal();
1054     double aDot = fabs(aNormal->dot(aCircNormal));
1055     bool aValid = fabs(aDot - 1.0) < tolerance * tolerance;
1056     if (!aValid)
1057       theError.arg(anEdge->isCircle() ? "Error: Cirlce is already in the sketch plane."
1058                                       : "Error: Arc is already in the sketch plane.");
1059     return aValid;
1060   }
1061
1062   theError = "Error: Selected object is not line, circle or arc.";
1063   return false;
1064 }
1065
1066
1067 static std::set<FeaturePtr> common(const std::set<FeaturePtr>& theSet1,
1068                                    const std::set<FeaturePtr>& theSet2)
1069 {
1070   std::set<FeaturePtr> aCommon;
1071   if (theSet1.empty() || theSet2.empty())
1072     return aCommon;
1073
1074   std::set<FeaturePtr>::const_iterator anIt2 = theSet2.begin();
1075   for (; anIt2 != theSet2.end(); ++anIt2)
1076     if (theSet1.find(*anIt2) != theSet1.end())
1077       aCommon.insert(*anIt2);
1078   return aCommon;
1079 }
1080
1081 bool SketchPlugin_DifferentReferenceValidator::isValid(
1082     const AttributePtr& theAttribute,
1083     const std::list<std::string>& theArguments,
1084     Events_InfoMessage& theError) const
1085 {
1086   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner());
1087
1088   int aNbFeaturesReferred = 0;
1089   int aNbAttributesReferred = 0;
1090   std::set<FeaturePtr> aCommonReferredFeatures;
1091
1092   // find all features referred by attributes listed in theArguments
1093   std::list<std::string>::const_iterator anArgIt = theArguments.begin();
1094   for (; anArgIt != theArguments.end(); ++anArgIt) {
1095     AttributeRefAttrPtr aRefAttr = anOwner->refattr(*anArgIt);
1096     if (!aRefAttr)
1097       continue;
1098
1099     std::set<FeaturePtr> aCoincidentFeatures;
1100     if (aRefAttr->isObject()) {
1101       FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object());
1102       if (aFeature) {
1103         aCoincidentFeatures.insert(aFeature);
1104         aNbFeaturesReferred += 1;
1105       }
1106     } else {
1107       AttributePoint2DPtr aPoint =
1108           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aRefAttr->attr());
1109       if (aPoint) {
1110         aCoincidentFeatures = SketchPlugin_Tools::findFeaturesCoincidentToPoint(aPoint);
1111         aNbAttributesReferred += 1;
1112       }
1113     }
1114
1115     if (aCommonReferredFeatures.empty())
1116       aCommonReferredFeatures = aCoincidentFeatures;
1117     else
1118       aCommonReferredFeatures = common(aCommonReferredFeatures, aCoincidentFeatures);
1119
1120     if (aCommonReferredFeatures.empty())
1121       return true;
1122   }
1123
1124   bool isOk = aNbFeaturesReferred < 1;
1125   if (aNbFeaturesReferred == 1) {
1126     if (aCommonReferredFeatures.size() == 1) {
1127       FeaturePtr aFeature = *aCommonReferredFeatures.begin();
1128       isOk = aNbAttributesReferred <= 1 ||
1129              aFeature->getKind() == SketchPlugin_Circle::ID() ||
1130              aFeature->getKind() == SketchPlugin_Arc::ID();
1131     } else
1132       isOk = false;
1133   }
1134
1135   if (!isOk)
1136     theError = "Attributes are referred to the same feature";
1137   return isOk;
1138 }
1139
1140 bool SketchPlugin_DifferentPointReferenceValidator::isValid(
1141     const AttributePtr& theAttribute,
1142     const std::list<std::string>& theArguments,
1143     Events_InfoMessage& theError) const
1144 {
1145   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner());
1146   std::set<AttributePoint2DPtr> aReferredCoincidentPoints;
1147
1148   // find all points referred by attributes listed in theArguments
1149   bool hasRefsToPoints = false;
1150   std::list<std::string>::const_iterator anArgIt = theArguments.begin();
1151   for (; anArgIt != theArguments.end(); ++anArgIt) {
1152     AttributeRefAttrPtr aRefAttr = anOwner->refattr(*anArgIt);
1153     if (!aRefAttr)
1154       continue;
1155
1156     if (!aRefAttr->isObject()) {
1157       AttributePoint2DPtr aPoint =
1158           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aRefAttr->attr());
1159       if (aReferredCoincidentPoints.empty())
1160         aReferredCoincidentPoints = SketchPlugin_Tools::findPointsCoincidentToPoint(aPoint);
1161       else if (aReferredCoincidentPoints.find(aPoint) == aReferredCoincidentPoints.end())
1162         return true; // non-coincident point has been found
1163       else
1164         hasRefsToPoints = true;
1165     }
1166   }
1167
1168   if (hasRefsToPoints)
1169     theError = "Attributes are referred to the same point";
1170   return !hasRefsToPoints;
1171 }
1172
1173 bool SketchPlugin_CirclePassedPointValidator::isValid(
1174     const AttributePtr& theAttribute,
1175     const std::list<std::string>&,
1176     Events_InfoMessage& theError) const
1177 {
1178   static const std::string aErrorMessage(
1179       "Passed point refers to the same feature as a center point");
1180
1181   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner());
1182
1183   AttributeRefAttrPtr aCenterRef =
1184       anOwner->refattr(SketchPlugin_MacroCircle::CENTER_POINT_REF_ID());
1185   AttributeRefAttrPtr aPassedRef =
1186       anOwner->refattr(SketchPlugin_MacroCircle::PASSED_POINT_REF_ID());
1187
1188   if (!aPassedRef->isObject())
1189     return true;
1190
1191   FeaturePtr aPassedFeature = ModelAPI_Feature::feature(aPassedRef->object());
1192   if (!aPassedFeature)
1193     return true;
1194
1195   if (aCenterRef->isObject()) {
1196     if (aCenterRef->object() == aPassedRef->object()) {
1197       theError = aErrorMessage;
1198       return false;
1199     }
1200   } else {
1201     AttributePoint2DPtr aCenterPoint =
1202         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aCenterRef->attr());
1203     if (aCenterPoint) {
1204       std::set<FeaturePtr> aCoincidentFeatures =
1205           SketchPlugin_Tools::findFeaturesCoincidentToPoint(aCenterPoint);
1206       // check one of coincident features is a feature referred by passed point
1207       std::set<FeaturePtr>::const_iterator anIt = aCoincidentFeatures.begin();
1208       for(; anIt != aCoincidentFeatures.end(); ++anIt)
1209         if (*anIt == aPassedFeature) {
1210           theError = aErrorMessage;
1211           return false;
1212         }
1213     }
1214   }
1215   return true;
1216 }
1217
1218 bool SketchPlugin_ThirdPointValidator::isValid(
1219     const AttributePtr& theAttribute,
1220     const std::list<std::string>& theArguments,
1221     Events_InfoMessage& theError) const
1222 {
1223   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner());
1224   return arePointsNotOnLine(anOwner, theError) &&
1225          arePointsNotSeparated(anOwner, theArguments, theError);
1226 }
1227
1228 static std::shared_ptr<GeomAPI_Pnt2d> toPoint(const FeaturePtr& theMacroCircle,
1229                                               const std::string& thePointAttrName,
1230                                               const std::string& theRefPointAttrName)
1231 {
1232   AttributePoint2DPtr aPointAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1233       theMacroCircle->attribute(thePointAttrName));
1234   AttributeRefAttrPtr aRefAttr = theMacroCircle->refattr(theRefPointAttrName);
1235
1236   std::shared_ptr<GeomAPI_Pnt2d> aPoint = aPointAttr->pnt();
1237   if (aRefAttr) {
1238     if (aRefAttr->isObject()) {
1239       // project a point onto selected feature
1240       std::shared_ptr<SketchPlugin_Feature> aFeature =
1241           std::dynamic_pointer_cast<SketchPlugin_Feature>(
1242           ModelAPI_Feature::feature(aRefAttr->object()));
1243       if (aFeature) {
1244         SketchPlugin_Sketch* aSketch = aFeature->sketch();
1245         std::shared_ptr<GeomAPI_Edge> anEdge =
1246             std::dynamic_pointer_cast<GeomAPI_Edge>(aFeature->lastResult()->shape());
1247         if (anEdge) {
1248           std::shared_ptr<GeomAPI_Pnt> aPoint3D = aSketch->to3D(aPoint->x(), aPoint->y());
1249           if (anEdge->isLine())
1250             aPoint3D = anEdge->line()->project(aPoint3D);
1251           else if (anEdge->isCircle())
1252             aPoint3D = anEdge->circle()->project(aPoint3D);
1253           if(aPoint3D)
1254             aPoint = aSketch->to2D(aPoint3D);
1255         }
1256       }
1257     } else {
1258       AttributePoint2DPtr anOtherPoint =
1259           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aRefAttr->attr());
1260       if (anOtherPoint)
1261         aPoint = anOtherPoint->pnt(); // the reference point is much more precise, use it
1262     }
1263   }
1264
1265   return aPoint;
1266 }
1267
1268 static void threePointsOfFeature(const FeaturePtr& theMacroFeature,
1269                                  std::shared_ptr<GeomAPI_Pnt2d> thePoints[3])
1270 {
1271   if (theMacroFeature->getKind() == SketchPlugin_MacroCircle::ID()) {
1272     thePoints[0] = toPoint(theMacroFeature,
1273           SketchPlugin_MacroCircle::FIRST_POINT_ID(),
1274           SketchPlugin_MacroCircle::FIRST_POINT_REF_ID());
1275     thePoints[1] = toPoint(theMacroFeature,
1276           SketchPlugin_MacroCircle::SECOND_POINT_ID(),
1277           SketchPlugin_MacroCircle::SECOND_POINT_REF_ID());
1278     thePoints[2] = toPoint(theMacroFeature,
1279           SketchPlugin_MacroCircle::THIRD_POINT_ID(),
1280           SketchPlugin_MacroCircle::THIRD_POINT_REF_ID());
1281   } else if (theMacroFeature->getKind() == SketchPlugin_MacroArc::ID()) {
1282     thePoints[0] = toPoint(theMacroFeature,
1283           SketchPlugin_MacroArc::START_POINT_2_ID(),
1284           SketchPlugin_MacroArc::START_POINT_REF_ID());
1285     thePoints[1] = toPoint(theMacroFeature,
1286           SketchPlugin_MacroArc::END_POINT_2_ID(),
1287           SketchPlugin_MacroArc::END_POINT_REF_ID());
1288     thePoints[2] = toPoint(theMacroFeature,
1289           SketchPlugin_MacroArc::PASSED_POINT_ID(),
1290           SketchPlugin_MacroArc::PASSED_POINT_REF_ID());
1291   }
1292 }
1293
1294 static bool isPointsOnLine(const std::shared_ptr<GeomAPI_Pnt2d>& thePoint1,
1295                            const std::shared_ptr<GeomAPI_Pnt2d>& thePoint2,
1296                            const std::shared_ptr<GeomAPI_Pnt2d>& thePoint3)
1297 {
1298   static const double aTolerance = 1.e-7;
1299   if (thePoint1->distance(thePoint2) < aTolerance ||
1300       thePoint1->distance(thePoint3) < aTolerance)
1301     return true;
1302
1303   std::shared_ptr<GeomAPI_Dir2d> aDirP1P2(new GeomAPI_Dir2d(thePoint2->x() - thePoint1->x(),
1304                                                             thePoint2->y() - thePoint1->y()));
1305   std::shared_ptr<GeomAPI_Dir2d> aDirP1P3(new GeomAPI_Dir2d(thePoint3->x() - thePoint1->x(),
1306                                                             thePoint3->y() - thePoint1->y()));
1307   return fabs(aDirP1P2->cross(aDirP1P3)) < aTolerance;
1308 }
1309
1310 static bool isOnSameSide(const std::shared_ptr<GeomAPI_Lin>& theLine,
1311                          const std::shared_ptr<GeomAPI_Pnt>& thePoint1,
1312                          const std::shared_ptr<GeomAPI_Pnt>& thePoint2)
1313 {
1314   static const double aTolerance = 1.e-7;
1315   std::shared_ptr<GeomAPI_Dir> aLineDir = theLine->direction();
1316   std::shared_ptr<GeomAPI_XYZ> aLineLoc = theLine->location()->xyz();
1317
1318   std::shared_ptr<GeomAPI_XYZ> aVec1 = thePoint1->xyz()->decreased(aLineLoc);
1319   // the first point is on the line
1320   if (aVec1->squareModulus() < aTolerance * aTolerance)
1321     return false;
1322   std::shared_ptr<GeomAPI_Dir> aDirP1L(new GeomAPI_Dir(aVec1));
1323   std::shared_ptr<GeomAPI_XYZ> aVec2 = thePoint2->xyz()->decreased(aLineLoc);
1324   // the second point is on the line
1325   if (aVec2->squareModulus() < aTolerance * aTolerance)
1326     return false;
1327   std::shared_ptr<GeomAPI_Dir> aDirP2L(new GeomAPI_Dir(aVec2));
1328
1329   return aLineDir->cross(aDirP1L)->dot(aLineDir->cross(aDirP2L)) > -aTolerance;
1330 }
1331
1332 static bool isOnSameSide(const std::shared_ptr<GeomAPI_Circ>& theCircle,
1333                          const std::shared_ptr<GeomAPI_Pnt>&  thePoint1,
1334                          const std::shared_ptr<GeomAPI_Pnt>&  thePoint2)
1335 {
1336   static const double aTolerance = 1.e-7;
1337   std::shared_ptr<GeomAPI_Pnt> aCenter = theCircle->center();
1338   double aDistP1C = thePoint1->distance(aCenter);
1339   double aDistP2C = thePoint2->distance(aCenter);
1340   return (aDistP1C - theCircle->radius()) * (aDistP2C - theCircle->radius()) > -aTolerance;
1341 }
1342
1343 bool SketchPlugin_ThirdPointValidator::arePointsNotOnLine(
1344     const FeaturePtr& theMacroFeature,
1345     Events_InfoMessage& theError) const
1346 {
1347   static const std::string aErrorPointsOnLine(
1348       "Selected points are on the same line");
1349
1350   std::shared_ptr<GeomAPI_Pnt2d> aPoints[3];
1351   threePointsOfFeature(theMacroFeature, aPoints);
1352
1353   if (isPointsOnLine(aPoints[0], aPoints[1], aPoints[2])) {
1354     theError = aErrorPointsOnLine;
1355     return false;
1356   }
1357   return true;
1358 }
1359
1360 bool SketchPlugin_ThirdPointValidator::arePointsNotSeparated(
1361     const FeaturePtr& theMacroFeature,
1362     const std::list<std::string>& theArguments,
1363     Events_InfoMessage& theError) const
1364 {
1365   static const std::string aErrorPointsApart(
1366       "Selected entity is lying between first two points");
1367
1368   AttributeRefAttrPtr aThirdPointRef = theMacroFeature->refattr(theArguments.front());
1369   FeaturePtr aRefByThird;
1370   if (aThirdPointRef->isObject())
1371     aRefByThird = ModelAPI_Feature::feature(aThirdPointRef->object());
1372   if (!aRefByThird)
1373     return true;
1374
1375   std::shared_ptr<GeomAPI_Pnt2d> aPoints[3];
1376   threePointsOfFeature(theMacroFeature, aPoints);
1377
1378   std::shared_ptr<GeomAPI_Edge> aThirdShape =
1379       std::dynamic_pointer_cast<GeomAPI_Edge>(aRefByThird->lastResult()->shape());
1380   if (!aThirdShape)
1381     return true;
1382
1383   SketchPlugin_Sketch* aSketch =
1384       std::dynamic_pointer_cast<SketchPlugin_Feature>(theMacroFeature)->sketch();
1385   std::shared_ptr<GeomAPI_Pnt> aFirstPnt3D = aSketch->to3D(aPoints[0]->x(), aPoints[0]->y());
1386   std::shared_ptr<GeomAPI_Pnt> aSecondPnt3D = aSketch->to3D(aPoints[1]->x(), aPoints[1]->y());
1387
1388   bool isOk = true;
1389   if (aThirdShape->isLine())
1390     isOk = isOnSameSide(aThirdShape->line(), aFirstPnt3D, aSecondPnt3D);
1391   else if (aThirdShape->isCircle() || aThirdShape->isArc())
1392     isOk = isOnSameSide(aThirdShape->circle(), aFirstPnt3D, aSecondPnt3D);
1393
1394   if (!isOk)
1395     theError = aErrorPointsApart;
1396   return isOk;
1397 }
1398
1399 bool SketchPlugin_ArcEndPointValidator::isValid(
1400     const AttributePtr& theAttribute,
1401     const std::list<std::string>& theArguments,
1402     Events_InfoMessage& theError) const
1403 {
1404   FeaturePtr aFeature = ModelAPI_Feature::feature(theAttribute->owner());
1405   AttributeRefAttrPtr anEndPointRef = aFeature->refattr(theArguments.front());
1406
1407   if(!anEndPointRef.get()) {
1408     return true;
1409   }
1410
1411   ObjectPtr anObject = anEndPointRef->object();
1412   AttributePtr anAttr = anEndPointRef->attr();
1413   if(!anObject.get() && !anAttr.get()) {
1414     return true;
1415   }
1416
1417   if(anEndPointRef->attr().get()) {
1418     return false;
1419   }
1420
1421   ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(anObject);
1422   if(aResult.get()) {
1423     GeomShapePtr aShape = aResult->shape();
1424     if(aShape.get() && aShape->isVertex()) {
1425       return false;
1426     }
1427   }
1428
1429   aFeature = ModelAPI_Feature::feature(anObject);
1430   if(aFeature.get()) {
1431     if(aFeature->getKind() == SketchPlugin_Point::ID()) {
1432       return false;
1433     }
1434   }
1435
1436   return true;
1437 }
1438
1439 static GeomShapePtr toInfiniteEdge(const GeomShapePtr theShape)
1440 {
1441   if(!theShape.get()) {
1442     return theShape;
1443   }
1444
1445   if(!theShape->isEdge()) {
1446     return theShape;
1447   }
1448
1449   std::shared_ptr<GeomAPI_Edge> anEdge(new GeomAPI_Edge(theShape));
1450
1451   if(!anEdge.get()) {
1452     return theShape;
1453   }
1454
1455   if(anEdge->isLine()) {
1456     std::shared_ptr<GeomAPI_Lin> aLine = anEdge->line();
1457     GeomShapePtr aShape = GeomAlgoAPI_EdgeBuilder::line(aLine);
1458     return aShape;
1459   }
1460
1461   if(anEdge->isCircle() || anEdge->isArc()) {
1462     std::shared_ptr<GeomAPI_Circ> aCircle = anEdge->circle();
1463     GeomShapePtr aShape = GeomAlgoAPI_EdgeBuilder::lineCircle(aCircle);
1464     return aShape;
1465   }
1466
1467   return theShape;
1468 }
1469
1470 bool SketchPlugin_ArcEndPointIntersectionValidator::isValid(
1471     const AttributePtr& theAttribute,
1472     const std::list<std::string>& theArguments,
1473     Events_InfoMessage& theError) const
1474 {
1475   std::shared_ptr<SketchPlugin_MacroArc> anArcFeature =
1476       std::dynamic_pointer_cast<SketchPlugin_MacroArc>(theAttribute->owner());
1477   AttributeRefAttrPtr anEndPointRef = anArcFeature->refattr(theArguments.front());
1478
1479   if(!anEndPointRef.get()) {
1480     return true;
1481   }
1482
1483   GeomShapePtr anArcShape = toInfiniteEdge(anArcFeature->getArcShape(false));
1484
1485   if(!anArcShape.get() || anArcShape->isNull()) {
1486     return true;
1487   }
1488
1489   ObjectPtr anObject = anEndPointRef->object();
1490   AttributePtr anAttr = anEndPointRef->attr();
1491   if(!anObject.get() && !anAttr.get()) {
1492     return true;
1493   }
1494
1495   ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(anObject);
1496   if(aResult.get()) {
1497     GeomShapePtr aShape = aResult->shape();
1498     if (!aShape->isEdge())
1499       return true;
1500     aShape = toInfiniteEdge(aShape);
1501     if(aShape.get() && !aShape->isNull()) {
1502       if(anArcShape->isIntersect(aShape)) {
1503         return true;
1504       }
1505     }
1506   }
1507
1508   FeaturePtr aSelectedFeature = ModelAPI_Feature::feature(anObject);
1509   if(aSelectedFeature.get()) {
1510     std::list<ResultPtr> aResults = aSelectedFeature->results();
1511     for(std::list<ResultPtr>::const_iterator anIt = aResults.cbegin();
1512         anIt != aResults.cend();
1513         ++anIt)
1514     {
1515       GeomShapePtr aShape = (*anIt)->shape();
1516       if (!aShape->isEdge())
1517         return true;
1518       aShape = toInfiniteEdge(aShape);
1519       if(aShape.get() && !aShape->isNull()) {
1520         if(anArcShape->isIntersect(aShape)) {
1521           return true;
1522         }
1523       }
1524     }
1525   }
1526
1527   return false;
1528 }
1529
1530 bool SketchPlugin_HasNoConstraint::isValid(const AttributePtr& theAttribute,
1531                                            const std::list<std::string>& theArguments,
1532                                            Events_InfoMessage& theError) const
1533 {
1534   std::set<std::string> aFeatureKinds;
1535   for (std::list<std::string>::const_iterator anArgIt = theArguments.begin();
1536        anArgIt != theArguments.end(); anArgIt++) {
1537     aFeatureKinds.insert(*anArgIt);
1538   }
1539
1540   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
1541     theError = "The attribute with the %1 type is not processed";
1542     theError.arg(theAttribute->attributeType());
1543     return false;
1544   }
1545
1546   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>
1547                                                                       (theAttribute);
1548   bool isObject = aRefAttr->isObject();
1549   if (!isObject) {
1550     theError = "It uses an empty object";
1551     return false;
1552   }
1553   ObjectPtr anObject = aRefAttr->object();
1554   FeaturePtr aFeature = ModelAPI_Feature::feature(anObject);
1555   if (!aFeature.get()) {
1556     theError = "The feature of the checked attribute is empty";
1557     return false;
1558   }
1559
1560   FeaturePtr aCurrentFeature = ModelAPI_Feature::feature(aRefAttr->owner());
1561
1562   std::set<AttributePtr> aRefsList = anObject->data()->refsToMe();
1563   std::set<AttributePtr>::const_iterator anIt = aRefsList.begin();
1564   for (; anIt != aRefsList.end(); anIt++) {
1565     FeaturePtr aRefFeature = ModelAPI_Feature::feature((*anIt)->owner());
1566     if (aRefFeature.get() && aCurrentFeature != aRefFeature &&
1567         aFeatureKinds.find(aRefFeature->getKind()) != aFeatureKinds.end())
1568       return false; // constraint is found, that means that the check is not valid
1569   }
1570   return true;
1571 }
1572
1573 bool SketchPlugin_ReplicationReferenceValidator::isValid(
1574     const AttributePtr& theAttribute,
1575     const std::list<std::string>& theArguments,
1576     Events_InfoMessage& theError) const
1577 {
1578   AttributeRefAttrPtr aRefAttr =
1579       std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
1580   if (!aRefAttr)
1581   {
1582     theError = "Incorrect attribute";
1583     return false;
1584   }
1585
1586   ObjectPtr anOwner;
1587   if (aRefAttr->isObject())
1588     anOwner = aRefAttr->object();
1589   else
1590   {
1591     AttributePtr anAttr = aRefAttr->attr();
1592     anOwner = anAttr->owner();
1593   }
1594   FeaturePtr anAttrOwnerFeature = ModelAPI_Feature::feature(anOwner);
1595   if (!anAttrOwnerFeature)
1596     return true;
1597   AttributeBooleanPtr aCopyAttr = anAttrOwnerFeature->boolean(SketchPlugin_SketchEntity::COPY_ID());
1598   if (!aCopyAttr || !aCopyAttr->value())
1599     return true; // feature is not a copy, thus valid
1600
1601   // check the copy feature is already referred by the "Multi" feature
1602   FeaturePtr aMultiFeature = ModelAPI_Feature::feature(theAttribute->owner());
1603   AttributeRefListPtr aRefList = aMultiFeature->reflist(theArguments.front());
1604   for (int i = 0; i < aRefList->size(); ++i)
1605   {
1606     FeaturePtr aRefOwner = ModelAPI_Feature::feature(aRefList->object(i));
1607     if (aRefOwner == anAttrOwnerFeature)
1608     {
1609       theError = "Attribute refers to the object generated by this feature";
1610       return false;
1611     }
1612   }
1613
1614   return true;
1615 }
1616
1617 bool SketchPlugin_SketchFeatureValidator::isValid(const AttributePtr& theAttribute,
1618                                                   const std::list<std::string>& theArguments,
1619                                                   Events_InfoMessage& theError) const
1620 {
1621   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
1622     theError = "The attribute with the %1 type is not processed";
1623     theError.arg(theAttribute->attributeType());
1624     return false;
1625   }
1626
1627   // check the attribute refers to a sketch feature
1628   AttributeRefAttrPtr aRefAttr =
1629       std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
1630   bool isSketchFeature = aRefAttr->isObject();
1631   if (isSketchFeature) {
1632     FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object());
1633     isSketchFeature = aFeature.get() != NULL;
1634     if (isSketchFeature) {
1635       std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
1636           std::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
1637       isSketchFeature = aSketchFeature.get() != NULL;
1638     }
1639   }
1640
1641   if (isSketchFeature)
1642     return true;
1643
1644   theError = "The object selected is not a sketch feature";
1645   return false;
1646 }
1647
1648 bool SketchPlugin_MultiRotationAngleValidator::isValid(const AttributePtr& theAttribute,
1649                                                        const std::list<std::string>& theArguments,
1650                                                        Events_InfoMessage& theError) const
1651 {
1652   if (theAttribute->attributeType() != ModelAPI_AttributeDouble::typeId()) {
1653     theError = "The attribute with the %1 type is not processed";
1654     theError.arg(theAttribute->attributeType());
1655     return false;
1656   }
1657
1658   AttributeDoublePtr anAngleAttr =
1659     std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
1660
1661   FeaturePtr aMultiRotation = ModelAPI_Feature::feature(theAttribute->owner());
1662   AttributeStringPtr anAngleType =
1663       aMultiRotation->string(SketchPlugin_MultiRotation::ANGLE_TYPE());
1664   AttributeIntegerPtr aNbCopies =
1665       aMultiRotation->integer(SketchPlugin_MultiRotation::NUMBER_OF_OBJECTS_ID());
1666
1667   if (anAngleType->value() != "FullAngle")
1668   {
1669     double aFullAngleValue = anAngleAttr->value() * (aNbCopies->value() - 1);
1670     if (aFullAngleValue < -1.e-7 || aFullAngleValue > 359.9999999)
1671     {
1672       theError = "Rotation single angle should produce full angle less than 360 degree";
1673       return false;
1674     }
1675   }
1676   else
1677   {
1678     double aFullAngleValue = anAngleAttr->value();
1679     if (aFullAngleValue < -1.e-7 || aFullAngleValue > 360.0000001)
1680     {
1681       theError = "Rotation full angle should be in range [0, 360]";
1682       return false;
1683     }
1684   }
1685
1686   return true;
1687 }