Salome HOME
f039a445e70440083e0c886a9336fe4102a5f0f2
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_Validators.cpp
1 // Copyright (C) 2014-2023  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   FeaturePtr anEdge1, anEdge2;
674   return isValidVertex(theAttribute, theError, anEdge1, anEdge2);
675 }
676
677 bool SketchPlugin_FilletVertexValidator::isValidVertex(const AttributePtr& theAttribute,
678                                                        Events_InfoMessage& theError,
679                                                        FeaturePtr&         theEdge1,
680                                                        FeaturePtr&         theEdge2)
681 {
682   AttributeRefAttrPtr aPointRefAttr =
683     std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
684   if(!aPointRefAttr.get()) {
685     theError = "Error: Point not selected.";
686     return false;
687   }
688
689   AttributePtr aPointAttribute = aPointRefAttr->attr();
690   if (!aPointAttribute.get()) {
691     theError = "Error: Bad point selected.";
692     return false;
693   }
694   std::shared_ptr<GeomAPI_Pnt2d> aSelectedPnt =
695     std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aPointAttribute)->pnt();
696
697   // Obtain constraint coincidence for the fillet point.
698   const std::set<AttributePtr>& aRefsList = aPointAttribute->owner()->data()->refsToMe();
699   FeaturePtr aConstraintCoincidence;
700   for(std::set<AttributePtr>::const_iterator anIt = aRefsList.cbegin();
701       anIt != aRefsList.cend(); ++anIt) {
702     std::shared_ptr<ModelAPI_Attribute> aAttr = (*anIt);
703     FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
704     if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
705       if (!isPointPointCoincidence(aConstrFeature))
706         continue;
707
708       AttributeRefAttrPtr anAttrRefA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
709         aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_A()));
710       AttributeRefAttrPtr anAttrRefB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
711         aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_B()));
712
713       AttributePtr anAttrA = anAttrRefA->attr();
714       if(aPointAttribute == anAttrA) {
715         aConstraintCoincidence = aConstrFeature;
716         break;
717       }
718
719       AttributePtr anAttrB = anAttrRefB->attr();
720       if(aPointAttribute == anAttrB) {
721         aConstraintCoincidence = aConstrFeature;
722         break;
723       }
724     }
725   }
726
727   if(!aConstraintCoincidence.get()) {
728     theError = "Error: one of the selected point does not have coincidence.";
729     return false;
730   }
731
732   // Get coincides from constraint.
733   std::set<FeaturePtr> aCoinsides;
734   SketchPlugin_Tools::findCoincidences(aConstraintCoincidence,
735                                        SketchPlugin_ConstraintCoincidence::ENTITY_A(),
736                                        aCoinsides,
737                                        true);
738   SketchPlugin_Tools::findCoincidences(aConstraintCoincidence,
739                                        SketchPlugin_ConstraintCoincidence::ENTITY_B(),
740                                        aCoinsides,
741                                        true);
742
743   // Remove points and external lines from set of coincides.
744   std::set<FeaturePtr> aNewSetOfCoincides;
745   for(std::set<FeaturePtr>::iterator anIt = aCoinsides.begin();
746       anIt != aCoinsides.end(); ++anIt) {
747     std::shared_ptr<SketchPlugin_SketchEntity> aSketchEntity =
748       std::dynamic_pointer_cast<SketchPlugin_SketchEntity>(*anIt);
749     if(aSketchEntity.get() && (aSketchEntity->isCopy() || aSketchEntity->isExternal())) {
750       continue;
751     }
752     if((*anIt)->getKind() != SketchPlugin_Line::ID() &&
753         (*anIt)->getKind() != SketchPlugin_Arc::ID()) {
754           continue;
755     }
756     if((*anIt)->getKind() == SketchPlugin_Arc::ID()) {
757       AttributePtr anArcCenter = (*anIt)->attribute(SketchPlugin_Arc::CENTER_ID());
758       std::shared_ptr<GeomAPI_Pnt2d> anArcCenterPnt =
759         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anArcCenter)->pnt();
760       double aDistSelectedArcCenter = aSelectedPnt->distance(anArcCenterPnt);
761       if(aDistSelectedArcCenter < tolerance) {
762         continue;
763       }
764     }
765     aNewSetOfCoincides.insert(*anIt);
766   }
767   aCoinsides = aNewSetOfCoincides;
768
769   // If we still have more than two coincides remove auxilary entities from set of coincides.
770   if(aCoinsides.size() > 2) {
771     aNewSetOfCoincides.clear();
772     for(std::set<FeaturePtr>::iterator anIt = aCoinsides.begin();
773         anIt != aCoinsides.end(); ++anIt) {
774       if(!(*anIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) {
775         aNewSetOfCoincides.insert(*anIt);
776       }
777     }
778     aCoinsides = aNewSetOfCoincides;
779   }
780
781   if(aCoinsides.size() != 2) {
782     theError = "Error: One of the selected points does not have two suitable edges for fillet.";
783     return false;
784   }
785
786   // output edges
787   std::set<FeaturePtr>::iterator aFIt = aCoinsides.begin();
788   theEdge1 = *aFIt;
789   theEdge2 = *(++aFIt);
790
791   // Check that selected edges don't have tangent constraint.
792   std::set<FeaturePtr>::iterator anIt = aCoinsides.begin();
793   FeaturePtr aFirstFeature = *anIt++;
794   FeaturePtr aSecondFeature = *anIt;
795   const std::set<AttributePtr>& aFirstFeatureRefsList = aFirstFeature->data()->refsToMe();
796   if(hasSameTangentFeature(aFirstFeatureRefsList, aSecondFeature)) {
797     theError = "Error: Edges in selected point has tangent constraint.";
798     return false;
799   }
800
801   std::list<ResultPtr> aFirstResults = aFirstFeature->results();
802   for(std::list<ResultPtr>::iterator aResIt = aFirstResults.begin();
803       aResIt != aFirstResults.end(); ++aResIt) {
804     ResultPtr aRes = *aResIt;
805     const std::set<AttributePtr>& aResRefsList = aRes->data()->refsToMe();
806     if(hasSameTangentFeature(aResRefsList, aSecondFeature)) {
807       theError = "Error: Edges in selected point has tangent constraint.";
808       return false;
809     }
810   }
811
812   // Check the features are not tangent
813   std::shared_ptr<GeomAPI_Shape> aFirstShape = aFirstFeature->lastResult()->shape();
814   std::shared_ptr<GeomAPI_Shape> aSecondShape = aSecondFeature->lastResult()->shape();
815   if (!aFirstShape || !aFirstShape->isEdge() ||
816       !aSecondShape || !aSecondShape->isEdge()) {
817     theError = "Error: At least on of the features is not an edge";
818     return false;
819   }
820
821   std::shared_ptr<GeomAPI_Edge> anEdge1 = std::dynamic_pointer_cast<GeomAPI_Edge>(aFirstShape);
822   std::shared_ptr<GeomAPI_Edge> anEdge2 = std::dynamic_pointer_cast<GeomAPI_Edge>(aSecondShape);
823
824   static const double TOL = 1.e-7;
825   if (anEdge1->isLine() && anEdge2->isLine()) {
826     // Check that lines not collinear
827     std::shared_ptr<GeomAPI_Dir> aDir1 = anEdge1->line()->direction();
828     std::shared_ptr<GeomAPI_Dir> aDir2 = anEdge2->line()->direction();
829     double aCross = aDir1->cross(aDir2)->squareModulus();
830     if (aCross < TOL * TOL)
831       return false;
832   } else if (anEdge1->isArc() && anEdge2->isArc()) {
833     // check the circles are not tangent
834     std::shared_ptr<GeomAPI_Circ> aCirc1 = anEdge1->circle();
835     std::shared_ptr<GeomAPI_Circ> aCirc2 = anEdge2->circle();
836     double aDistCC = aCirc1->center()->distance(aCirc2->center());
837     double aRadSum = aCirc1->radius() + aCirc2->radius();
838     double aRadDiff = fabs(aCirc1->radius() - aCirc2->radius());
839     if (fabs(aDistCC - aRadSum) < TOL || fabs(aDistCC - aRadDiff) < TOL)
840       return false;
841   } else {
842     // check whether line and arc are tangent
843     std::shared_ptr<GeomAPI_Circ> aCirc;
844     std::shared_ptr<GeomAPI_Lin> aLine;
845     if (anEdge1->isLine()) {
846       aLine = anEdge1->line();
847       aCirc = anEdge2->circle();
848     } else {
849       aCirc = anEdge1->circle();
850       aLine = anEdge2->line();
851     }
852
853     double aDistCL = aLine->distance(aCirc->center());
854     if (fabs(aDistCL - aCirc->radius()) < TOL)
855       return false;
856   }
857
858   return true;
859 }
860
861 bool SketchPlugin_MiddlePointAttrValidator::isValid(const AttributePtr& theAttribute,
862                                                     const std::list<std::string>& theArguments,
863                                                     Events_InfoMessage& theError) const
864 {
865   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
866     theError = "The attribute with the %1 type is not processed";
867     theError.arg(theAttribute->attributeType());
868     return false;
869   }
870
871   // there is a check whether the feature contains a point and a linear edge or two point values
872   std::string aParamA = theArguments.front();
873
874   FeaturePtr anAttributeFeature =
875     std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
876   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
877   AttributeRefAttrPtr anOtherAttr = anAttributeFeature->data()->refattr(aParamA);
878
879   AttributeRefAttrPtr aRefAttrs[2] = {aRefAttr, anOtherAttr};
880   int aNbPoints = 0;
881   int aNbLines = 0;
882   for (int i = 0; i < 2; ++i) {
883     if (!aRefAttrs[i]->isObject())
884       ++aNbPoints;
885     else {
886       FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttrs[i]->object());
887       if (!aFeature) {
888         if (aNbPoints + aNbLines != 0)
889           return true;
890         else continue;
891       }
892
893       if (aFeature->getKind() == SketchPlugin_Point::ID())
894         ++aNbPoints;
895       else if (aFeature->getKind() == SketchPlugin_Line::ID() ||
896                aFeature->getKind() == SketchPlugin_Arc::ID() ||
897                aFeature->getKind() == SketchPlugin_EllipticArc::ID())
898         ++aNbLines;
899     }
900   }
901
902   if (aNbPoints != 1 || aNbLines != 1) {
903     theError = "Middle point constraint allows points and lines only";
904     return false;
905   }
906   return true;
907 }
908
909 bool SketchPlugin_ArcTangentPointValidator::isValid(const AttributePtr& theAttribute,
910                                                     const std::list<std::string>& /*theArguments*/,
911                                                     Events_InfoMessage& theError) const
912 {
913   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
914     theError = "The attribute with the %1 type is not processed";
915     theError.arg(theAttribute->attributeType());
916     return false;
917   }
918   FeaturePtr anOwner = std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
919   AttributeStringPtr anArcTypeAttr = anOwner->string(SketchPlugin_MacroArc::ARC_TYPE());
920   if (anArcTypeAttr && anArcTypeAttr->value() != SketchPlugin_MacroArc::ARC_TYPE_BY_TANGENT_EDGE())
921     return true; // not applicable for non-tangent arcs
922
923   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
924   AttributePtr anAttr = aRefAttr->attr();
925   if (!anAttr) {
926     theError = "The attribute %1 should be a point";
927     theError.arg(theAttribute->id());
928     return false;
929   }
930
931   FeaturePtr anAttrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anAttr->owner());
932   const std::string& aFeatureType = anAttrFeature->getKind();
933   if (aFeatureType == SketchPlugin_Arc::ID()) {
934     // selected point should not be a center of arc
935     const std::string& aPntId = anAttr->id();
936     if (aPntId != SketchPlugin_Arc::START_ID() && aPntId != SketchPlugin_Arc::END_ID()) {
937       theError = "The attribute %1 is not supported";
938       theError.arg(aPntId);
939       return false;
940     }
941   }
942   else if (aFeatureType == SketchPlugin_Line::ID()) {
943     // selected point should be bound point of line
944     const std::string& aPntId = anAttr->id();
945     if (aPntId != SketchPlugin_Line::START_ID() && aPntId != SketchPlugin_Line::END_ID()) {
946       theError = "The attribute %1 is not supported";
947       theError.arg(aPntId);
948       return false;
949     }
950   }
951   else {
952     theError = "Unable to build tangent arc on %1";
953     theError.arg(anAttrFeature->getKind());
954     return false;
955   }
956
957   return true;
958 }
959
960 bool SketchPlugin_ArcTransversalPointValidator::isValid(
961     const AttributePtr& theAttribute,
962     const std::list<std::string>& /*theArguments*/,
963     Events_InfoMessage& theError) const
964 {
965   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
966     theError = "The attribute with the %1 type is not processed";
967     theError.arg(theAttribute->attributeType());
968     return false;
969   }
970   FeaturePtr anOwner = std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
971   AttributeStringPtr anArcTypeAttr = anOwner->string(SketchPlugin_MacroArc::ARC_TYPE());
972   if (anArcTypeAttr &&
973       anArcTypeAttr->value() != SketchPlugin_MacroArc::ARC_TYPE_BY_TRANSVERSAL_LINE())
974     return true; // not applicable for non-transversal arcs
975
976   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
977   AttributePtr anAttr = aRefAttr->attr();
978   if (!anAttr) {
979     theError = "The attribute %1 should be a point";
980     theError.arg(theAttribute->id());
981     return false;
982   }
983
984   FeaturePtr anAttrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anAttr->owner());
985   const std::string& aFeatureType = anAttrFeature->getKind();
986   if (aFeatureType == SketchPlugin_Line::ID()) {
987     // selected point should be bound point of line
988     const std::string& aPntId = anAttr->id();
989     if (aPntId != SketchPlugin_Line::START_ID() && aPntId != SketchPlugin_Line::END_ID()) {
990       theError = "The attribute %1 is not supported";
991       theError.arg(aPntId);
992       return false;
993     }
994   }
995   else {
996     theError = "Unable to build perpendicular arc on %1";
997     theError.arg(anAttrFeature->getKind());
998     return false;
999   }
1000
1001   return true;
1002 }
1003
1004 bool SketchPlugin_IntersectionValidator::isValid(const AttributePtr& theAttribute,
1005                                                  const std::list<std::string>& theArguments,
1006                                                  Events_InfoMessage& theError) const
1007 {
1008   if (theAttribute->attributeType() != ModelAPI_AttributeSelection::typeId()) {
1009     theError = "The attribute with the %1 type is not processed";
1010     theError.arg(theAttribute->attributeType());
1011     return false;
1012   }
1013   AttributeSelectionPtr anExternalAttr =
1014       std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(theAttribute);
1015   std::shared_ptr<GeomAPI_Edge> anEdge;
1016   if (anExternalAttr && anExternalAttr->value() && anExternalAttr->value()->isEdge()) {
1017     anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(anExternalAttr->value()));
1018   } else if(anExternalAttr->context() && anExternalAttr->context()->shape() &&
1019             anExternalAttr->context()->shape()->isEdge()) {
1020     anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(anExternalAttr->context()->shape()));
1021   }
1022
1023   if (!anEdge) {
1024     theError = "The attribute %1 should be an edge";
1025     theError.arg(theAttribute->id());
1026     return false;
1027   }
1028
1029   // find a sketch
1030   std::shared_ptr<SketchPlugin_Sketch> aSketch;
1031   std::set<AttributePtr> aRefs = anExternalAttr->owner()->data()->refsToMe();
1032   std::set<AttributePtr>::const_iterator anIt = aRefs.begin();
1033   for (; anIt != aRefs.end(); ++anIt) {
1034     CompositeFeaturePtr aComp =
1035         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>((*anIt)->owner());
1036     if (aComp && aComp->getKind() == SketchPlugin_Sketch::ID()) {
1037       aSketch = std::dynamic_pointer_cast<SketchPlugin_Sketch>(aComp);
1038       break;
1039     }
1040   }
1041   if (!aSketch) {
1042     theError = "There is no sketch referring to the current feature";
1043     return false;
1044   }
1045
1046   // check the edge is intersected with sketch plane
1047   std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
1048
1049   std::list<GeomPointPtr> anIntersectionsPoints;
1050   anEdge->intersectWithPlane(aPlane, anIntersectionsPoints);
1051   if (anIntersectionsPoints.empty()) {
1052     theError = "The edge is not intersected with sketch plane";
1053     return false;
1054   }
1055   return true;
1056 }
1057
1058 bool SketchPlugin_SplitValidator::isValid(const AttributePtr& theAttribute,
1059                                           const std::list<std::string>& theArguments,
1060                                           Events_InfoMessage& theError) const
1061 {
1062   bool aValid = false;
1063
1064   if (theAttribute->attributeType() != ModelAPI_AttributeReference::typeId()) {
1065     theError = "The attribute with the %1 type is not processed";
1066     theError.arg(theAttribute->attributeType());
1067     return aValid;
1068   }
1069   AttributeReferencePtr aFeatureAttr =
1070             std::dynamic_pointer_cast<ModelAPI_AttributeReference>(theAttribute);
1071   std::shared_ptr<SketchPlugin_Feature> aSplitFeature =
1072     std::dynamic_pointer_cast<SketchPlugin_Feature>(theAttribute->owner());
1073
1074   ObjectPtr anAttrObject = aFeatureAttr->value();
1075   if (!anAttrObject) {
1076     AttributePtr aPreviewAttr = aSplitFeature->attribute(SketchPlugin_Trim::PREVIEW_OBJECT());
1077     aFeatureAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(aPreviewAttr);
1078     anAttrObject = aFeatureAttr->value();
1079   }
1080
1081   FeaturePtr anAttrFeature = ModelAPI_Feature::feature(anAttrObject);
1082   if (!anAttrFeature)
1083     return aValid;
1084
1085   // B-splines are not supported by the Split yet
1086   if (anAttrFeature->getKind() == SketchPlugin_BSpline::ID() ||
1087       anAttrFeature->getKind() == SketchPlugin_BSplinePeriodic::ID()) {
1088     theError = "Not supported";
1089     return false;
1090   }
1091
1092   std::set<ResultPtr> anEdgeShapes;
1093   ModelGeomAlgo_Shape::shapesOfType(anAttrFeature, GeomAPI_Shape::EDGE, anEdgeShapes);
1094   if (anEdgeShapes.empty() || anEdgeShapes.size() > 1 /*there case has not existed yet*/)
1095     return aValid;
1096
1097   // coincidences to the feature
1098   std::set<std::shared_ptr<GeomDataAPI_Point2D> > aRefAttributes;
1099   ModelGeomAlgo_Point2D::getPointsOfReference(anAttrFeature,
1100                       SketchPlugin_ConstraintCoincidence::ID(),
1101                       aRefAttributes, SketchPlugin_Point::ID(), SketchPlugin_Point::COORD_ID());
1102
1103   GeomShapePtr anAttrShape = (*anEdgeShapes.begin())->shape();
1104   std::shared_ptr<SketchPlugin_Feature> aSFeature =
1105                                 std::dynamic_pointer_cast<SketchPlugin_Feature>(anAttrFeature);
1106   if (!aSFeature || aSFeature->isCopy())
1107     return false;
1108   SketchPlugin_Sketch* aSketch = aSFeature->sketch();
1109
1110   std::shared_ptr<ModelAPI_Data> aData = aSketch->data();
1111   std::shared_ptr<GeomDataAPI_Point> aC = std::dynamic_pointer_cast<GeomDataAPI_Point>(
1112       aData->attribute(SketchPlugin_Sketch::ORIGIN_ID()));
1113   std::shared_ptr<GeomDataAPI_Dir> aX = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
1114       aData->attribute(SketchPlugin_Sketch::DIRX_ID()));
1115   std::shared_ptr<GeomDataAPI_Dir> aNorm = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
1116       aData->attribute(SketchPlugin_Sketch::NORM_ID()));
1117   std::shared_ptr<GeomAPI_Dir> aDirY(new GeomAPI_Dir(aNorm->dir()->cross(aX->dir())));
1118
1119   typedef std::map<std::shared_ptr<GeomAPI_Pnt>,
1120                     std::pair<std::list<std::shared_ptr<GeomDataAPI_Point2D> >,
1121                               std::list<std::shared_ptr<ModelAPI_Object> > > > PointToRefsMap;
1122   PointToRefsMap aPointsInfo;
1123
1124   ModelGeomAlgo_Point2D::getPointsInsideShape(anAttrShape, aRefAttributes, aC->pnt(),
1125                                               aX->dir(), aDirY, aPointsInfo);
1126   int aCoincidentToFeature = (int)aPointsInfo.size();
1127   if (anAttrFeature->getKind() == SketchPlugin_Circle::ID() ||
1128       anAttrFeature->getKind() == SketchPlugin_Ellipse::ID())
1129     aValid = aCoincidentToFeature >= 2;
1130   else
1131     aValid = aCoincidentToFeature >= 1;
1132
1133   return aValid;
1134 }
1135
1136 bool SketchPlugin_TrimValidator::isValid(const AttributePtr& theAttribute,
1137                                          const std::list<std::string>& theArguments,
1138                                          Events_InfoMessage& theError) const
1139 {
1140   bool aValid = false;
1141
1142   if (theAttribute->attributeType() != ModelAPI_AttributeReference::typeId()) {
1143     theError = "The attribute with the %1 type is not processed";
1144     theError.arg(theAttribute->attributeType());
1145     return aValid;
1146   }
1147   AttributeReferencePtr aBaseObjectAttr =
1148             std::dynamic_pointer_cast<ModelAPI_AttributeReference>(theAttribute);
1149
1150   std::shared_ptr<SketchPlugin_Feature> aTrimFeature =
1151                  std::dynamic_pointer_cast<SketchPlugin_Feature>(theAttribute->owner());
1152
1153   ObjectPtr aBaseObject = aBaseObjectAttr->value();
1154   if (!aBaseObject) {
1155     AttributePtr aPreviewAttr = aTrimFeature->attribute(SketchPlugin_Trim::PREVIEW_OBJECT());
1156     aBaseObjectAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(aPreviewAttr);
1157     aBaseObject = aBaseObjectAttr->value();
1158   }
1159
1160   FeaturePtr aBaseFeature = ModelAPI_Feature::feature(aBaseObject);
1161   if (!aBaseFeature)
1162     return aValid;
1163
1164   std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
1165                                  std::dynamic_pointer_cast<SketchPlugin_Feature>(aBaseFeature);
1166   if (!aSketchFeature.get() || aSketchFeature->isCopy())
1167     return aValid;
1168
1169   // B-splines are not supported by the Trim yet
1170   if (aBaseFeature->getKind() == SketchPlugin_BSpline::ID() ||
1171       aBaseFeature->getKind() == SketchPlugin_BSplinePeriodic::ID()) {
1172     theError = "Not supported";
1173     return false;
1174   }
1175
1176   // point on feature
1177   AttributePoint2DPtr aPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1178                        aTrimFeature->data()->attribute(SketchPlugin_Trim::PREVIEW_POINT()));
1179
1180   SketchPlugin_Sketch* aSketch = aTrimFeature->sketch();
1181
1182   std::shared_ptr<GeomAPI_Pnt2d> anAttributePnt2d = aPoint->pnt();
1183   std::shared_ptr<GeomAPI_Pnt> anAttributePnt = aSketch->to3D(anAttributePnt2d->x(),
1184                                                               anAttributePnt2d->y());
1185
1186   std::map<ObjectPtr, std::set<GeomShapePtr> > aCashedShapes;
1187   std::map<ObjectPtr, std::map<std::shared_ptr<GeomAPI_Pnt>,
1188            std::pair<std::list<std::shared_ptr<GeomDataAPI_Point2D> >,
1189                      std::list<std::shared_ptr<ModelAPI_Object> > > > > anObjectToPoints;
1190   SketchPlugin_SegmentationTools::fillObjectShapes(
1191       aTrimFeature.get(), aBaseObject, aCashedShapes, anObjectToPoints);
1192   const std::set<GeomShapePtr>& aShapes = aCashedShapes[aBaseObject];
1193
1194   return aShapes.size() > 1;
1195 }
1196
1197 bool SketchPlugin_ProjectionValidator::isValid(const AttributePtr& theAttribute,
1198                                                const std::list<std::string>& theArguments,
1199                                                Events_InfoMessage& theError) const
1200 {
1201   if (theAttribute->attributeType() != ModelAPI_AttributeSelection::typeId()) {
1202     theError = "The attribute with the %1 type is not processed";
1203     theError.arg(theAttribute->attributeType());
1204     return false;
1205   }
1206
1207   AttributeSelectionPtr aFeatureAttr =
1208       std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(theAttribute);
1209   std::shared_ptr<GeomAPI_Vertex> aVertex;
1210   std::shared_ptr<GeomAPI_Edge> anEdge;
1211   std::shared_ptr<SketchPlugin_Feature> aSketchFeature;
1212   if (aFeatureAttr.get()) {
1213     GeomShapePtr aVal = aFeatureAttr->value();
1214     ResultPtr aRes = aFeatureAttr->context();
1215     if (aVal && aVal->isVertex())
1216       aVertex = std::shared_ptr<GeomAPI_Vertex>(new GeomAPI_Vertex(aVal));
1217     else if (aVal && aVal->isEdge()) {
1218       anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(aVal));
1219     } else if(aRes && aRes->shape()) {
1220       if (aRes->shape()->isVertex())
1221         aVertex = std::shared_ptr<GeomAPI_Vertex>(new GeomAPI_Vertex(aRes->shape()));
1222       else if (aRes->shape()->isEdge())
1223         anEdge = std::shared_ptr<GeomAPI_Edge>(new GeomAPI_Edge(aRes->shape()));
1224     }
1225
1226     // try to convert result to sketch feature
1227     if (aRes) {
1228       aSketchFeature =
1229         std::dynamic_pointer_cast<SketchPlugin_Feature>(ModelAPI_Feature::feature(aRes));
1230     }
1231   }
1232   if (!aVertex && !anEdge) {
1233     theError = "The attribute %1 should be an edge or vertex";
1234     theError.arg(theAttribute->id());
1235     return false;
1236   }
1237
1238   // find a sketch
1239   std::shared_ptr<SketchPlugin_Sketch> aSketch;
1240   std::set<AttributePtr> aRefs = theAttribute->owner()->data()->refsToMe();
1241   std::set<AttributePtr>::const_iterator anIt = aRefs.begin();
1242   for (; anIt != aRefs.end(); ++anIt) {
1243     CompositeFeaturePtr aComp =
1244         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>((*anIt)->owner());
1245     if (aComp && aComp->getKind() == SketchPlugin_Sketch::ID()) {
1246       aSketch = std::dynamic_pointer_cast<SketchPlugin_Sketch>(aComp);
1247       break;
1248     }
1249   }
1250   if (!aSketch) {
1251     theError = "There is no sketch referring to the current feature";
1252     return false;
1253   }
1254   if (aSketchFeature && aSketch.get() == aSketchFeature->sketch()) {
1255     theError = "Unable to project feature from the same sketch";
1256     return false;
1257   }
1258
1259   std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
1260   std::shared_ptr<GeomAPI_Dir> aNormal = aPlane->direction();
1261   std::shared_ptr<GeomAPI_Pnt> anOrigin = aPlane->location();
1262
1263   bool aValid = true;
1264   if (aVertex)
1265     aValid = true; // vertex is always could be projected
1266   else if (anEdge->isLine()) {
1267     std::shared_ptr<GeomAPI_Lin> aLine = anEdge->line();
1268     std::shared_ptr<GeomAPI_Dir> aLineDir = aLine->direction();
1269     double aDot = fabs(aNormal->dot(aLineDir));
1270     aValid = fabs(aDot - 1.0) >= tolerance * tolerance;
1271     if (!aValid)
1272       theError = "Error: Line is orthogonal to the sketch plane.";
1273   }
1274   else if (anEdge->isCircle() || anEdge->isArc()) {
1275     std::shared_ptr<GeomAPI_Circ> aCircle = anEdge->circle();
1276     std::shared_ptr<GeomAPI_Dir> aCircNormal = aCircle->normal();
1277     double aDot = fabs(aNormal->dot(aCircNormal));
1278     aValid = aDot >= tolerance * tolerance;
1279     if (!aValid)
1280       theError.arg(anEdge->isCircle() ? "Error: Circle is orthogonal to the sketch plane."
1281                                       : "Error: Arc is orthogonal to the sketch plane.");
1282   }
1283   else if (anEdge->isEllipse()) {
1284     std::shared_ptr<GeomAPI_Ellipse> anEllipse = anEdge->ellipse();
1285     std::shared_ptr<GeomAPI_Dir> anEllipseNormal = anEllipse->normal();
1286     double aDot = fabs(aNormal->dot(anEllipseNormal));
1287     aValid = aDot >= tolerance * tolerance;
1288     if (!aValid)
1289       theError.arg(anEdge->isClosed() ? "Error: Ellipse is orthogonal to the sketch plane."
1290                                       : "Error: Elliptic Arc is orthogonal to the sketch plane.");
1291   }
1292   else if (anEdge->isBSpline()) {
1293     // check B-spline is periodic and planar
1294     std::shared_ptr<GeomAPI_Curve> aCurve(new GeomAPI_Curve(anEdge));
1295     std::shared_ptr<GeomAPI_BSpline> aBSpline(new GeomAPI_BSpline(aCurve));
1296     if (aBSpline->isPeriodic()) {
1297       GeomPlanePtr aBSplinePlane = GeomAlgoAPI_ShapeTools::findPlane(ListOfShape(1, anEdge));
1298       if (aBSplinePlane) {
1299         std::shared_ptr<GeomAPI_Dir> aBSplineNormal = aBSplinePlane->direction();
1300         double aDot = fabs(aNormal->dot(aBSplineNormal));
1301         aValid = aDot > tolerance * tolerance;
1302         if (!aValid) {
1303           // B-spline's plane is orthogonal to the sketch plane,
1304           // thus, need to check whether B-spline is planar.
1305           std::list<GeomPointPtr> aPoles = aBSpline->poles();
1306           for (std::list<GeomPointPtr>::iterator it = aPoles.begin();
1307             it != aPoles.end() && !aValid; ++it) {
1308             if (aBSplinePlane->distance(*it) > tolerance)
1309               aValid = true; // non-planar B-spline curve
1310           }
1311           if (!aValid)
1312             theError = "Error: Periodic B-spline is orthogonal to the sketch plane.";
1313         }
1314       }
1315     }
1316   }
1317
1318   return aValid;
1319 }
1320
1321
1322 static std::set<FeaturePtr> common(const std::set<FeaturePtr>& theSet1,
1323                                    const std::set<FeaturePtr>& theSet2)
1324 {
1325   std::set<FeaturePtr> aCommon;
1326   if (theSet1.empty() || theSet2.empty())
1327     return aCommon;
1328
1329   std::set<FeaturePtr>::const_iterator anIt2 = theSet2.begin();
1330   for (; anIt2 != theSet2.end(); ++anIt2)
1331     if (theSet1.find(*anIt2) != theSet1.end())
1332       aCommon.insert(*anIt2);
1333   return aCommon;
1334 }
1335
1336 bool SketchPlugin_DifferentReferenceValidator::isValid(
1337     const AttributePtr& theAttribute,
1338     const std::list<std::string>& theArguments,
1339     Events_InfoMessage& theError) const
1340 {
1341   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner());
1342
1343   int aNbFeaturesReferred = 0;
1344   int aNbAttributesReferred = 0;
1345   std::set<FeaturePtr> aCommonReferredFeatures;
1346
1347   // find all features referred by attributes listed in theArguments
1348   std::list<std::string>::const_iterator anArgIt = theArguments.begin();
1349   for (; anArgIt != theArguments.end(); ++anArgIt) {
1350     AttributeRefAttrPtr aRefAttr = anOwner->refattr(*anArgIt);
1351     if (!aRefAttr)
1352       continue;
1353
1354     std::set<FeaturePtr> aCoincidentFeatures;
1355     if (aRefAttr->isObject()) {
1356       FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object());
1357       if (aFeature) {
1358         aCoincidentFeatures.insert(aFeature);
1359         aNbFeaturesReferred += 1;
1360       }
1361     } else {
1362       AttributePoint2DPtr aPoint =
1363           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aRefAttr->attr());
1364       if (aPoint) {
1365         aCoincidentFeatures = SketchPlugin_Tools::findFeaturesCoincidentToPoint(aPoint);
1366         aNbAttributesReferred += 1;
1367       }
1368     }
1369
1370     if (aCommonReferredFeatures.empty())
1371       aCommonReferredFeatures = aCoincidentFeatures;
1372     else
1373       aCommonReferredFeatures = common(aCommonReferredFeatures, aCoincidentFeatures);
1374
1375     if (aCommonReferredFeatures.empty())
1376       return true;
1377   }
1378
1379   bool isOk = aNbFeaturesReferred < 1;
1380   if (aNbFeaturesReferred == 1) {
1381     if (aCommonReferredFeatures.size() == 1) {
1382       FeaturePtr aFeature = *aCommonReferredFeatures.begin();
1383       isOk = aNbAttributesReferred <= 1 ||
1384              aFeature->getKind() == SketchPlugin_Circle::ID() ||
1385              aFeature->getKind() == SketchPlugin_Arc::ID();
1386     } else
1387       isOk = false;
1388   }
1389
1390   if (!isOk)
1391     theError = "Attributes are referred to the same feature";
1392   return isOk;
1393 }
1394
1395 bool SketchPlugin_DifferentPointReferenceValidator::isValid(
1396     const AttributePtr& theAttribute,
1397     const std::list<std::string>& theArguments,
1398     Events_InfoMessage& theError) const
1399 {
1400   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner());
1401   std::set<AttributePoint2DPtr> aReferredCoincidentPoints;
1402
1403   // find all points referred by attributes listed in theArguments
1404   bool hasRefsToPoints = false;
1405   std::list<std::string>::const_iterator anArgIt = theArguments.begin();
1406   for (; anArgIt != theArguments.end(); ++anArgIt) {
1407     AttributeRefAttrPtr aRefAttr = anOwner->refattr(*anArgIt);
1408     if (!aRefAttr)
1409       continue;
1410
1411     if (!aRefAttr->isObject()) {
1412       AttributePoint2DPtr aPoint =
1413           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aRefAttr->attr());
1414       if (aReferredCoincidentPoints.empty())
1415         aReferredCoincidentPoints = SketchPlugin_Tools::findPointsCoincidentToPoint(aPoint);
1416       else if (aReferredCoincidentPoints.find(aPoint) == aReferredCoincidentPoints.end())
1417         return true; // non-coincident point has been found
1418       else
1419         hasRefsToPoints = true;
1420     }
1421   }
1422
1423   if (hasRefsToPoints)
1424     theError = "Attributes are referred to the same point";
1425   return !hasRefsToPoints;
1426 }
1427
1428 bool SketchPlugin_CirclePassedPointValidator::isValid(
1429     const AttributePtr& theAttribute,
1430     const std::list<std::string>&,
1431     Events_InfoMessage& theError) const
1432 {
1433   static const std::string aErrorMessage(
1434       "Passed point refers to the same feature as a center point");
1435
1436   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner());
1437
1438   AttributeRefAttrPtr aCenterRef =
1439       anOwner->refattr(SketchPlugin_MacroCircle::CENTER_POINT_REF_ID());
1440   AttributeRefAttrPtr aPassedRef =
1441       anOwner->refattr(SketchPlugin_MacroCircle::PASSED_POINT_REF_ID());
1442
1443   if (!aPassedRef->isObject())
1444     return true;
1445
1446   FeaturePtr aPassedFeature = ModelAPI_Feature::feature(aPassedRef->object());
1447   if (!aPassedFeature)
1448     return true;
1449
1450   if (aCenterRef->isObject()) {
1451     if (aCenterRef->object() == aPassedRef->object()) {
1452       theError = aErrorMessage;
1453       return false;
1454     }
1455   } else {
1456     AttributePoint2DPtr aCenterPoint =
1457         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aCenterRef->attr());
1458     if (aCenterPoint) {
1459       std::set<FeaturePtr> aCoincidentFeatures =
1460           SketchPlugin_Tools::findFeaturesCoincidentToPoint(aCenterPoint);
1461       // check one of coincident features is a feature referred by passed point
1462       std::set<FeaturePtr>::const_iterator anIt = aCoincidentFeatures.begin();
1463       for(; anIt != aCoincidentFeatures.end(); ++anIt)
1464         if (*anIt == aPassedFeature) {
1465           theError = aErrorMessage;
1466           return false;
1467         }
1468     }
1469   }
1470   return true;
1471 }
1472
1473 bool SketchPlugin_ThirdPointValidator::isValid(
1474     const AttributePtr& theAttribute,
1475     const std::list<std::string>& theArguments,
1476     Events_InfoMessage& theError) const
1477 {
1478   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner());
1479   return arePointsNotOnLine(anOwner, theError) &&
1480          arePointsNotSeparated(anOwner, theArguments, theError);
1481 }
1482
1483 static std::shared_ptr<GeomAPI_Pnt2d> toPoint(const FeaturePtr& theMacroCircle,
1484                                               const std::string& thePointAttrName,
1485                                               const std::string& theRefPointAttrName)
1486 {
1487   AttributePoint2DPtr aPointAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1488       theMacroCircle->attribute(thePointAttrName));
1489   AttributeRefAttrPtr aRefAttr = theMacroCircle->refattr(theRefPointAttrName);
1490
1491   std::shared_ptr<GeomAPI_Pnt2d> aPoint = aPointAttr->pnt();
1492   if (aRefAttr) {
1493     if (aRefAttr->isObject()) {
1494       // project a point onto selected feature
1495       std::shared_ptr<SketchPlugin_Feature> aFeature =
1496           std::dynamic_pointer_cast<SketchPlugin_Feature>(
1497           ModelAPI_Feature::feature(aRefAttr->object()));
1498       if (aFeature) {
1499         SketchPlugin_Sketch* aSketch = aFeature->sketch();
1500         std::shared_ptr<GeomAPI_Edge> anEdge =
1501             std::dynamic_pointer_cast<GeomAPI_Edge>(aFeature->lastResult()->shape());
1502         if (anEdge) {
1503           std::shared_ptr<GeomAPI_Pnt> aPoint3D = aSketch->to3D(aPoint->x(), aPoint->y());
1504           if (anEdge->isLine())
1505             aPoint3D = anEdge->line()->project(aPoint3D);
1506           else if (anEdge->isCircle())
1507             aPoint3D = anEdge->circle()->project(aPoint3D);
1508           if(aPoint3D)
1509             aPoint = aSketch->to2D(aPoint3D);
1510         }
1511       }
1512     } else {
1513       AttributePoint2DPtr anOtherPoint =
1514           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aRefAttr->attr());
1515       if (anOtherPoint)
1516         aPoint = anOtherPoint->pnt(); // the reference point is much more precise, use it
1517     }
1518   }
1519
1520   return aPoint;
1521 }
1522
1523 static void threePointsOfFeature(const FeaturePtr& theMacroFeature,
1524                                  std::shared_ptr<GeomAPI_Pnt2d> thePoints[3])
1525 {
1526   if (theMacroFeature->getKind() == SketchPlugin_MacroCircle::ID()) {
1527     thePoints[0] = toPoint(theMacroFeature,
1528           SketchPlugin_MacroCircle::FIRST_POINT_ID(),
1529           SketchPlugin_MacroCircle::FIRST_POINT_REF_ID());
1530     thePoints[1] = toPoint(theMacroFeature,
1531           SketchPlugin_MacroCircle::SECOND_POINT_ID(),
1532           SketchPlugin_MacroCircle::SECOND_POINT_REF_ID());
1533     thePoints[2] = toPoint(theMacroFeature,
1534           SketchPlugin_MacroCircle::THIRD_POINT_ID(),
1535           SketchPlugin_MacroCircle::THIRD_POINT_REF_ID());
1536   } else if (theMacroFeature->getKind() == SketchPlugin_MacroArc::ID()) {
1537     thePoints[0] = toPoint(theMacroFeature,
1538           SketchPlugin_MacroArc::START_POINT_2_ID(),
1539           SketchPlugin_MacroArc::START_POINT_REF_ID());
1540     thePoints[1] = toPoint(theMacroFeature,
1541           SketchPlugin_MacroArc::END_POINT_2_ID(),
1542           SketchPlugin_MacroArc::END_POINT_REF_ID());
1543     thePoints[2] = toPoint(theMacroFeature,
1544           SketchPlugin_MacroArc::PASSED_POINT_ID(),
1545           SketchPlugin_MacroArc::PASSED_POINT_REF_ID());
1546   }
1547 }
1548
1549 static bool isPointsOnLine(const std::shared_ptr<GeomAPI_Pnt2d>& thePoint1,
1550                            const std::shared_ptr<GeomAPI_Pnt2d>& thePoint2,
1551                            const std::shared_ptr<GeomAPI_Pnt2d>& thePoint3)
1552 {
1553   static const double aTolerance = 1.e-7;
1554   if (thePoint1->distance(thePoint2) < aTolerance ||
1555       thePoint1->distance(thePoint3) < aTolerance)
1556     return true;
1557
1558   std::shared_ptr<GeomAPI_Dir2d> aDirP1P2(new GeomAPI_Dir2d(thePoint2->x() - thePoint1->x(),
1559                                                             thePoint2->y() - thePoint1->y()));
1560   std::shared_ptr<GeomAPI_Dir2d> aDirP1P3(new GeomAPI_Dir2d(thePoint3->x() - thePoint1->x(),
1561                                                             thePoint3->y() - thePoint1->y()));
1562   return fabs(aDirP1P2->cross(aDirP1P3)) < aTolerance;
1563 }
1564
1565 static bool isOnSameSide(const std::shared_ptr<GeomAPI_Lin>& theLine,
1566                          const std::shared_ptr<GeomAPI_Pnt>& thePoint1,
1567                          const std::shared_ptr<GeomAPI_Pnt>& thePoint2)
1568 {
1569   static const double aTolerance = 1.e-7;
1570   std::shared_ptr<GeomAPI_Dir> aLineDir = theLine->direction();
1571   std::shared_ptr<GeomAPI_XYZ> aLineLoc = theLine->location()->xyz();
1572
1573   std::shared_ptr<GeomAPI_XYZ> aVec1 = thePoint1->xyz()->decreased(aLineLoc);
1574   // the first point is on the line
1575   if (aVec1->squareModulus() < aTolerance * aTolerance)
1576     return false;
1577   std::shared_ptr<GeomAPI_Dir> aDirP1L(new GeomAPI_Dir(aVec1));
1578   std::shared_ptr<GeomAPI_XYZ> aVec2 = thePoint2->xyz()->decreased(aLineLoc);
1579   // the second point is on the line
1580   if (aVec2->squareModulus() < aTolerance * aTolerance)
1581     return false;
1582   std::shared_ptr<GeomAPI_Dir> aDirP2L(new GeomAPI_Dir(aVec2));
1583
1584   return aLineDir->cross(aDirP1L)->dot(aLineDir->cross(aDirP2L)) > -aTolerance;
1585 }
1586
1587 static bool isOnSameSide(const std::shared_ptr<GeomAPI_Circ>& theCircle,
1588                          const std::shared_ptr<GeomAPI_Pnt>&  thePoint1,
1589                          const std::shared_ptr<GeomAPI_Pnt>&  thePoint2)
1590 {
1591   static const double aTolerance = 1.e-7;
1592   std::shared_ptr<GeomAPI_Pnt> aCenter = theCircle->center();
1593   double aDistP1C = thePoint1->distance(aCenter);
1594   double aDistP2C = thePoint2->distance(aCenter);
1595   return (aDistP1C - theCircle->radius()) * (aDistP2C - theCircle->radius()) > -aTolerance;
1596 }
1597
1598 bool SketchPlugin_ThirdPointValidator::arePointsNotOnLine(
1599     const FeaturePtr& theMacroFeature,
1600     Events_InfoMessage& theError) const
1601 {
1602   static const std::string aErrorPointsOnLine(
1603       "Selected points are on the same line");
1604
1605   std::shared_ptr<GeomAPI_Pnt2d> aPoints[3];
1606   threePointsOfFeature(theMacroFeature, aPoints);
1607
1608   if (isPointsOnLine(aPoints[0], aPoints[1], aPoints[2])) {
1609     theError = aErrorPointsOnLine;
1610     return false;
1611   }
1612   return true;
1613 }
1614
1615 bool SketchPlugin_ThirdPointValidator::arePointsNotSeparated(
1616     const FeaturePtr& theMacroFeature,
1617     const std::list<std::string>& theArguments,
1618     Events_InfoMessage& theError) const
1619 {
1620   static const std::string aErrorPointsApart(
1621       "Selected entity is lying between first two points");
1622
1623   AttributeRefAttrPtr aThirdPointRef = theMacroFeature->refattr(theArguments.front());
1624   FeaturePtr aRefByThird;
1625   if (aThirdPointRef->isObject())
1626     aRefByThird = ModelAPI_Feature::feature(aThirdPointRef->object());
1627   if (!aRefByThird)
1628     return true;
1629
1630   std::shared_ptr<GeomAPI_Pnt2d> aPoints[3];
1631   threePointsOfFeature(theMacroFeature, aPoints);
1632
1633   std::shared_ptr<GeomAPI_Edge> aThirdShape =
1634       std::dynamic_pointer_cast<GeomAPI_Edge>(aRefByThird->lastResult()->shape());
1635   if (!aThirdShape)
1636     return true;
1637
1638   SketchPlugin_Sketch* aSketch =
1639       std::dynamic_pointer_cast<SketchPlugin_Feature>(theMacroFeature)->sketch();
1640   std::shared_ptr<GeomAPI_Pnt> aFirstPnt3D = aSketch->to3D(aPoints[0]->x(), aPoints[0]->y());
1641   std::shared_ptr<GeomAPI_Pnt> aSecondPnt3D = aSketch->to3D(aPoints[1]->x(), aPoints[1]->y());
1642
1643   bool isOk = true;
1644   if (aThirdShape->isLine())
1645     isOk = isOnSameSide(aThirdShape->line(), aFirstPnt3D, aSecondPnt3D);
1646   else if (aThirdShape->isCircle() || aThirdShape->isArc())
1647     isOk = isOnSameSide(aThirdShape->circle(), aFirstPnt3D, aSecondPnt3D);
1648
1649   if (!isOk)
1650     theError = aErrorPointsApart;
1651   return isOk;
1652 }
1653
1654 bool SketchPlugin_ArcEndPointValidator::isValid(
1655     const AttributePtr& theAttribute,
1656     const std::list<std::string>& theArguments,
1657     Events_InfoMessage& theError) const
1658 {
1659   FeaturePtr aFeature = ModelAPI_Feature::feature(theAttribute->owner());
1660   AttributeRefAttrPtr anEndPointRef = aFeature->refattr(theArguments.front());
1661
1662   if(!anEndPointRef.get()) {
1663     return true;
1664   }
1665
1666   ObjectPtr anObject = anEndPointRef->object();
1667   AttributePtr anAttr = anEndPointRef->attr();
1668   if(!anObject.get() && !anAttr.get()) {
1669     return true;
1670   }
1671
1672   if(anEndPointRef->attr().get()) {
1673     return false;
1674   }
1675
1676   ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(anObject);
1677   if(aResult.get()) {
1678     GeomShapePtr aShape = aResult->shape();
1679     if(aShape.get() && aShape->isVertex()) {
1680       return false;
1681     }
1682   }
1683
1684   aFeature = ModelAPI_Feature::feature(anObject);
1685   if(aFeature.get()) {
1686     if(aFeature->getKind() == SketchPlugin_Point::ID()) {
1687       return false;
1688     }
1689   }
1690
1691   return true;
1692 }
1693
1694 static GeomShapePtr toInfiniteEdge(const GeomShapePtr theShape)
1695 {
1696   if(!theShape.get()) {
1697     return theShape;
1698   }
1699
1700   if(!theShape->isEdge()) {
1701     return theShape;
1702   }
1703
1704   std::shared_ptr<GeomAPI_Edge> anEdge(new GeomAPI_Edge(theShape));
1705
1706   if(!anEdge.get()) {
1707     return theShape;
1708   }
1709
1710   if(anEdge->isLine()) {
1711     std::shared_ptr<GeomAPI_Lin> aLine = anEdge->line();
1712     GeomShapePtr aShape = GeomAlgoAPI_EdgeBuilder::line(aLine);
1713     return aShape;
1714   }
1715
1716   if(anEdge->isCircle() || anEdge->isArc()) {
1717     std::shared_ptr<GeomAPI_Circ> aCircle = anEdge->circle();
1718     GeomShapePtr aShape = GeomAlgoAPI_EdgeBuilder::lineCircle(aCircle);
1719     return aShape;
1720   }
1721
1722   return theShape;
1723 }
1724
1725 bool SketchPlugin_ArcEndPointIntersectionValidator::isValid(
1726     const AttributePtr& theAttribute,
1727     const std::list<std::string>& theArguments,
1728     Events_InfoMessage& theError) const
1729 {
1730   std::shared_ptr<SketchPlugin_MacroArc> anArcFeature =
1731       std::dynamic_pointer_cast<SketchPlugin_MacroArc>(theAttribute->owner());
1732   AttributeRefAttrPtr anEndPointRef = anArcFeature->refattr(theArguments.front());
1733
1734   if(!anEndPointRef.get()) {
1735     return true;
1736   }
1737
1738   GeomShapePtr anArcShape = toInfiniteEdge(anArcFeature->getArcShape(false));
1739
1740   if(!anArcShape.get() || anArcShape->isNull()) {
1741     return true;
1742   }
1743
1744   ObjectPtr anObject = anEndPointRef->object();
1745   AttributePtr anAttr = anEndPointRef->attr();
1746   if(!anObject.get() && !anAttr.get()) {
1747     return true;
1748   }
1749
1750   ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(anObject);
1751   if(aResult.get()) {
1752     GeomShapePtr aShape = aResult->shape();
1753     if (!aShape->isEdge())
1754       return true;
1755     aShape = toInfiniteEdge(aShape);
1756     if(aShape.get() && !aShape->isNull()) {
1757       if(anArcShape->isIntersect(aShape)) {
1758         return true;
1759       }
1760     }
1761   }
1762
1763   FeaturePtr aSelectedFeature = ModelAPI_Feature::feature(anObject);
1764   if(aSelectedFeature.get()) {
1765     std::list<ResultPtr> aResults = aSelectedFeature->results();
1766     for(std::list<ResultPtr>::const_iterator anIt = aResults.cbegin();
1767         anIt != aResults.cend();
1768         ++anIt)
1769     {
1770       GeomShapePtr aShape = (*anIt)->shape();
1771       if (!aShape->isEdge())
1772         return true;
1773       aShape = toInfiniteEdge(aShape);
1774       if(aShape.get() && !aShape->isNull()) {
1775         if(anArcShape->isIntersect(aShape)) {
1776           return true;
1777         }
1778       }
1779     }
1780   }
1781
1782   return false;
1783 }
1784
1785 bool SketchPlugin_HasNoConstraint::isValid(const AttributePtr& theAttribute,
1786                                            const std::list<std::string>& theArguments,
1787                                            Events_InfoMessage& theError) const
1788 {
1789   std::set<std::string> aFeatureKinds;
1790   for (std::list<std::string>::const_iterator anArgIt = theArguments.begin();
1791        anArgIt != theArguments.end(); anArgIt++) {
1792     aFeatureKinds.insert(*anArgIt);
1793   }
1794
1795   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
1796     theError = "The attribute with the %1 type is not processed";
1797     theError.arg(theAttribute->attributeType());
1798     return false;
1799   }
1800
1801   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>
1802                                                                       (theAttribute);
1803   bool isObject = aRefAttr->isObject();
1804   if (!isObject) {
1805     theError = "It uses an empty object";
1806     return false;
1807   }
1808   ObjectPtr anObject = aRefAttr->object();
1809   FeaturePtr aFeature = ModelAPI_Feature::feature(anObject);
1810   if (!aFeature.get()) {
1811     theError = "The feature of the checked attribute is empty";
1812     return false;
1813   }
1814
1815   FeaturePtr aCurrentFeature = ModelAPI_Feature::feature(aRefAttr->owner());
1816
1817   std::set<AttributePtr> aRefsList = anObject->data()->refsToMe();
1818   std::set<AttributePtr>::const_iterator anIt = aRefsList.begin();
1819   for (; anIt != aRefsList.end(); anIt++) {
1820     FeaturePtr aRefFeature = ModelAPI_Feature::feature((*anIt)->owner());
1821     if (aRefFeature.get() && aCurrentFeature != aRefFeature &&
1822         aFeatureKinds.find(aRefFeature->getKind()) != aFeatureKinds.end())
1823       return false; // constraint is found, that means that the check is not valid
1824   }
1825   return true;
1826 }
1827
1828 bool SketchPlugin_ReplicationReferenceValidator::isValid(
1829     const AttributePtr& theAttribute,
1830     const std::list<std::string>& theArguments,
1831     Events_InfoMessage& theError) const
1832 {
1833   AttributeRefAttrPtr aRefAttr =
1834       std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
1835   if (!aRefAttr)
1836   {
1837     theError = "Incorrect attribute";
1838     return false;
1839   }
1840
1841   ObjectPtr anOwner;
1842   if (aRefAttr->isObject())
1843     anOwner = aRefAttr->object();
1844   else
1845   {
1846     AttributePtr anAttr = aRefAttr->attr();
1847     anOwner = anAttr->owner();
1848   }
1849   FeaturePtr anAttrOwnerFeature = ModelAPI_Feature::feature(anOwner);
1850   if (!anAttrOwnerFeature)
1851     return true;
1852   AttributeBooleanPtr aCopyAttr = anAttrOwnerFeature->boolean(SketchPlugin_SketchEntity::COPY_ID());
1853   if (!aCopyAttr || !aCopyAttr->value())
1854     return true; // feature is not a copy, thus valid
1855
1856   FeaturePtr aMultiFeature = ModelAPI_Feature::feature(theAttribute->owner());
1857   // Collect original entities
1858   std::set<FeaturePtr> anOriginalFeatures;
1859   if (theArguments.size() > 1) {
1860     AttributeRefListPtr anOrigList = aMultiFeature->reflist(theArguments.back());
1861     for (int i = 0; i < anOrigList->size(); ++i)
1862     {
1863       FeaturePtr aFeature = ModelAPI_Feature::feature(anOrigList->object(i));
1864       if (aFeature == anAttrOwnerFeature)
1865         return true;
1866     }
1867   }
1868
1869   // check the copy feature is already referred by the "Multi" feature
1870   AttributeRefListPtr aRefList = aMultiFeature->reflist(theArguments.front());
1871   for (int i = 0; i < aRefList->size(); ++i)
1872   {
1873     FeaturePtr aRefOwner = ModelAPI_Feature::feature(aRefList->object(i));
1874     if (aRefOwner == anAttrOwnerFeature)
1875     {
1876       theError = "Attribute refers to the object generated by this feature";
1877       return false;
1878     }
1879   }
1880
1881   return true;
1882 }
1883
1884 bool SketchPlugin_SketchFeatureValidator::isValid(const AttributePtr& theAttribute,
1885                                                   const std::list<std::string>& theArguments,
1886                                                   Events_InfoMessage& theError) const
1887 {
1888   if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId() &&
1889       theAttribute->attributeType() != ModelAPI_AttributeReference::typeId()) {
1890     theError = "The attribute with the %1 type is not processed";
1891     theError.arg(theAttribute->attributeType());
1892     return false;
1893   }
1894
1895   // check the attribute refers to a sketch feature
1896   bool isSketchFeature = false;
1897   AttributeRefAttrPtr aRefAttr =
1898       std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
1899   if (aRefAttr) {
1900     isSketchFeature = aRefAttr->isObject();
1901     if (isSketchFeature) {
1902       FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object());
1903       isSketchFeature = aFeature.get() != NULL;
1904       if (isSketchFeature) {
1905         std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
1906             std::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
1907         isSketchFeature = aSketchFeature.get() != NULL;
1908       }
1909     }
1910   }
1911   else {
1912     AttributeReferencePtr aReference =
1913       std::dynamic_pointer_cast<ModelAPI_AttributeReference>(theAttribute);
1914     if (aReference) {
1915       FeaturePtr aFeature = ModelAPI_Feature::feature(aReference->value());
1916       isSketchFeature = aFeature.get() && aFeature->getKind() == SketchPlugin_Sketch::ID();
1917     }
1918   }
1919
1920   if (!isSketchFeature)
1921     theError = "The object selected is not a sketch feature";
1922   return isSketchFeature;
1923 }
1924
1925 bool SketchPlugin_MultiRotationAngleValidator::isValid(const AttributePtr& theAttribute,
1926                                                        const std::list<std::string>& theArguments,
1927                                                        Events_InfoMessage& theError) const
1928 {
1929   if (theAttribute->attributeType() != ModelAPI_AttributeDouble::typeId()) {
1930     theError = "The attribute with the %1 type is not processed";
1931     theError.arg(theAttribute->attributeType());
1932     return false;
1933   }
1934
1935   AttributeDoublePtr anAngleAttr =
1936     std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
1937
1938   FeaturePtr aMultiRotation = ModelAPI_Feature::feature(theAttribute->owner());
1939   AttributeStringPtr anAngleType =
1940       aMultiRotation->string(SketchPlugin_MultiRotation::ANGLE_TYPE());
1941   AttributeIntegerPtr aNbCopies =
1942       aMultiRotation->integer(SketchPlugin_MultiRotation::NUMBER_OF_OBJECTS_ID());
1943
1944   if (anAngleType->value() != "FullAngle")
1945   {
1946     double aFullAngleValue = anAngleAttr->value() * (aNbCopies->value() - 1);
1947     if (aFullAngleValue < -1.e-7 || aFullAngleValue > 359.9999999)
1948     {
1949       theError = "Rotation single angle should produce full angle less than 360 degree";
1950       return false;
1951     }
1952   }
1953   else
1954   {
1955     double aFullAngleValue = anAngleAttr->value();
1956     if (aFullAngleValue < -1.e-7 || aFullAngleValue > 360.0000001)
1957     {
1958       theError = "Rotation full angle should be in range [0, 360]";
1959       return false;
1960     }
1961   }
1962
1963   return true;
1964 }
1965
1966 bool SketchPlugin_BSplineValidator::isValid(const AttributePtr& theAttribute,
1967                                             const std::list<std::string>& theArguments,
1968                                             Events_InfoMessage& theError) const
1969 {
1970   AttributePoint2DArrayPtr aPolesArray =
1971       std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(theAttribute);
1972   if (!aPolesArray)
1973     return false;
1974
1975   if (aPolesArray->size() < 2) {
1976     theError = "Number of B-spline poles should be 2 or more";
1977     return false;
1978   }
1979
1980   return true;
1981 }
1982
1983 bool SketchPlugin_CurveFittingValidator::isValid(const FeaturePtr& theFeature,
1984                                                  const std::list<std::string>& theArguments,
1985                                                  Events_InfoMessage& theError) const
1986 {
1987   AttributeRefAttrListPtr aRefAttrList =
1988       theFeature->refattrlist(SketchPlugin_CurveFitting::POINTS_ID());
1989   AttributeBooleanPtr aPeriodicAttr =
1990       theFeature->boolean(SketchPlugin_CurveFitting::PERIODIC_ID());
1991
1992   // check number of selected entities
1993   int aMinNbPoints = aPeriodicAttr->value() ? 3 : 2;
1994   if (aRefAttrList->size() < aMinNbPoints) {
1995     theError = "Not enough points selected. Need at least %1 points.";
1996     theError.arg(aMinNbPoints);
1997     return false;
1998   }
1999   return true;
2000 }