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