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) {
71 if (TNaming_Tool::HasLabel(theAccess, theShape)) { // selection and delete evolution are not used
72 for(TNaming_SameShapeIterator aShapes(theShape, theAccess); aShapes.More(); aShapes.Next())
74 Handle(TNaming_NamedShape) aNS;
75 if (aShapes.Label().FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
76 if (aNS->Evolution() == TNaming_MODIFY || aNS->Evolution() == TNaming_GENERATED ||
77 aNS->Evolution() == TNaming_PRIMITIVE) {
78 aResult = aNS->Label();
87 #define SET_ALGO_FLAGS(algo) \
88 algo->myLab = theAccess; \
89 algo->myBaseDocumentLab = theBaseDocument; \
90 algo->myGeometricalNaming = theGeometricalNaming; \
91 algo->myUseNeighbors = theUseNeighbors; \
92 algo->myUseIntersections = theUseIntersections; \
93 algo->myAlwaysGeometricalNaming = theAlwaysGeometricalNaming;
95 Selector_Algo* Selector_Algo::select(const TopoDS_Shape theContext, const TopoDS_Shape theValue,
96 const TDF_Label theAccess, const TDF_Label theBaseDocument,
97 const bool theGeometricalNaming, const bool theUseNeighbors, const bool theUseIntersections,
98 const bool theAlwaysGeometricalNaming)
100 Selector_Algo* aResult = NULL;
101 if (theValue.IsNull() || theContext.IsNull())
104 // check the value shape can be named as it is, or it is needed to construct it from the
105 // higher level shapes (like a box vertex by faces that form this vertex)
106 bool aIsFound = !findGoodLabelWithShape(theAccess, theValue).IsNull();
107 if (!aIsFound && !theBaseDocument.IsNull()) // searching in the base document
108 aIsFound = !findGoodLabelWithShape(theBaseDocument, theValue).IsNull();
110 TopAbs_ShapeEnum aSelectionType = theValue.ShapeType();
111 if (aSelectionType == TopAbs_COMPOUND || aSelectionType == TopAbs_COMPSOLID ||
112 aSelectionType == TopAbs_SHELL || aSelectionType == TopAbs_WIRE)
114 Selector_Container* aContainer = new Selector_Container;
115 SET_ALGO_FLAGS(aContainer);
116 if (aContainer->select(theContext, theValue))
122 Selector_Intersect* anIntersect = new Selector_Intersect;
123 SET_ALGO_FLAGS(anIntersect);
124 bool aGoodIntersector = anIntersect->select(theContext, theValue);
125 // weak intersector is bad for not use neighbors and has lower priority than neighbors
126 if (aGoodIntersector && anIntersect->myWeakIndex == -1) {
129 if (!theUseNeighbors) {
133 // searching by neighbors
134 Selector_FilterByNeighbors* aNBs = new Selector_FilterByNeighbors;
135 SET_ALGO_FLAGS(aNBs);
136 // searching a context lab to store in NB algorithm
137 TDF_Label aContextLab = findGoodLabelWithShape(theAccess, theContext);
138 if (aContextLab.IsNull() && !theBaseDocument.IsNull()) // search also in the base document
139 aContextLab = findGoodLabelWithShape(theBaseDocument, theContext);
140 if (aNBs->select(aContextLab, theContext, theValue)) {
145 if (aGoodIntersector) { // if neighbors are bad, use intersector with weak index
150 // pure weak naming: there is no sense to use pure weak naming for neighbors selection
151 if (theUseNeighbors) {
152 Selector_WeakName* aWeak = new Selector_WeakName;
153 SET_ALGO_FLAGS(aWeak);
154 if (aWeak->select(theContext, theValue)) {
159 return NULL; // not found value in the tree, not found context shape in the tree also
161 // searching for the base shapes of the value
162 Handle(TNaming_NamedShape) aPrimitiveNS;
163 NCollection_List<Handle(TNaming_NamedShape)> aModifList;
164 for(int aUseExternal = 0; aUseExternal < 2; aUseExternal++) {
165 TDF_Label aLab = aUseExternal == 0 ? theAccess : theBaseDocument;
166 if (aLab.IsNull() || !TNaming_Tool::HasLabel(aLab, theValue))
168 for(TNaming_SameShapeIterator aShapes(theValue, aLab); aShapes.More(); aShapes.Next())
170 Handle(TNaming_NamedShape) aNS;
171 if (aShapes.Label().FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
172 TNaming_Evolution anEvolution = aNS->Evolution();
173 if (anEvolution == TNaming_PRIMITIVE) { // the value shape is declared as PRIMITIVE
176 } else if (anEvolution == TNaming_GENERATED || anEvolution == TNaming_MODIFY) {
177 // check this is a new shape
178 TNaming_Iterator aNSIter(aNS);
179 for(; aNSIter.More(); aNSIter.Next())
180 if (aNSIter.NewShape().IsSame(theValue))
182 if (aNSIter.More()) // new was found
183 aModifList.Append(aNS);
189 if (!aPrimitiveNS.IsNull()) {
190 Selector_Primitive* aPrimitive = new Selector_Primitive;
191 SET_ALGO_FLAGS(aPrimitive);
192 aPrimitive->select(aPrimitiveNS->Label());
195 Selector_Modify* aModify = new Selector_Modify;
196 SET_ALGO_FLAGS(aModify);
197 bool aGoodModify = aModify->select(aModifList, theContext, theValue);
198 // weak intersector is bad for not use neighbors and has lower priority than neighbors
199 if (aGoodModify && aModify->myWeakIndex == -1) {
202 if (!theUseNeighbors) {
206 // searching by neighbors
207 Selector_FilterByNeighbors* aNBs = new Selector_FilterByNeighbors;
208 SET_ALGO_FLAGS(aNBs);
209 // searching a context lab to store in NB algorithm
210 TDF_Label aContextLab = findGoodLabelWithShape(theAccess, theContext);
211 if (aContextLab.IsNull() && !theBaseDocument.IsNull()) // search also in the base document
212 aContextLab = findGoodLabelWithShape(theBaseDocument, theContext);
213 if (aNBs->select(aContextLab, theContext, theValue)) {
219 if (aGoodModify) { // if neighbors are bad, use modify algo with weak index
225 return NULL; // invalid case
228 Selector_Algo* Selector_Algo::relesectWithAllGeometry(
229 Selector_Algo* theOldAlgo, const TopoDS_Shape theContext)
231 return select(theContext, theOldAlgo->value(),
232 theOldAlgo->myLab, theOldAlgo->myBaseDocumentLab, true, true, true, true);
235 void Selector_Algo::storeBaseArray(const TDF_LabelList& theRef, const TDF_Label& theLast)
237 Handle(TDataStd_ReferenceArray) anArray =
238 TDataStd_ReferenceArray::Set(myLab, kBASE_ARRAY, 0, theRef.Extent());
239 Handle(TDataStd_ExtStringList) anEntries; // entries of references to external document
240 const TDF_Label aThisDocRoot = myLab.Root();
241 TDF_LabelList::Iterator aBIter(theRef);
242 for(int anIndex = 0; true; aBIter.Next(), anIndex++) {
243 const TDF_Label& aLab = aBIter.More() ? aBIter.Value() : theLast;
244 // check this is a label of this document
245 if (aLab.Root().IsEqual(aThisDocRoot)) {
246 anArray->SetValue(anIndex, aLab);
247 } else { // store reference to external document as an entry-string
248 if (anEntries.IsNull()) {
249 anEntries = TDataStd_ExtStringList::Set(myLab, kBASE_LIST);
251 TCollection_AsciiString anEntry;
252 TDF_Tool::Entry(aLab, anEntry);
253 anEntries->Append(anEntry);
254 anArray->SetValue(anIndex, aThisDocRoot); // stored root means it is external reference
261 bool Selector_Algo::restoreBaseArray(TDF_LabelList& theRef, TDF_Label& theLast)
263 const TDF_Label aThisDocRoot = myLab.Root();
264 Handle(TDataStd_ExtStringList) anEntries; // entries of references to external document
265 TDataStd_ListOfExtendedString::Iterator anIter;
266 Handle(TDataStd_ReferenceArray) anArray;
267 if (myLab.FindAttribute(kBASE_ARRAY, anArray)) {
268 int anUpper = anArray->Upper();
269 for(int anIndex = anArray->Lower(); anIndex <= anUpper; anIndex++) {
270 TDF_Label aLab = anArray->Value(anIndex);
271 if (aLab.IsEqual(aThisDocRoot)) { // external document reference
272 if (myBaseDocumentLab.IsNull())
274 if (anEntries.IsNull()) {
275 if (!myLab.FindAttribute(kBASE_LIST, anEntries))
277 anIter.Initialize(anEntries->List());
281 TDF_Tool::Label(myBaseDocumentLab.Data(), anIter.Value(), aLab);
284 if (anIndex == anUpper) {
295 void Selector_Algo::store(const TopoDS_Shape theShape)
297 TNaming_Builder aBuilder(myLab);
298 aBuilder.Select(theShape, theShape);
301 bool Selector_Algo::sameGeometry(const TopoDS_Shape theShape1, const TopoDS_Shape theShape2) {
302 if (!theShape1.IsNull() && !theShape2.IsNull() && theShape1.ShapeType() == theShape2.ShapeType())
304 if (theShape1.ShapeType() == TopAbs_FACE) { // check surfaces
305 TopLoc_Location aLoc1, aLoc2;
306 TopoDS_Face aFace1 = TopoDS::Face(theShape1);
307 Handle(Geom_Surface) aSurf1 = BRep_Tool::Surface(aFace1, aLoc1);
308 TopoDS_Face aFace2 = TopoDS::Face(theShape2);
309 Handle(Geom_Surface) aSurf2 = BRep_Tool::Surface(aFace2, aLoc2);
310 return aSurf1 == aSurf2 && aLoc1.IsEqual(aLoc2);
311 } else if (theShape1.ShapeType() == TopAbs_EDGE) { // check curves
312 TopLoc_Location aLoc1, aLoc2;
313 Standard_Real aFirst, aLast;
314 TopoDS_Edge anEdge1 = TopoDS::Edge(theShape1);
315 Handle(Geom_Curve) aCurve1 = BRep_Tool::Curve(anEdge1, aLoc1, aFirst, aLast);
316 TopoDS_Edge anEdge2 = TopoDS::Edge(theShape2);
317 Handle(Geom_Curve) aCurve2 = BRep_Tool::Curve(anEdge2, aLoc2, aFirst, aLast);
318 return aCurve1 == aCurve2 && aLoc1.IsEqual(aLoc2);
324 TopoDS_Shape Selector_Algo::value()
326 Handle(TNaming_NamedShape) aNS;
327 if (myLab.FindAttribute(TNaming_NamedShape::GetID(), aNS))
329 return TopoDS_Shape(); // empty, error shape
332 Selector_Algo* Selector_Algo::restoreByLab(TDF_Label theLab, TDF_Label theBaseDocLab)
334 Handle(TDataStd_Integer) aTypeAttr;
335 if (!theLab.FindAttribute(kSEL_TYPE, aTypeAttr))
337 Selector_Type aType = Selector_Type(aTypeAttr->Get());
338 Selector_Algo* aResult = NULL;
340 case SELTYPE_CONTAINER: {
341 aResult = new Selector_Container;
344 case SELTYPE_INTERSECT: {
345 aResult = new Selector_Intersect;
348 case SELTYPE_MODIFICATION: {
349 aResult = new Selector_Modify;
352 case SELTYPE_PRIMITIVE: {
353 aResult = new Selector_Primitive;
356 case SELTYPE_FILTER_BY_NEIGHBOR: {
357 aResult = new Selector_FilterByNeighbors;
360 case SELTYPE_WEAK_NAMING: {
361 aResult = new Selector_WeakName;
364 default: { // unknown case
368 aResult->myLab = theLab;
369 aResult->myBaseDocumentLab = theBaseDocLab;
370 aResult->myGeometricalNaming = theLab.IsAttribute(kGEOMETRICAL_NAMING);
371 if (!aResult->restore()) {
379 Selector_Algo* Selector_Algo::restoreByName(TDF_Label theLab, TDF_Label theBaseDocLab,
380 std::string theName, const TopAbs_ShapeEnum theShapeType, const bool theGeomNaming,
381 Selector_NameGenerator* theNameGenerator, TDF_Label& theContextLab)
383 Selector_Algo* aResult = NULL;
384 if (theName[0] == '[') { // intersection or container
385 switch(theShapeType) {
386 case TopAbs_COMPOUND:
387 case TopAbs_COMPSOLID:
390 aResult = new Selector_Container;
395 aResult = new Selector_Intersect;
399 } else if (theName[0] == '(') { // filter by neighbors
400 aResult = new Selector_FilterByNeighbors;
401 } else if (theName.find(pureWeakNameID()) == 0) { // weak naming identifier
402 aResult = new Selector_WeakName;
403 } else if (theName.find('&') != std::string::npos) { // modification
404 aResult = new Selector_Modify;
405 } else { // primitive
406 aResult = new Selector_Primitive;
409 aResult->myLab = theLab;
410 aResult->myBaseDocumentLab = theBaseDocLab;
411 aResult->myGeometricalNaming = theGeomNaming;
412 theContextLab = aResult->restoreByName(theName, theShapeType, theNameGenerator);
413 if (theContextLab.IsNull()) {
421 void Selector_Algo::storeType(const Selector_Type theType)
423 myLab.ForgetAllAttributes(true);
424 TDataStd_Integer::Set(myLab, kSEL_TYPE, (int)(theType));
425 if (myGeometricalNaming)
426 TDataStd_UAttribute::Set(myLab, kGEOMETRICAL_NAMING);
429 /// Returns true if theSub is in theContext shape
430 static bool isInContext(const TopoDS_Shape& theContext, const TopoDS_Shape& theSub) {
431 for(TopExp_Explorer anExp(theContext, theSub.ShapeType()); anExp.More(); anExp.Next()) {
432 if (anExp.Current().IsSame(theSub))
438 bool Selector_Algo::findNewVersion(const TopoDS_Shape& theContext, TopoDS_Shape& theResult) const
440 if (theResult.IsNull())
442 if (!TNaming_Tool::HasLabel(myLab, theResult)) {
443 if (theResult.ShapeType() == TopAbs_COMPOUND) { // do it for all elements of compound
444 BRep_Builder aBuilder;
445 TopoDS_Compound aResultingCompound;
446 aBuilder.MakeCompound(aResultingCompound);
447 bool aWasChanged = false;
448 for (TopoDS_Iterator anIter(theResult); anIter.More(); anIter.Next()) {
449 TopoDS_Shape aSub = anIter.Value();
450 if (findNewVersion(theContext, aSub))
452 aBuilder.Add(aResultingCompound, aSub);
455 theResult = aResultingCompound;
459 // check theResult is in theContext
460 if (isInContext(theContext, theResult))
462 // searching the next modifications of the result shape in document
463 TopTools_MapOfShape aResultShapes;
464 for(TNaming_NewShapeIterator aBaseIter(theResult, myLab); aBaseIter.More(); aBaseIter.Next())
466 TNaming_Evolution anEvolution = aBaseIter.NamedShape()->Evolution();
467 if (anEvolution == TNaming_GENERATED || anEvolution == TNaming_MODIFY) {
468 TopoDS_Shape aNextModification = aBaseIter.Shape();
469 if (aNextModification.IsNull())
471 if (isInContext(theContext, aNextModification))
472 // don't add vertices generated from edges
473 if (aNextModification.ShapeType() <= theResult.ShapeType())
474 aResultShapes.Add(aNextModification);
475 else if (findNewVersion(theContext, aNextModification))
476 if (aNextModification.ShapeType() <= theResult.ShapeType())
477 aResultShapes.Add(aNextModification);
480 if (aResultShapes.IsEmpty())
482 if (aResultShapes.Size() == 1) {
483 theResult = TopTools_MapIteratorOfMapOfShape(aResultShapes).Value();
484 } else { // make a compound of all results
485 BRep_Builder aBuilder;
486 TopoDS_Compound aResultingCompound;
487 aBuilder.MakeCompound(aResultingCompound);
488 for(TopTools_MapIteratorOfMapOfShape anIter(aResultShapes); anIter.More(); anIter.Next())
489 aBuilder.Add(aResultingCompound, anIter.Value());
490 theResult = aResultingCompound;