Salome HOME
Copyright update 2021
[modules/shaper.git] / src / BuildPlugin / BuildPlugin_Validators.cpp
1 // Copyright (C) 2014-2021  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 "BuildPlugin_Validators.h"
21
22 #include <ModelAPI_AttributeSelectionList.h>
23 #include <ModelAPI_AttributeString.h>
24 #include <ModelAPI_ResultConstruction.h>
25
26 #include <GeomAPI_PlanarEdges.h>
27 #include <GeomAPI_Pln.h>
28 #include <GeomAPI_ShapeExplorer.h>
29 #include <GeomAPI_ShapeIterator.h>
30
31 #include <GeomAlgoAPI_CompoundBuilder.h>
32 #include <GeomAlgoAPI_PaveFiller.h>
33 #include <GeomAlgoAPI_ShapeBuilder.h>
34 #include <GeomAlgoAPI_ShapeTools.h>
35 #include <GeomAlgoAPI_SketchBuilder.h>
36 #include <GeomAlgoAPI_WireBuilder.h>
37 #include <GeomAlgoAPI_MakeVolume.h>
38 #include <GeomAlgoAPI_Tools.h>
39
40 #include <GeomValidators_FeatureKind.h>
41 #include <GeomValidators_ShapeType.h>
42
43 #include <BuildPlugin_Interpolation.h>
44
45 #include <SketchPlugin_Sketch.h>
46
47 #include <Events_InfoMessage.h>
48
49 //=================================================================================================
50 bool BuildPlugin_ValidatorBaseForBuild::isValid(const AttributePtr& theAttribute,
51                                                 const std::list<std::string>& theArguments,
52                                                 Events_InfoMessage& theError) const
53 {
54   // Get base objects list.
55   if(theAttribute->attributeType() != ModelAPI_AttributeSelectionList::typeId()) {
56     std::string aMsg = "Error: BuildPlugin_ValidatorBaseForBuild does "
57                        "not support attribute type '%1'\nOnly '%2' is supported.";
58     Events_InfoMessage("BuildPlugin_Validators", aMsg).
59       arg(theAttribute->attributeType()).arg(ModelAPI_AttributeSelectionList::typeId()).send();
60     return false;
61   }
62   AttributeSelectionListPtr aSelectionList =
63     std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(theAttribute);
64   if(!aSelectionList.get()) {
65     theError = "Could not get selection list.";
66     return false;
67   }
68   if(aSelectionList->size() == 0) {
69     theError = "Empty selection list.";
70     return false;
71   }
72
73   // Collect base shapes.
74   for(int anIndex = 0; anIndex < aSelectionList->size(); ++anIndex) {
75     AttributeSelectionPtr aSelection = aSelectionList->value(anIndex);
76     if(!aSelection.get()) {
77       theError = "Could not get selection.";
78       return false;
79     }
80     ResultPtr aContext = aSelection->context();
81     if(!aContext.get()) {
82       theError = "Attribute have empty context.";
83       return false;
84     }
85
86     GeomShapePtr aShape = aSelection->value();
87     GeomShapePtr aContextShape = aContext->shape();
88     if(!aShape.get()) {
89       aShape = aContextShape;
90     }
91     if(!aShape.get()) {
92       theError = "Empty shape selected.";
93       return false;
94     }
95
96     // Check that shapes has acceptable type.
97     GeomValidators_ShapeType aValidatorShapeType;
98     if(!aValidatorShapeType.isValid(aSelection, theArguments, theError)) {
99       return false;
100     }
101
102     // Check that it is shape on sketch.
103     ResultConstructionPtr aConstruction =
104       std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aContext);
105     if(aConstruction.get()) {
106       if(aConstruction->isInfinite()) {
107         theError = "Infinite objects not acceptable.";
108         return false;
109       }
110     }
111   }
112
113   return true;
114 }
115
116 //=================================================================================================
117 bool BuildPlugin_ValidatorBaseForWire::isValid(const std::shared_ptr<ModelAPI_Feature>& theFeature,
118                                                const std::list<std::string>& theArguments,
119                                                Events_InfoMessage& theError) const
120 {
121   // Get attribute.
122   AttributeSelectionListPtr aSelectionList = theFeature->selectionList(theArguments.front());
123   if(!aSelectionList.get()) {
124     theError = "Empty attribute \"%1\".";
125     theError.arg(theArguments.front());
126     return false;
127   }
128
129   GeomAPI_Shape::ShapeType aShapeType = GeomAPI_Shape::shapeTypeByStr(theArguments.back());
130
131   // Collect base shapes.
132   ListOfShape aListOfShapes;
133   for(int anIndex = 0; anIndex < aSelectionList->size(); ++anIndex) {
134     AttributeSelectionPtr aSelection = aSelectionList->value(anIndex);
135     GeomShapePtr aShape = aSelection->value();
136     ResultPtr aContext = aSelection->context();
137     if (!aShape.get() && aContext.get())
138       aShape = aContext->shape();
139
140     bool isProper = aShape.get() &&
141         (aShape->shapeType() == GeomAPI_Shape::EDGE || aShape->shapeType() == aShapeType);
142
143     if (isProper)
144       aListOfShapes.push_back(aShape);
145     else {
146       // is it a sketch?
147       FeaturePtr aFeature = aSelection->contextFeature();
148       if (!aFeature.get()) {
149         GeomShapePtr aValue = aSelection->value();
150         // whole sketch is allowed only
151         if (aContext.get() && !aValue.get()) {
152           aFeature = ModelAPI_Feature::feature(aContext);
153         }
154       }
155
156       if (!aFeature.get()) {
157         theError = "Error: Incorrect selection.";
158         return false;
159       }
160
161       if (aFeature->getKind() != SketchPlugin_Sketch::ID()) {
162         theError = "Error: %1 shape is not allowed for selection.";
163         theError.arg(aFeature->getKind());
164         return false;
165       }
166     }
167   }
168
169   if (aShapeType == GeomAPI_Shape::WIRE) {
170     // Create wire.
171     GeomShapePtr aWire = GeomAlgoAPI_WireBuilder::wire(aListOfShapes);
172     if (!aWire.get() && !aListOfShapes.empty()) {
173       theError = "Result wire empty. Probably it has disconnected edges or non-manifold.";
174       return false;
175     }
176   }
177
178   return true;
179 }
180
181 //=================================================================================================
182 bool BuildPlugin_ValidatorBaseForFace::isValid(const std::shared_ptr<ModelAPI_Feature>& theFeature,
183                                                const std::list<std::string>& theArguments,
184                                                Events_InfoMessage& theError) const
185 {
186   // Get attribute.
187   if(theArguments.size() != 1) {
188     std::string aMsg = "Error: BuildPlugin_ValidatorBaseForFace should be used only with "
189       "1 parameter (ID of base objects list).";
190     Events_InfoMessage("BuildPlugin_Validators", aMsg).send();
191     return false;
192   }
193   AttributeSelectionListPtr aSelectionList = theFeature->selectionList(theArguments.front());
194   if(!aSelectionList.get()) {
195     theError = "Empty attribute \"%1\".";
196     theError.arg(theArguments.front());
197     return false;
198   }
199
200   bool hasEdgesOrWires = false;
201   bool hasFaces = false;
202
203   // Collect base shapes.
204   ListOfShape anEdges;
205   for(int anIndex = 0; anIndex < aSelectionList->size(); ++anIndex) {
206     AttributeSelectionPtr aSelection = aSelectionList->value(anIndex);
207     GeomShapePtr aShape = aSelection->value();
208     if(!aShape.get()) {
209       if (!aSelection->context()) {
210         theError = "Objects are not selected.";
211         return false;
212       }
213       aShape = aSelection->context()->shape();
214     }
215     ResultConstructionPtr aSketchRes =
216         std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aSelection->context());
217
218     if (aShape->shapeType() == GeomAPI_Shape::FACE ||
219         (!aSelection->value() && aSketchRes && aSketchRes->facesNum() > 0)) {
220       // skip faces exploding
221       hasFaces = true;
222       continue;
223     }
224
225     for(GeomAPI_ShapeExplorer anExp(aShape, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) {
226       hasEdgesOrWires = true;
227       GeomShapePtr anEdge = anExp.current();
228       anEdges.push_back(anEdge);
229     }
230   }
231
232   if (hasFaces && hasEdgesOrWires) {
233     theError = "Faces and edges/wires should be selected together.";
234     return false;
235   } else if (hasEdgesOrWires && anEdges.empty()) {
236     theError = "Objects are not selected.";
237     return false;
238   }
239
240   // Check that edges does not have intersections.
241   if(anEdges.size() > 1) {
242     GeomAlgoAPI_PaveFiller aPaveFiller(anEdges, false);
243     if(!aPaveFiller.isDone()) {
244       theError = "Error while checking if edges intersects.";
245       return false;
246     }
247     GeomShapePtr aSectedEdges = aPaveFiller.shape();
248
249     size_t anEdgesNum = 0;
250     for(GeomAPI_ShapeExplorer
251         anExp(aSectedEdges, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) {
252       anEdgesNum++;
253     }
254     if(anEdgesNum != anEdges.size()) {
255       theError = "Selected objects have intersections.";
256       return false;
257     }
258   }
259
260   if (!anEdges.empty()) {
261     // Check that they are planar.
262     std::shared_ptr<GeomAPI_Pln> aPln = GeomAlgoAPI_ShapeTools::findPlane(anEdges);
263     if(!aPln.get()) {
264       theError = "Selected object(s) should belong to only one plane.";
265       return false;
266     }
267
268     // Check that selected objects have closed contours.
269     GeomAlgoAPI_SketchBuilder aBuilder(aPln, anEdges);
270     const ListOfShape& aFaces = aBuilder.faces();
271     if(aFaces.empty()) {
272       theError = "Selected objects do not generate closed contour.";
273       return false;
274     }
275   }
276
277   return true;
278 }
279
280 //=================================================================================================
281 bool BuildPlugin_ValidatorBaseForSolids::isValid(
282   const std::shared_ptr<ModelAPI_Feature>& theFeature, const std::list<std::string>& theArguments,
283   Events_InfoMessage& theError) const
284 {
285   // Get base objects list.
286   AttributeSelectionListPtr aSelectionList = theFeature->selectionList(theArguments.front());
287   if (!aSelectionList.get()) {
288     theError = "Could not get selection list.";
289     return false;
290   }
291   if (aSelectionList->size() == 0) {
292     theError = "Empty selection list.";
293     return false;
294   }
295
296   // Collect base shapes.
297   ListOfShape anOriginalShapes;
298   for (int anIndex = 0; anIndex < aSelectionList->size(); ++anIndex) {
299     AttributeSelectionPtr aSelection = aSelectionList->value(anIndex);
300     if (!aSelection->context().get()) {
301       theError = "Invalid selection.";
302       return false;
303     }
304     GeomShapePtr aShape = aSelection->value();
305     if (!aShape.get())
306       aShape = aSelection->context()->shape();
307     anOriginalShapes.push_back(aShape);
308   }
309
310   std::shared_ptr<GeomAlgoAPI_MakeVolume> anAlgorithm(
311     new GeomAlgoAPI_MakeVolume(anOriginalShapes, false));
312
313   std::string anErr;
314   if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(anAlgorithm, "MakeVolume", anErr)) {
315     theError = anErr;
316     return false;
317   }
318
319   // set of allowed types of results
320   std::set<GeomAPI_Shape::ShapeType> aResultType;
321   std::string aType = theArguments.back();
322   if (aType == "solid")
323     aResultType.insert(GeomAPI_Shape::SOLID);
324   else if (aType == "compsolid") {
325     aResultType.insert(GeomAPI_Shape::COMPSOLID);
326     aResultType.insert(GeomAPI_Shape::SOLID);
327   }
328
329   GeomShapePtr aCompound = anAlgorithm->shape();
330   if (aCompound->shapeType() == GeomAPI_Shape::COMPOUND) {
331     GeomAPI_ShapeIterator anIt(aCompound);
332     GeomShapePtr aFoundSub;
333     for (; anIt.more() && !aFoundSub; anIt.next()) {
334       aFoundSub = anIt.current();
335       if (aResultType.count(aFoundSub->shapeType()) == 0) {
336         theError = "Unable to build a solid";
337         return false;
338       }
339     }
340     if (anIt.more() || !aFoundSub.get()) {
341       theError = "Unable to build a solid";
342       return false;
343     }
344   } else if (aResultType.count(aCompound->shapeType()) == 0) {
345     theError = "Unable to build a solid";
346     return false;
347   }
348   // check the internal faces presence
349   for(GeomAPI_ShapeExplorer aFaces(aCompound, GeomAPI_Shape::FACE); aFaces.more(); aFaces.next()) {
350     if (aFaces.current()->orientation() == GeomAPI_Shape::INTERNAL) {
351       theError = "Internal faces are not allowed in the resulting solid";
352       return false;
353     }
354   }
355
356   return true;
357 }
358
359
360 //=================================================================================================
361 bool BuildPlugin_ValidatorSubShapesSelection::isValid(const AttributePtr& theAttribute,
362                                                       const std::list<std::string>& theArguments,
363                                                       Events_InfoMessage& theError) const
364 {
365   if(theArguments.size() != 1) {
366     // LCOV_EXCL_START
367     std::string aMsg = "Error: BuildPlugin_ValidatorSubShapesSelection should be used only with "
368       "1 parameter(Sketch feature id).";
369     Events_InfoMessage("BuildPlugin_Validators", aMsg).send();
370     return false;
371     // LCOV_EXCL_STOP
372   }
373
374   // Get base objects list.
375   if(theAttribute->attributeType() != ModelAPI_AttributeSelectionList::typeId()) {
376     // LCOV_EXCL_START
377     std::string aMsg =
378       "Error: BuildPlugin_ValidatorSubShapesSelection does not support attribute type \""
379       "%1\"\n Only \"%2\" supported.";
380     Events_InfoMessage("BuildPlugin_Validators", aMsg).
381       arg(theAttribute->attributeType()).arg(ModelAPI_AttributeSelectionList::typeId()).send();
382     return false;
383     // LCOV_EXCL_STOP
384   }
385   AttributeSelectionListPtr aSelectionList =
386     std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(theAttribute);
387   if(!aSelectionList.get()) {
388     theError = "Could not get selection list.";
389     return false;
390   }
391
392   // Get base shape.
393   const std::string aBaseShapeId = "base_shape";
394   FeaturePtr aFeature = ModelAPI_Feature::feature(theAttribute->owner());
395   AttributeSelectionPtr aShapeAttrSelection = aFeature->selection(aBaseShapeId);
396
397   if(!aShapeAttrSelection.get()) {
398     theError = "Base shape is empty.";
399     return false;
400   }
401
402   ResultPtr aBaseContext = aShapeAttrSelection->context();
403
404   GeomShapePtr aBaseShape  = aShapeAttrSelection->value();
405   if(!aBaseShape.get()) {
406     theError = "Base shape is empty.";
407     return false;
408   }
409
410   GeomAlgoAPI_ShapeBuilder aBuilder;
411   aBuilder.removeInternal(aBaseShape);
412   aBaseShape = aBuilder.shape();
413
414   // If selected shape is wire allow to select only vertices. If face - allow vertices and edges.
415   std::set<GeomAPI_Shape::ShapeType> anAllowedTypes;
416   switch(aBaseShape->shapeType()) {
417     case GeomAPI_Shape::FACE: anAllowedTypes.insert(GeomAPI_Shape::EDGE);
418     case GeomAPI_Shape::WIRE: anAllowedTypes.insert(GeomAPI_Shape::VERTEX);
419     default: break;
420   }
421
422   // Check selected shapes.
423   GeomValidators_FeatureKind aFeatureKindValidator;
424   std::list<std::string> anArguments;
425   anArguments.push_back(theArguments.front());
426   for(int anIndex = 0; anIndex < aSelectionList->size(); ++anIndex) {
427     AttributeSelectionPtr aSelectionAttrInList = aSelectionList->value(anIndex);
428     if(!aSelectionAttrInList.get()) {
429       theError = "Empty attribute in list.";
430       return false;
431     }
432
433     // If context of selection same skip.
434     if(aBaseContext == aSelectionAttrInList->context()) {
435       continue;
436     }
437
438     // Check that it is a selection on Sketch.
439     if(!aFeatureKindValidator.isValid(aSelectionAttrInList, anArguments, theError)) {
440       return false;
441     }
442
443     // Check shape type.
444     GeomShapePtr aShapeInList = aSelectionAttrInList->value();
445     if(!aShapeInList.get()) {
446       aShapeInList = aSelectionAttrInList->context()->shape();
447     }
448     if(anAllowedTypes.find(aShapeInList->shapeType()) == anAllowedTypes.cend()) {
449       theError = "Selected shape has unacceptable type.";
450       return false;
451     }
452
453     // Check that shape inside wire or face.
454     if(!GeomAlgoAPI_ShapeTools::isSubShapeInsideShape(aShapeInList, aBaseShape)) {
455       theError = "Selected shape is not inside base face.";
456       return false;
457     }
458   }
459
460   return true;
461 }
462
463
464 //=================================================================================================
465 bool BuildPlugin_ValidatorFillingSelection::isValid(const AttributePtr& theAttribute,
466                                                     const std::list<std::string>& /*theArguments*/,
467                                                     Events_InfoMessage& theError) const
468 {
469   // Get base objects list.
470   if (theAttribute->attributeType() != ModelAPI_AttributeSelectionList::typeId()) {
471     // LCOV_EXCL_START
472     std::string aMsg =
473       "Error: BuildPlugin_ValidatorFillingSelection does not support attribute type \""
474       "%1\"\n Only \"%2\" supported.";
475     Events_InfoMessage("BuildPlugin_Validators", aMsg).
476       arg(theAttribute->attributeType()).arg(ModelAPI_AttributeSelectionList::typeId()).send();
477     return false;
478     // LCOV_EXCL_STOP
479   }
480   AttributeSelectionListPtr aSelectionList =
481     std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(theAttribute);
482   if (!aSelectionList.get()) {
483     theError = "Could not get selection list.";
484     return false;
485   }
486
487   //FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner());
488
489   // Check selected shapes.
490   for (int anIndex = 0; anIndex < aSelectionList->size(); ++anIndex) {
491     AttributeSelectionPtr aSelectionAttrInList = aSelectionList->value(anIndex);
492     if (!aSelectionAttrInList.get()) {
493       theError = "Empty attribute in list.";
494       return false;
495     }
496
497     // Check shape exists.
498     GeomShapePtr aShapeInList = aSelectionAttrInList->value();
499     if (!aShapeInList.get()) {
500       theError = "Object has no shape";
501       return false;
502     }
503
504     // Check shape type.
505     GeomAPI_Shape::ShapeType aType = aShapeInList->shapeType();
506     if (aType != GeomAPI_Shape::EDGE && aType != GeomAPI_Shape::WIRE) {
507       theError = "Incorrect objects selected";
508       return false;
509     }
510   }
511
512   return true;
513 }
514
515
516 //=================================================================================================
517 bool BuildPlugin_ValidatorBaseForVertex::isValid(const AttributePtr& theAttribute,
518                                                  const std::list<std::string>& /*theArguments*/,
519                                                  Events_InfoMessage& theError) const
520 {
521   if (!theAttribute.get()) {
522     theError = "Error: empty selection.";
523     return false;
524   }
525
526   AttributeSelectionListPtr aSelectionList =
527     std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(theAttribute);
528   if (!aSelectionList.get()) {
529     theError = "Could not get selection list.";
530     return false;
531   }
532
533   for (int anIndex = 0; anIndex < aSelectionList->size(); ++anIndex) {
534     AttributeSelectionPtr aSelectionAttr = aSelectionList->value(anIndex);
535     if (!aSelectionAttr.get()) {
536       theError = "Empty attribute in list.";
537       return false;
538     }
539
540     // Vertex?
541     bool isVertex = false;
542     GeomShapePtr aShape = aSelectionAttr->value();
543     ResultPtr aContext = aSelectionAttr->context();
544     if (!aShape.get() && aContext.get())
545       aShape = aContext->shape();
546     if (aShape.get())
547       isVertex = (aShape->shapeType() == GeomAPI_Shape::VERTEX);
548
549     if (!isVertex) {
550       // Sketch?
551       FeaturePtr aFeature = aSelectionAttr->contextFeature();
552       if (!aFeature.get()) {
553         GeomShapePtr aValue = aSelectionAttr->value();
554         // whole sketch is allowed only
555         if (aContext.get() && !aValue.get()) {
556           aFeature = ModelAPI_Feature::feature(aContext);
557         }
558       }
559
560       if (!aFeature.get()) {
561         theError = "Error: Incorrect selection.";
562         return false;
563       }
564
565       if (aFeature->getKind() != SketchPlugin_Sketch::ID()) {
566         theError = "Error: %1 shape is not allowed for selection.";
567         theError.arg(aFeature->getKind());
568         return false;
569       }
570     }
571   }
572
573   return true;
574 }
575
576 //=================================================================================================
577 bool BuildPlugin_ValidatorExpressionInterpolation::isValid(const AttributePtr& theAttribute,
578                                                    const std::list<std::string>& /*theArguments*/,
579                                                    Events_InfoMessage& theError) const
580 {
581   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
582
583   AttributeStringPtr aStrAttr =
584       std::dynamic_pointer_cast<ModelAPI_AttributeString>(theAttribute);
585   if (!aStrAttr->isInitialized()) {
586     theError = "Attribute \"%1\" is not initialized.";
587     theError.arg(aStrAttr->id());
588     return false;
589   }
590   bool isEmptyExpr = aStrAttr->value().empty();
591   if (isEmptyExpr) {
592     theError = "Expression is empty.";
593     return false;
594   }
595
596   theError = aFeature->string(BuildPlugin_Interpolation::EXPRESSION_ERROR_ID())->value();
597   return theError.empty();
598 }
599