1 // Copyright (C) 2014-2017 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
18 // email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
21 #include <Selector_Algo.h>
23 #include <Selector_Primitive.h>
24 #include <Selector_Intersect.h>
25 #include <Selector_Modify.h>
26 #include <Selector_Container.h>
27 #include <Selector_FilterByNeighbors.h>
28 #include <Selector_WeakName.h>
30 #include <TDF_Tool.hxx>
31 #include <TopoDS_Face.hxx>
32 #include <TopoDS_Edge.hxx>
34 #include <TopTools_MapOfShape.hxx>
35 #include <Geom_Surface.hxx>
36 #include <BRep_Tool.hxx>
37 #include <TNaming_Builder.hxx>
38 #include <TNaming_Tool.hxx>
39 #include <TNaming_SameShapeIterator.hxx>
40 #include <TNaming_Iterator.hxx>
41 #include <TNaming_NewShapeIterator.hxx>
42 #include <TDataStd_ReferenceArray.hxx>
43 #include <TDataStd_ExtStringList.hxx>
44 #include <TDataStd_Integer.hxx>
45 #include <TDataStd_UAttribute.hxx>
46 #include <TopoDS_Iterator.hxx>
47 #include <TopExp_Explorer.hxx>
48 #include <BRep_Builder.hxx>
51 /// type of the selection, integer keeps the Selector_Type value
52 static const Standard_GUID kSEL_TYPE("db174d59-c2e3-4a90-955e-55544df090d6");
53 // geometrical naming indicator
54 static const Standard_GUID kGEOMETRICAL_NAMING("a5322d02-50fb-43ed-9255-75c7b93f6657");
57 // reference attribute that contains the reference to labels where the "from" or "base" shapes
58 // of selection are located
59 static const Standard_GUID kBASE_ARRAY("7c515b1a-9549-493d-9946-a4933a22f45f");
60 // if the base array contains reference to the root label, this means that it refers to an
61 // external document and this list contains a tag in the document
62 static const Standard_GUID kBASE_LIST("7c515b1a-9549-493d-9946-a4933a22f45a");
64 Selector_Algo::Selector_Algo()
66 myGeometricalNaming = false; // default values
67 myAlwaysGeometricalNaming = false;
70 static TDF_Label findGoodLabelWithShape(const TDF_Label theAccess, const TopoDS_Shape& theShape) {
72 if (TNaming_Tool::HasLabel(theAccess, theShape)) { // selection and delete evolution are not used
73 for(TNaming_SameShapeIterator aShapes(theShape, theAccess); aShapes.More(); aShapes.Next())
75 Handle(TNaming_NamedShape) aNS;
76 if (aShapes.Label().FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
77 if (aNS->Evolution() == TNaming_MODIFY || aNS->Evolution() == TNaming_GENERATED ||
78 aNS->Evolution() == TNaming_PRIMITIVE) {
79 aResult = aNS->Label();
88 #define SET_ALGO_FLAGS(algo) \
89 algo->myLab = theAccess; \
90 algo->myBaseDocumentLab = theBaseDocument; \
91 algo->myGeometricalNaming = theGeometricalNaming; \
92 algo->myUseNeighbors = theUseNeighbors; \
93 algo->myUseIntersections = theUseIntersections; \
94 algo->myAlwaysGeometricalNaming = theAlwaysGeometricalNaming;
96 Selector_Algo* Selector_Algo::select(const TopoDS_Shape theContext, const TopoDS_Shape theValue,
97 const TDF_Label theAccess, const TDF_Label theBaseDocument,
98 const bool theGeometricalNaming, const bool theUseNeighbors, const bool theUseIntersections,
99 const bool theAlwaysGeometricalNaming)
101 Selector_Algo* aResult = NULL;
102 if (theValue.IsNull() || theContext.IsNull())
105 // check the value shape can be named as it is, or it is needed to construct it from the
106 // higher level shapes (like a box vertex by faces that form this vertex)
107 bool aIsFound = !findGoodLabelWithShape(theAccess, theValue).IsNull();
108 if (!aIsFound && !theBaseDocument.IsNull()) // searching in the base document
109 aIsFound = !findGoodLabelWithShape(theBaseDocument, theValue).IsNull();
111 TopAbs_ShapeEnum aSelectionType = theValue.ShapeType();
112 if (aSelectionType == TopAbs_COMPOUND || aSelectionType == TopAbs_COMPSOLID ||
113 aSelectionType == TopAbs_SHELL || aSelectionType == TopAbs_WIRE)
115 Selector_Container* aContainer = new Selector_Container;
116 SET_ALGO_FLAGS(aContainer);
117 if (aContainer->select(theContext, theValue))
123 Selector_Intersect* anIntersect = new Selector_Intersect;
124 SET_ALGO_FLAGS(anIntersect);
125 bool aGoodIntersector = anIntersect->select(theContext, theValue);
126 // weak intersector is bad for not use neighbors and has lower priority than neighbors
127 if (aGoodIntersector && anIntersect->myWeakIndex == -1) {
130 if (!theUseNeighbors) {
134 // searching by neighbors
135 Selector_FilterByNeighbors* aNBs = new Selector_FilterByNeighbors;
136 SET_ALGO_FLAGS(aNBs);
137 // searching a context lab to store in NB algorithm
138 TDF_Label aContextLab = findGoodLabelWithShape(theAccess, theContext);
139 if (aContextLab.IsNull() && !theBaseDocument.IsNull()) // search also in the base document
140 aContextLab = findGoodLabelWithShape(theBaseDocument, theContext);
141 if (aNBs->select(aContextLab, theContext, theValue)) {
146 if (aGoodIntersector) { // if neighbors are bad, use intersector with weak index
151 // pure weak naming: there is no sense to use pure weak naming for neighbors selection
152 if (theUseNeighbors) {
153 Selector_WeakName* aWeak = new Selector_WeakName;
154 SET_ALGO_FLAGS(aWeak);
155 if (aWeak->select(theContext, theValue)) {
160 return NULL; // not found value in the tree, not found context shape in the tree also
162 // searching for the base shapes of the value
163 Handle(TNaming_NamedShape) aPrimitiveNS;
164 NCollection_List<Handle(TNaming_NamedShape)> aModifList;
165 for(int aUseExternal = 0; aUseExternal < 2; aUseExternal++) {
166 TDF_Label aLab = aUseExternal == 0 ? theAccess : theBaseDocument;
167 if (aLab.IsNull() || !TNaming_Tool::HasLabel(aLab, theValue))
169 for(TNaming_SameShapeIterator aShapes(theValue, aLab); aShapes.More(); aShapes.Next())
171 Handle(TNaming_NamedShape) aNS;
172 if (aShapes.Label().FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
173 TNaming_Evolution anEvolution = aNS->Evolution();
174 if (anEvolution == TNaming_PRIMITIVE) { // the value shape is declared as PRIMITIVE
177 } else if (anEvolution == TNaming_GENERATED || anEvolution == TNaming_MODIFY) {
178 // check this is a new shape
179 TNaming_Iterator aNSIter(aNS);
180 for(; aNSIter.More(); aNSIter.Next())
181 if (aNSIter.NewShape().IsSame(theValue))
183 if (aNSIter.More()) // new was found
184 aModifList.Append(aNS);
190 if (!aPrimitiveNS.IsNull()) {
191 Selector_Primitive* aPrimitive = new Selector_Primitive;
192 SET_ALGO_FLAGS(aPrimitive);
193 aPrimitive->select(aPrimitiveNS->Label());
196 Selector_Modify* aModify = new Selector_Modify;
197 SET_ALGO_FLAGS(aModify);
198 bool aGoodModify = aModify->select(aModifList, theContext, theValue);
199 // weak intersector is bad for not use neighbors and has lower priority than neighbors
200 if (aGoodModify && aModify->myWeakIndex == -1) {
203 if (!theUseNeighbors) {
207 // searching by neighbors
208 Selector_FilterByNeighbors* aNBs = new Selector_FilterByNeighbors;
209 SET_ALGO_FLAGS(aNBs);
210 // searching a context lab to store in NB algorithm
211 TDF_Label aContextLab = findGoodLabelWithShape(theAccess, theContext);
212 if (aContextLab.IsNull() && !theBaseDocument.IsNull()) // search also in the base document
213 aContextLab = findGoodLabelWithShape(theBaseDocument, theContext);
214 if (aNBs->select(aContextLab, theContext, theValue)) {
220 if (aGoodModify) { // if neighbors are bad, use modify algo with weak index
226 return NULL; // invalid case
229 Selector_Algo* Selector_Algo::relesectWithAllGeometry(
230 Selector_Algo* theOldAlgo, const TopoDS_Shape theContext)
232 return select(theContext, theOldAlgo->value(),
233 theOldAlgo->myLab, theOldAlgo->myBaseDocumentLab, true, true, true, true);
236 void Selector_Algo::storeBaseArray(const TDF_LabelList& theRef, const TDF_Label& theLast)
238 Handle(TDataStd_ReferenceArray) anArray =
239 TDataStd_ReferenceArray::Set(myLab, kBASE_ARRAY, 0, theRef.Extent());
240 Handle(TDataStd_ExtStringList) anEntries; // entries of references to external document
241 const TDF_Label aThisDocRoot = myLab.Root();
242 TDF_LabelList::Iterator aBIter(theRef);
243 for(int anIndex = 0; true; aBIter.Next(), anIndex++) {
244 const TDF_Label& aLab = aBIter.More() ? aBIter.Value() : theLast;
245 // check this is a label of this document
246 if (aLab.Root().IsEqual(aThisDocRoot)) {
247 anArray->SetValue(anIndex, aLab);
248 } else { // store reference to external document as an entry-string
249 if (anEntries.IsNull()) {
250 anEntries = TDataStd_ExtStringList::Set(myLab, kBASE_LIST);
252 TCollection_AsciiString anEntry;
253 TDF_Tool::Entry(aLab, anEntry);
254 anEntries->Append(anEntry);
255 anArray->SetValue(anIndex, aThisDocRoot); // stored root means it is external reference
262 bool Selector_Algo::restoreBaseArray(TDF_LabelList& theRef, TDF_Label& theLast)
264 const TDF_Label aThisDocRoot = myLab.Root();
265 Handle(TDataStd_ExtStringList) anEntries; // entries of references to external document
266 TDataStd_ListOfExtendedString::Iterator anIter;
267 Handle(TDataStd_ReferenceArray) anArray;
268 if (myLab.FindAttribute(kBASE_ARRAY, anArray)) {
269 int anUpper = anArray->Upper();
270 for(int anIndex = anArray->Lower(); anIndex <= anUpper; anIndex++) {
271 TDF_Label aLab = anArray->Value(anIndex);
272 if (aLab.IsEqual(aThisDocRoot)) { // external document reference
273 if (myBaseDocumentLab.IsNull())
275 if (anEntries.IsNull()) {
276 if (!myLab.FindAttribute(kBASE_LIST, anEntries))
278 anIter.Initialize(anEntries->List());
282 TDF_Tool::Label(myBaseDocumentLab.Data(), anIter.Value(), aLab);
285 if (anIndex == anUpper) {
296 void Selector_Algo::store(const TopoDS_Shape theShape)
298 TNaming_Builder aBuilder(myLab);
299 aBuilder.Select(theShape, theShape);
302 bool Selector_Algo::sameGeometry(const TopoDS_Shape theShape1, const TopoDS_Shape theShape2) {
303 if (!theShape1.IsNull() && !theShape2.IsNull() && theShape1.ShapeType() == theShape2.ShapeType())
305 if (theShape1.ShapeType() == TopAbs_FACE) { // check surfaces
306 TopLoc_Location aLoc1, aLoc2;
307 TopoDS_Face aFace1 = TopoDS::Face(theShape1);
308 Handle(Geom_Surface) aSurf1 = BRep_Tool::Surface(aFace1, aLoc1);
309 TopoDS_Face aFace2 = TopoDS::Face(theShape2);
310 Handle(Geom_Surface) aSurf2 = BRep_Tool::Surface(aFace2, aLoc2);
311 return aSurf1 == aSurf2 && aLoc1.IsEqual(aLoc2);
312 } else if (theShape1.ShapeType() == TopAbs_EDGE) { // check curves
313 TopLoc_Location aLoc1, aLoc2;
314 Standard_Real aFirst, aLast;
315 TopoDS_Edge anEdge1 = TopoDS::Edge(theShape1);
316 Handle(Geom_Curve) aCurve1 = BRep_Tool::Curve(anEdge1, aLoc1, aFirst, aLast);
317 TopoDS_Edge anEdge2 = TopoDS::Edge(theShape2);
318 Handle(Geom_Curve) aCurve2 = BRep_Tool::Curve(anEdge2, aLoc2, aFirst, aLast);
319 return aCurve1 == aCurve2 && aLoc1.IsEqual(aLoc2);
325 TopoDS_Shape Selector_Algo::value()
327 Handle(TNaming_NamedShape) aNS;
328 if (myLab.FindAttribute(TNaming_NamedShape::GetID(), aNS))
330 return TopoDS_Shape(); // empty, error shape
333 Selector_Algo* Selector_Algo::restoreByLab(TDF_Label theLab, TDF_Label theBaseDocLab)
335 Handle(TDataStd_Integer) aTypeAttr;
336 if (!theLab.FindAttribute(kSEL_TYPE, aTypeAttr))
338 Selector_Type aType = Selector_Type(aTypeAttr->Get());
339 Selector_Algo* aResult = NULL;
341 case SELTYPE_CONTAINER: {
342 aResult = new Selector_Container;
345 case SELTYPE_INTERSECT: {
346 aResult = new Selector_Intersect;
349 case SELTYPE_MODIFICATION: {
350 aResult = new Selector_Modify;
353 case SELTYPE_PRIMITIVE: {
354 aResult = new Selector_Primitive;
357 case SELTYPE_FILTER_BY_NEIGHBOR: {
358 aResult = new Selector_FilterByNeighbors;
361 case SELTYPE_WEAK_NAMING: {
362 aResult = new Selector_WeakName;
365 default: { // unknown case
369 aResult->myLab = theLab;
370 aResult->myBaseDocumentLab = theBaseDocLab;
371 aResult->myGeometricalNaming = theLab.IsAttribute(kGEOMETRICAL_NAMING);
372 if (!aResult->restore()) {
380 Selector_Algo* Selector_Algo::restoreByName(TDF_Label theLab, TDF_Label theBaseDocLab,
381 std::string theName, const TopAbs_ShapeEnum theShapeType, const bool theGeomNaming,
382 Selector_NameGenerator* theNameGenerator, TDF_Label& theContextLab)
384 Selector_Algo* aResult = NULL;
385 if (theName[0] == '[') { // intersection or container
386 switch(theShapeType) {
387 case TopAbs_COMPOUND:
388 case TopAbs_COMPSOLID:
391 aResult = new Selector_Container;
396 aResult = new Selector_Intersect;
400 } else if (theName[0] == '(') { // filter by neighbors
401 aResult = new Selector_FilterByNeighbors;
402 } else if (theName.find(pureWeakNameID()) == 0) { // weak naming identifier
403 aResult = new Selector_WeakName;
404 } else if (theName.find('&') != std::string::npos) { // modification
405 aResult = new Selector_Modify;
406 } else { // primitive
407 aResult = new Selector_Primitive;
410 aResult->myLab = theLab;
411 aResult->myBaseDocumentLab = theBaseDocLab;
412 aResult->myGeometricalNaming = theGeomNaming;
413 theContextLab = aResult->restoreByName(theName, theShapeType, theNameGenerator);
414 if (theContextLab.IsNull()) {
422 void Selector_Algo::storeType(const Selector_Type theType)
424 myLab.ForgetAllAttributes(true);
425 TDataStd_Integer::Set(myLab, kSEL_TYPE, (int)(theType));
426 if (myGeometricalNaming)
427 TDataStd_UAttribute::Set(myLab, kGEOMETRICAL_NAMING);
430 /// Returns true if theSub is in theContext shape
431 static bool isInContext(const TopoDS_Shape& theContext, const TopoDS_Shape& theSub) {
432 for(TopExp_Explorer anExp(theContext, theSub.ShapeType()); anExp.More(); anExp.Next()) {
433 if (anExp.Current().IsSame(theSub))
439 bool Selector_Algo::findNewVersion(const TopoDS_Shape& theContext, TopoDS_Shape& theResult) const
441 if (theResult.IsNull())
443 if (!TNaming_Tool::HasLabel(myLab, theResult)) {
444 if (theResult.ShapeType() == TopAbs_COMPOUND) { // do it for all elements of compound
445 BRep_Builder aBuilder;
446 TopoDS_Compound aResultingCompound;
447 aBuilder.MakeCompound(aResultingCompound);
448 bool aWasChanged = false;
449 for (TopoDS_Iterator anIter(theResult); anIter.More(); anIter.Next()) {
450 TopoDS_Shape aSub = anIter.Value();
451 if (findNewVersion(theContext, aSub))
453 aBuilder.Add(aResultingCompound, aSub);
456 theResult = aResultingCompound;
460 // check theResult is in theContext
461 if (isInContext(theContext, theResult))
463 // searching the next modifications of the result shape in document
464 TopTools_MapOfShape aResultShapes;
465 for(TNaming_NewShapeIterator aBaseIter(theResult, myLab); aBaseIter.More(); aBaseIter.Next())
467 TNaming_Evolution anEvolution = aBaseIter.NamedShape()->Evolution();
468 if (anEvolution == TNaming_GENERATED || anEvolution == TNaming_MODIFY) {
469 TopoDS_Shape aNextModification = aBaseIter.Shape();
470 if (aNextModification.IsNull())
472 if (isInContext(theContext, aNextModification))
473 // don't add vertices generated from edges
474 if (aNextModification.ShapeType() <= theResult.ShapeType())
475 aResultShapes.Add(aNextModification);
476 else if (findNewVersion(theContext, aNextModification))
477 if (aNextModification.ShapeType() <= theResult.ShapeType())
478 aResultShapes.Add(aNextModification);
481 if (aResultShapes.IsEmpty())
483 if (aResultShapes.Size() == 1) {
484 theResult = TopTools_MapIteratorOfMapOfShape(aResultShapes).Value();
485 } else { // make a compound of all results
486 BRep_Builder aBuilder;
487 TopoDS_Compound aResultingCompound;
488 aBuilder.MakeCompound(aResultingCompound);
489 for(TopTools_MapIteratorOfMapOfShape anIter(aResultShapes); anIter.More(); anIter.Next())
490 aBuilder.Add(aResultingCompound, anIter.Value());
491 theResult = aResultingCompound;