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