1 // Copyright (C) 2014-2019 CEA/DEN, EDF R&D
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.
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.
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
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #include <Selector_Algo.h>
22 #include <Selector_Primitive.h>
23 #include <Selector_Intersect.h>
24 #include <Selector_Modify.h>
25 #include <Selector_Container.h>
26 #include <Selector_FilterByNeighbors.h>
27 #include <Selector_WeakName.h>
29 #include <TDF_Tool.hxx>
30 #include <TopoDS_Face.hxx>
31 #include <TopoDS_Edge.hxx>
33 #include <TopTools_MapOfShape.hxx>
34 #include <Geom_Surface.hxx>
35 #include <BRep_Tool.hxx>
36 #include <TNaming_Builder.hxx>
37 #include <TNaming_Tool.hxx>
38 #include <TNaming_SameShapeIterator.hxx>
39 #include <TNaming_Iterator.hxx>
40 #include <TNaming_NewShapeIterator.hxx>
41 #include <TDataStd_ReferenceArray.hxx>
42 #include <TDataStd_ExtStringList.hxx>
43 #include <TDataStd_Integer.hxx>
44 #include <TDataStd_UAttribute.hxx>
45 #include <TopoDS_Iterator.hxx>
46 #include <TopExp_Explorer.hxx>
47 #include <BRep_Builder.hxx>
50 /// type of the selection, integer keeps the Selector_Type value
51 static const Standard_GUID kSEL_TYPE("db174d59-c2e3-4a90-955e-55544df090d6");
52 // geometrical naming indicator
53 static const Standard_GUID kGEOMETRICAL_NAMING("a5322d02-50fb-43ed-9255-75c7b93f6657");
56 // reference attribute that contains the reference to labels where the "from" or "base" shapes
57 // of selection are located
58 static const Standard_GUID kBASE_ARRAY("7c515b1a-9549-493d-9946-a4933a22f45f");
59 // if the base array contains reference to the root label, this means that it refers to an
60 // external document and this list contains a tag in the document
61 static const Standard_GUID kBASE_LIST("7c515b1a-9549-493d-9946-a4933a22f45a");
63 Selector_Algo::Selector_Algo()
65 myGeometricalNaming = false; // default values
66 myAlwaysGeometricalNaming = false;
69 static TDF_Label findGoodLabelWithShape(const TDF_Label theAccess, const TopoDS_Shape& theShape) {
72 bool aMyOpLabIsComputed = false;
73 if (TNaming_Tool::HasLabel(theAccess, theShape)) { // selection and delete evolution are not used
74 for(TNaming_SameShapeIterator aShapes(theShape, theAccess); aShapes.More(); aShapes.Next())
76 Handle(TNaming_NamedShape) aNS;
77 if (aShapes.Label().FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
78 if (aNS->Evolution() == TNaming_MODIFY || aNS->Evolution() == TNaming_GENERATED ||
79 aNS->Evolution() == TNaming_PRIMITIVE) {
80 // getting label of this operation to avoid usage of ready shapes of this
81 if (!aMyOpLabIsComputed) {
83 if (!aMyOpLab.IsNull()) {
84 int aDepth = aMyOpLab.Depth();
88 for(; aDepth != 3; aDepth--)
89 aMyOpLab = aMyOpLab.Father();
92 aMyOpLabIsComputed = true;
94 if (!aMyOpLab.IsNull() && aNS->Label().IsDescendant(aMyOpLab))
96 aResult = aNS->Label();
105 #define SET_ALGO_FLAGS(algo) \
106 algo->myLab = theAccess; \
107 algo->myBaseDocumentLab = theBaseDocument; \
108 algo->myGeometricalNaming = theGeometricalNaming; \
109 algo->myUseNeighbors = theUseNeighbors; \
110 algo->myUseIntersections = theUseIntersections; \
111 algo->myAlwaysGeometricalNaming = theAlwaysGeometricalNaming;
113 Selector_Algo* Selector_Algo::select(const TopoDS_Shape theContext, const TopoDS_Shape theValue,
114 const TDF_Label theAccess, const TDF_Label theBaseDocument,
115 const bool theGeometricalNaming, const bool theUseNeighbors, const bool theUseIntersections,
116 const bool theAlwaysGeometricalNaming)
118 Selector_Algo* aResult = NULL;
119 if (theValue.IsNull() || theContext.IsNull())
122 // check the value shape can be named as it is, or it is needed to construct it from the
123 // higher level shapes (like a box vertex by faces that form this vertex)
124 bool aIsFound = !findGoodLabelWithShape(theAccess, theValue).IsNull();
125 if (!aIsFound && !theBaseDocument.IsNull()) // searching in the base document
126 aIsFound = !findGoodLabelWithShape(theBaseDocument, theValue).IsNull();
128 TopAbs_ShapeEnum aSelectionType = theValue.ShapeType();
129 if (aSelectionType == TopAbs_COMPOUND || aSelectionType == TopAbs_COMPSOLID ||
130 aSelectionType == TopAbs_SHELL || aSelectionType == TopAbs_WIRE)
132 Selector_Container* aContainer = new Selector_Container;
133 SET_ALGO_FLAGS(aContainer);
134 if (aContainer->select(theContext, theValue))
140 Selector_Intersect* anIntersect = new Selector_Intersect;
141 SET_ALGO_FLAGS(anIntersect);
142 bool aGoodIntersector = anIntersect->select(theContext, theValue);
143 // weak intersector is bad for not use neighbors and has lower priority than neighbors
144 if (aGoodIntersector && anIntersect->myWeakIndex == -1) {
147 if (!theUseNeighbors) {
151 // searching by neighbors
152 Selector_FilterByNeighbors* aNBs = new Selector_FilterByNeighbors;
153 SET_ALGO_FLAGS(aNBs);
154 // searching a context lab to store in NB algorithm
155 TDF_Label aContextLab = findGoodLabelWithShape(theAccess, theContext);
156 if (aContextLab.IsNull() && !theBaseDocument.IsNull()) // search also in the base document
157 aContextLab = findGoodLabelWithShape(theBaseDocument, theContext);
158 if (aNBs->select(aContextLab, theContext, theValue)) {
163 if (aGoodIntersector) { // if neighbors are bad, use intersector with weak index
168 // pure weak naming: there is no sense to use pure weak naming for neighbors selection
169 if (theUseNeighbors) {
170 Selector_WeakName* aWeak = new Selector_WeakName;
171 SET_ALGO_FLAGS(aWeak);
172 if (aWeak->select(theContext, theValue)) {
177 return NULL; // not found value in the tree, not found context shape in the tree also
179 // getting label of this operation to avoid usage of ready shapes of this
180 TDF_Label aMyOpLab = theAccess;
181 if (!aMyOpLab.IsNull()) {
182 int aDepth = aMyOpLab.Depth();
186 for(; aDepth != 3; aDepth--)
187 aMyOpLab = aMyOpLab.Father();
190 // searching for the base shapes of the value
191 Handle(TNaming_NamedShape) aPrimitiveNS;
192 NCollection_List<Handle(TNaming_NamedShape)> aModifList;
193 for(int aUseExternal = 0; aUseExternal < 2; aUseExternal++) {
194 TDF_Label aLab = aUseExternal == 0 ? theAccess : theBaseDocument;
195 if (aLab.IsNull() || !TNaming_Tool::HasLabel(aLab, theValue))
197 for(TNaming_SameShapeIterator aShapes(theValue, aLab); aShapes.More(); aShapes.Next())
199 Handle(TNaming_NamedShape) aNS;
200 if (aShapes.Label().FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
201 TNaming_Evolution anEvolution = aNS->Evolution();
202 if (anEvolution == TNaming_SELECTED || anEvolution == TNaming_DELETE)
204 if (!aMyOpLab.IsNull() && aNS->Label().IsDescendant(aMyOpLab))
206 if (anEvolution == TNaming_PRIMITIVE) { // the value shape is declared as PRIMITIVE
209 } else if (anEvolution == TNaming_GENERATED || anEvolution == TNaming_MODIFY) {
210 // check this is a new shape
211 TNaming_Iterator aNSIter(aNS);
212 for(; aNSIter.More(); aNSIter.Next())
213 if (aNSIter.NewShape().IsSame(theValue))
215 if (aNSIter.More()) // new was found
216 aModifList.Append(aNS);
222 if (!aPrimitiveNS.IsNull()) {
223 Selector_Primitive* aPrimitive = new Selector_Primitive;
224 SET_ALGO_FLAGS(aPrimitive);
225 aPrimitive->select(aPrimitiveNS->Label());
228 Selector_Modify* aModify = new Selector_Modify;
229 SET_ALGO_FLAGS(aModify);
230 bool aGoodModify = aModify->select(aModifList, theContext, theValue);
231 // weak intersector is bad for not use neighbors and has lower priority than neighbors
232 if (aGoodModify && aModify->myWeakIndex == -1) {
235 if (!theUseNeighbors) {
239 // searching by neighbors
240 Selector_FilterByNeighbors* aNBs = new Selector_FilterByNeighbors;
241 SET_ALGO_FLAGS(aNBs);
242 // searching a context lab to store in NB algorithm
243 TDF_Label aContextLab = findGoodLabelWithShape(theAccess, theContext);
244 if (aContextLab.IsNull() && !theBaseDocument.IsNull()) // search also in the base document
245 aContextLab = findGoodLabelWithShape(theBaseDocument, theContext);
246 if (aNBs->select(aContextLab, theContext, theValue)) {
252 if (aGoodModify) { // if neighbors are bad, use modify algo with weak index
258 return NULL; // invalid case
261 Selector_Algo* Selector_Algo::relesectWithAllGeometry(
262 Selector_Algo* theOldAlgo, const TopoDS_Shape theContext)
264 return select(theContext, theOldAlgo->value(),
265 theOldAlgo->myLab, theOldAlgo->myBaseDocumentLab, true, true, true, true);
268 void Selector_Algo::storeBaseArray(const TDF_LabelList& theRef, const TDF_Label& theLast)
270 Handle(TDataStd_ReferenceArray) anArray =
271 TDataStd_ReferenceArray::Set(myLab, kBASE_ARRAY, 0, theRef.Extent());
272 Handle(TDataStd_ExtStringList) anEntries; // entries of references to external document
273 const TDF_Label aThisDocRoot = myLab.Root();
274 TDF_LabelList::Iterator aBIter(theRef);
275 for(int anIndex = 0; true; aBIter.Next(), anIndex++) {
276 const TDF_Label& aLab = aBIter.More() ? aBIter.Value() : theLast;
277 // check this is a label of this document
278 if (aLab.Root().IsEqual(aThisDocRoot)) {
279 anArray->SetValue(anIndex, aLab);
280 } else { // store reference to external document as an entry-string
281 if (anEntries.IsNull()) {
282 anEntries = TDataStd_ExtStringList::Set(myLab, kBASE_LIST);
284 TCollection_AsciiString anEntry;
285 TDF_Tool::Entry(aLab, anEntry);
286 anEntries->Append(anEntry);
287 anArray->SetValue(anIndex, aThisDocRoot); // stored root means it is external reference
294 bool Selector_Algo::restoreBaseArray(TDF_LabelList& theRef, TDF_Label& theLast)
296 const TDF_Label aThisDocRoot = myLab.Root();
297 Handle(TDataStd_ExtStringList) anEntries; // entries of references to external document
298 TDataStd_ListOfExtendedString::Iterator anIter;
299 Handle(TDataStd_ReferenceArray) anArray;
300 if (myLab.FindAttribute(kBASE_ARRAY, anArray)) {
301 int anUpper = anArray->Upper();
302 for(int anIndex = anArray->Lower(); anIndex <= anUpper; anIndex++) {
303 TDF_Label aLab = anArray->Value(anIndex);
304 if (aLab.IsEqual(aThisDocRoot)) { // external document reference
305 if (myBaseDocumentLab.IsNull())
307 if (anEntries.IsNull()) {
308 if (!myLab.FindAttribute(kBASE_LIST, anEntries))
310 anIter.Initialize(anEntries->List());
314 TDF_Tool::Label(myBaseDocumentLab.Data(), anIter.Value(), aLab);
317 if (anIndex == anUpper) {
328 void Selector_Algo::store(const TopoDS_Shape theShape)
330 TNaming_Builder aBuilder(myLab);
331 aBuilder.Select(theShape, theShape);
334 bool Selector_Algo::sameGeometry(const TopoDS_Shape theShape1, const TopoDS_Shape theShape2) {
335 if (!theShape1.IsNull() && !theShape2.IsNull() && theShape1.ShapeType() == theShape2.ShapeType())
337 if (theShape1.ShapeType() == TopAbs_FACE) { // check surfaces
338 TopLoc_Location aLoc1, aLoc2;
339 TopoDS_Face aFace1 = TopoDS::Face(theShape1);
340 Handle(Geom_Surface) aSurf1 = BRep_Tool::Surface(aFace1, aLoc1);
341 TopoDS_Face aFace2 = TopoDS::Face(theShape2);
342 Handle(Geom_Surface) aSurf2 = BRep_Tool::Surface(aFace2, aLoc2);
343 return aSurf1 == aSurf2 && aLoc1.IsEqual(aLoc2);
344 } else if (theShape1.ShapeType() == TopAbs_EDGE) { // check curves
345 TopLoc_Location aLoc1, aLoc2;
346 Standard_Real aFirst, aLast;
347 TopoDS_Edge anEdge1 = TopoDS::Edge(theShape1);
348 Handle(Geom_Curve) aCurve1 = BRep_Tool::Curve(anEdge1, aLoc1, aFirst, aLast);
349 TopoDS_Edge anEdge2 = TopoDS::Edge(theShape2);
350 Handle(Geom_Curve) aCurve2 = BRep_Tool::Curve(anEdge2, aLoc2, aFirst, aLast);
351 return aCurve1 == aCurve2 && aLoc1.IsEqual(aLoc2);
357 TopoDS_Shape Selector_Algo::value()
359 Handle(TNaming_NamedShape) aNS;
360 if (myLab.FindAttribute(TNaming_NamedShape::GetID(), aNS))
362 return TopoDS_Shape(); // empty, error shape
365 Selector_Algo* Selector_Algo::restoreByLab(TDF_Label theLab, TDF_Label theBaseDocLab)
367 Handle(TDataStd_Integer) aTypeAttr;
368 if (!theLab.FindAttribute(kSEL_TYPE, aTypeAttr))
370 Selector_Type aType = Selector_Type(aTypeAttr->Get());
371 Selector_Algo* aResult = NULL;
373 case SELTYPE_CONTAINER: {
374 aResult = new Selector_Container;
377 case SELTYPE_INTERSECT: {
378 aResult = new Selector_Intersect;
381 case SELTYPE_MODIFICATION: {
382 aResult = new Selector_Modify;
385 case SELTYPE_PRIMITIVE: {
386 aResult = new Selector_Primitive;
389 case SELTYPE_FILTER_BY_NEIGHBOR: {
390 aResult = new Selector_FilterByNeighbors;
393 case SELTYPE_WEAK_NAMING: {
394 aResult = new Selector_WeakName;
397 default: { // unknown case
401 aResult->myLab = theLab;
402 aResult->myBaseDocumentLab = theBaseDocLab;
403 aResult->myGeometricalNaming = theLab.IsAttribute(kGEOMETRICAL_NAMING);
404 if (!aResult->restore()) {
412 Selector_Algo* Selector_Algo::restoreByName(TDF_Label theLab, TDF_Label theBaseDocLab,
413 std::string theName, const TopAbs_ShapeEnum theShapeType, const bool theGeomNaming,
414 Selector_NameGenerator* theNameGenerator, TDF_Label& theContextLab)
416 Selector_Algo* aResult = NULL;
417 if (theName[0] == '[') { // intersection or container
418 switch(theShapeType) {
419 case TopAbs_COMPOUND:
420 case TopAbs_COMPSOLID:
423 aResult = new Selector_Container;
428 aResult = new Selector_Intersect;
432 } else if (theName[0] == '(') { // filter by neighbors
433 aResult = new Selector_FilterByNeighbors;
434 } else if (theName.find(pureWeakNameID()) == 0) { // weak naming identifier
435 aResult = new Selector_WeakName;
436 } else if (theName.find('&') != std::string::npos) { // modification
437 aResult = new Selector_Modify;
438 } else { // primitive
439 aResult = new Selector_Primitive;
442 aResult->myLab = theLab;
443 aResult->myBaseDocumentLab = theBaseDocLab;
444 aResult->myGeometricalNaming = theGeomNaming;
445 theContextLab = aResult->restoreByName(theName, theShapeType, theNameGenerator);
446 if (theContextLab.IsNull()) {
454 void Selector_Algo::storeType(const Selector_Type theType)
456 myLab.ForgetAllAttributes(true);
457 TDataStd_Integer::Set(myLab, kSEL_TYPE, (int)(theType));
458 if (myGeometricalNaming)
459 TDataStd_UAttribute::Set(myLab, kGEOMETRICAL_NAMING);
462 /// Returns true if theSub is in theContext shape
463 static bool isInContext(const TopoDS_Shape& theContext, const TopoDS_Shape& theSub) {
464 for(TopExp_Explorer anExp(theContext, theSub.ShapeType()); anExp.More(); anExp.Next()) {
465 if (anExp.Current().IsSame(theSub))
471 bool Selector_Algo::findNewVersion(const TopoDS_Shape& theContext, TopoDS_Shape& theResult) const
473 if (theResult.IsNull())
475 if (!TNaming_Tool::HasLabel(myLab, theResult)) {
476 if (theResult.ShapeType() == TopAbs_COMPOUND) { // do it for all elements of compound
477 BRep_Builder aBuilder;
478 TopoDS_Compound aResultingCompound;
479 aBuilder.MakeCompound(aResultingCompound);
480 bool aWasChanged = false;
481 for (TopoDS_Iterator anIter(theResult); anIter.More(); anIter.Next()) {
482 TopoDS_Shape aSub = anIter.Value();
483 if (findNewVersion(theContext, aSub))
485 aBuilder.Add(aResultingCompound, aSub);
488 theResult = aResultingCompound;
492 // check theResult is in theContext
493 if (isInContext(theContext, theResult))
495 // searching the next modifications of the result shape in document
496 TopTools_MapOfShape aResultShapes;
497 for(TNaming_NewShapeIterator aBaseIter(theResult, myLab); aBaseIter.More(); aBaseIter.Next())
499 TNaming_Evolution anEvolution = aBaseIter.NamedShape()->Evolution();
500 if (anEvolution == TNaming_GENERATED || anEvolution == TNaming_MODIFY) {
501 TopoDS_Shape aNextModification = aBaseIter.Shape();
502 if (aNextModification.IsNull())
504 if (isInContext(theContext, aNextModification))
505 // don't add vertices generated from edges
506 if (aNextModification.ShapeType() <= theResult.ShapeType())
507 aResultShapes.Add(aNextModification);
508 else if (findNewVersion(theContext, aNextModification))
509 if (aNextModification.ShapeType() <= theResult.ShapeType())
510 aResultShapes.Add(aNextModification);
513 if (aResultShapes.IsEmpty())
515 if (aResultShapes.Size() == 1) {
516 theResult = TopTools_MapIteratorOfMapOfShape(aResultShapes).Value();
517 } else { // make a compound of all results
518 BRep_Builder aBuilder;
519 TopoDS_Compound aResultingCompound;
520 aBuilder.MakeCompound(aResultingCompound);
521 for(TopTools_MapIteratorOfMapOfShape anIter(aResultShapes); anIter.More(); anIter.Next())
522 aBuilder.Add(aResultingCompound, anIter.Value());
523 theResult = aResultingCompound;