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