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 <Model_ResultConstruction.h>
23 #include <Model_Data.h>
24 #include <ModelAPI_CompositeFeature.h>
25 #include <GeomAlgoAPI_SketchBuilder.h>
26 #include <GeomAPI_Tools.h>
27 #include <ModelAPI_Events.h>
28 #include <Model_Document.h>
29 #include <GeomAPI_PlanarEdges.h>
30 #include <GeomAPI_Shape.h>
31 #include <Events_Loop.h>
32 #include <GeomDataAPI_Point.h>
33 #include <GeomDataAPI_Dir.h>
35 #include <TDF_ChildIDIterator.hxx>
36 #include <TNaming_NamedShape.hxx>
37 #include <TNaming_Builder.hxx>
38 #include <TDataStd_IntPackedMap.hxx>
39 #include <TDataStd_Name.hxx>
40 #include <TDataStd_UAttribute.hxx>
41 #include <BRep_Builder.hxx>
43 #include <TopoDS_Edge.hxx>
44 #include <TopoDS_Vertex.hxx>
45 #include <TopoDS_ListOfShape.hxx>
46 #include <TopExp_Explorer.hxx>
47 #include <TopTools_MapOfShape.hxx>
48 #include <NCollection_IndexedDataMap.hxx>
53 // identifier of the infinite result
54 Standard_GUID kIS_INFINITE("dea8cc5a-53f2-49c1-94e8-a947bed20a9f");
55 // identifier of the result not in history
56 Standard_GUID kIS_IN_HISTORY("a9aec01c-805e-44d1-b5d2-a63f06522f8a");
58 void Model_ResultConstruction::colorConfigInfo(std::string& theSection, std::string& theName,
59 std::string& theDefault)
61 theSection = "Visualization";
62 theName = "result_construction_color";
63 theDefault = DEFAULT_COLOR();
66 void Model_ResultConstruction::setShape(std::shared_ptr<GeomAPI_Shape> theShape)
68 if (myShape != theShape) {
70 if (!theShape.get() || !theShape->isEqual(myShape)) {
71 static const Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
72 ModelAPI_EventCreator::get()->sendUpdated(data()->owner(), anEvent);
78 std::shared_ptr<GeomAPI_Shape> Model_ResultConstruction::shape()
83 static std::string shortName(
84 std::shared_ptr<ModelAPI_ResultConstruction>& theConstr)
86 std::string aName = theConstr->data()->name();
87 // remove "-", "/" and "&" command-symbols
88 aName.erase(std::remove(aName.begin(), aName.end(), '-'), aName.end());
89 aName.erase(std::remove(aName.begin(), aName.end(), '/'), aName.end());
90 aName.erase(std::remove(aName.begin(), aName.end(), '&'), aName.end());
91 // remove the last 's', 'e', 'f' and 'r' symbols:
92 // they are used as markers of start/end/forward/reversed indicators
93 static const std::string aSyms("sefr");
94 std::string::iterator aSuffix = aName.end() - 1;
95 while(aSyms.find(*aSuffix) != std::string::npos) {
98 aName.erase(aSuffix + 1, aName.end());
102 bool Model_ResultConstruction::updateShape()
104 std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(data());
105 if (aData && aData->isValid()) {
106 TDF_Label aShapeLab = aData->shapeLab();
107 Handle(TNaming_NamedShape) aNS;
108 if (aShapeLab.FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
109 TopoDS_Shape aShape = aNS->Get();
110 if (!aShape.IsNull()) {
111 if (aShape.ShapeType() == TopAbs_COMPOUND) {
112 // restore the sketch planar edges object
113 std::shared_ptr<GeomAPI_PlanarEdges> aBigWire(new GeomAPI_PlanarEdges);
114 aBigWire->setImpl<TopoDS_Shape>(new TopoDS_Shape(aShape));
116 document()->feature(std::dynamic_pointer_cast<ModelAPI_Result>(data()->owner()));
117 std::shared_ptr<GeomDataAPI_Point> anOrigin =
118 std::dynamic_pointer_cast<GeomDataAPI_Point>(aSketch->data()->attribute("Origin"));
119 std::shared_ptr<GeomDataAPI_Dir> aDirX =
120 std::dynamic_pointer_cast<GeomDataAPI_Dir>(aSketch->data()->attribute("DirX"));
121 std::shared_ptr<GeomDataAPI_Dir> aNorm =
122 std::dynamic_pointer_cast<GeomDataAPI_Dir>(aSketch->data()->attribute("Norm"));
123 if (anOrigin.get() && aDirX.get() && aNorm.get()) {
124 aBigWire->setPlane(anOrigin->pnt(), aDirX->dir(), aNorm->dir());
129 // just restore shape
130 GeomShapePtr aGShape(new GeomAPI_Shape);
131 aGShape->setImpl<TopoDS_Shape>(new TopoDS_Shape(aShape));
132 myShape = GeomAPI_Tools::getTypedShape(aGShape); // restore the sketch sub-components
140 Model_ResultConstruction::Model_ResultConstruction()
144 bool Model_ResultConstruction::isInHistory()
146 std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(data());
147 if (aData.get() && aData->isValid()) {
148 return !aData->label().IsAttribute(kIS_IN_HISTORY); // by default no attribute, but in history
150 return true; // unknown case
153 void Model_ResultConstruction::setIsInHistory(const bool isInHistory)
155 std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(data());
156 if (aData.get() && aData->isValid()) {
158 TDataStd_UAttribute::Set(aData->label(), kIS_IN_HISTORY);
160 aData->label().ForgetAttribute(kIS_IN_HISTORY);
164 bool Model_ResultConstruction::isInfinite()
166 std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(data());
167 if (aData.get() && aData->isValid()) {
168 return aData->label().IsAttribute(kIS_INFINITE);
170 return false; // unknown case
173 void Model_ResultConstruction::setInfinite(const bool theInfinite)
175 std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(data());
176 if (aData.get() && aData->isValid()) {
178 TDataStd_UAttribute::Set(aData->label(), kIS_INFINITE);
180 aData->label().ForgetAttribute(kIS_INFINITE);
184 int Model_ResultConstruction::facesNum(const bool theUpdateNaming)
187 std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(data());
188 if (aData.get() && aData->isValid()) {
189 TDF_Label aShapeLab = aData->shapeLab();
190 TDF_ChildIDIterator anOldIter(aShapeLab, TDataStd_IntPackedMap::GetID());
191 for (; anOldIter.More(); anOldIter.Next()) {
198 std::shared_ptr<GeomAPI_Face> Model_ResultConstruction::face(const int theIndex)
200 std::shared_ptr<GeomAPI_Face> aResult;
202 std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(data());
203 if (aData.get() && aData->isValid()) {
204 TDF_Label aShapeLab = aData->shapeLab();
205 TDF_ChildIDIterator anOldIter(aShapeLab, TDataStd_IntPackedMap::GetID());
206 for (; anOldIter.More(); anOldIter.Next()) {
207 if (anIndex == theIndex) {
208 Handle(TNaming_NamedShape) aNS;
209 anOldIter.Value()->Label().FindAttribute(TNaming_NamedShape::GetID(), aNS);
210 aResult.reset(new GeomAPI_Face);
211 aResult->setImpl(new TopoDS_Shape(aNS->Get()));
220 void Model_ResultConstruction::setIsConcealed(const bool theValue)
222 // do nothing: the construction element is never concealed
225 void Model_ResultConstruction::storeShape(std::shared_ptr<GeomAPI_Shape> theShape)
227 std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(data());
228 if (aData && aData->isValid()) {
229 std::string aMyName = data()->name();
230 TDF_Label aShapeLab = aData->shapeLab();
231 if (!theShape.get() || theShape->isNull()) {
232 aShapeLab.ForgetAllAttributes();
233 TDataStd_Name::Set(aShapeLab, aMyName.c_str()); // restore name forgotten
236 std::shared_ptr<Model_Document> aMyDoc =
237 std::dynamic_pointer_cast<Model_Document>(document());
238 const TopoDS_Shape& aShape = theShape->impl<TopoDS_Shape>();
239 if (isInfinite() || aShape.ShapeType() == TopAbs_VERTEX) {
240 aShapeLab.ForgetAllAttributes(); // clear all previously stored
241 TNaming_Builder aBuilder(aShapeLab);
242 aBuilder.Generated(aShape);
243 TDataStd_Name::Set(aShapeLab, aMyName.c_str());
244 aMyDoc->addNamingName(aShapeLab, aMyName);
245 } else if (aShape.ShapeType() == TopAbs_EDGE) { // store sub-vertices on sub-labels
246 aShapeLab.ForgetAllAttributes(); // clear all previously stored
247 TNaming_Builder aBuilder(aShapeLab);
248 aBuilder.Generated(aShape);
250 TopExp_Explorer anExp(aShape, TopAbs_VERTEX);
251 for(int anIndex = 1; anExp.More(); anExp.Next(), anIndex++) {
252 TDF_Label aSubLab = aShapeLab.FindChild(anIndex);
253 TNaming_Builder aBuilder(aSubLab);
254 aBuilder.Generated(anExp.Current());
255 std::string aVertexName = aMyName + "_" + (anIndex == 1 ? "StartVertex" : "EndVertex");
256 TDataStd_Name::Set(aSubLab, aVertexName.c_str());
257 aMyDoc->addNamingName(aSubLab, aVertexName);
259 TDataStd_Name::Set(aShapeLab, aMyName.c_str());
260 aMyDoc->addNamingName(aShapeLab, aMyName);
261 } else { // this is probably sketch, so, work with it as with composite
262 std::shared_ptr<GeomAPI_PlanarEdges> aWirePtr =
263 std::dynamic_pointer_cast<GeomAPI_PlanarEdges>(theShape);
265 return; // unknown case
266 ResultPtr aThisPtr = std::dynamic_pointer_cast<ModelAPI_Result>(data()->owner());
267 FeaturePtr aThisFeature = aMyDoc->feature(aThisPtr);
268 CompositeFeaturePtr aComposite =
269 std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aThisFeature);
270 if (!aComposite || aComposite->numberOfSubs() == 0)
271 return; // unknown case
272 // collect indices of curves of current composite
273 NCollection_DataMap<Handle(Geom_Curve), int> aCurvesIndices;
274 NCollection_DataMap<int, TopoDS_Edge> anEdgeIndices;
275 std::map<int, std::string> aComponentsNames; // names of components that lay on index
276 const int aSubNum = aComposite->numberOfSubs();
277 for (int a = 0; a < aSubNum; a++) {
278 FeaturePtr aSub = aComposite->subFeature(a);
279 const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aSub->results();
280 std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRes = aResults.cbegin();
281 for (; aRes != aResults.cend(); aRes++) {
282 ResultConstructionPtr aConstr =
283 std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(*aRes);
284 if (aConstr->shape() && aConstr->shape()->isEdge()) {
285 TopoDS_Edge anEdge = TopoDS::Edge(aConstr->shape()->impl<TopoDS_Shape>());
286 Standard_Real aFirst, aLast;
287 Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
288 aCurvesIndices.Bind(aCurve, a);
289 anEdgeIndices.Bind(a, anEdge);
290 aComponentsNames[a] = shortName(aConstr);
295 std::list<std::shared_ptr<GeomAPI_Shape> > aFaces;
296 GeomAlgoAPI_SketchBuilder::createFaces(aWirePtr->origin(), aWirePtr->dirX(),
297 aWirePtr->norm(), aWirePtr, aFaces);
298 // order is important to store faces in the same order if sketch is created from scratch
299 NCollection_IndexedDataMap<TopoDS_Face, TColStd_ListOfInteger> aNewIndices; // edges indices
300 std::list<std::shared_ptr<GeomAPI_Shape> >::iterator aFIter = aFaces.begin();
301 for (; aFIter != aFaces.end(); aFIter++) {
302 std::shared_ptr<GeomAPI_Face> aFace(new GeomAPI_Face(*aFIter));
303 // put them to a label, trying to keep the same faces on the same labels
304 if (aFace.get() && !aFace->isNull()) {
305 TopoDS_Face aTopoFace = TopoDS::Face(aFace->impl<TopoDS_Shape>());
306 aNewIndices.Add(aTopoFace, TColStd_ListOfInteger());
307 // keep new indices of sub-elements used in this face
308 for (TopExp_Explorer anEdges(aTopoFace, TopAbs_EDGE); anEdges.More(); anEdges.Next()) {
309 TopoDS_Edge anEdge = TopoDS::Edge(anEdges.Current());
310 Standard_Real aFirst, aLast;
311 Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
312 if (aCurvesIndices.IsBound(aCurve)) {
313 int anIndex = aCurvesIndices.Find(aCurve);
314 if ((aFirst > aLast) != (anEdge.Orientation() == TopAbs_REVERSED))
316 aNewIndices.ChangeFromKey(aTopoFace).Append(anIndex);
321 NCollection_DataMap<int, TopoDS_Face> aFacesOrder; // faces -> tag where they must be set
322 NCollection_List<TopoDS_Face> anUnorderedFaces; // faces that may be located at any index
323 // searching for the best new candidate to old location
324 NCollection_IndexedDataMap<TopoDS_Face, TColStd_ListOfInteger>::Iterator
325 aNewIter(aNewIndices);
326 for (; aNewIter.More(); aNewIter.Next()) {
327 double aBestFound = 0, aBestNotFound = 1.e+100;
329 const TColStd_ListOfInteger& aNewInd = aNewIter.Value();
330 // old faces indices where they where located
331 TDF_ChildIDIterator anOldIter(aShapeLab, TDataStd_IntPackedMap::GetID());
332 for (; anOldIter.More(); anOldIter.Next()) {
333 int aTag = anOldIter.Value()->Label().Tag();
334 if (aFacesOrder.IsBound(aTag))
335 continue; // already found a best candidate
336 Handle(TDataStd_IntPackedMap) anOldIndices =
337 Handle(TDataStd_IntPackedMap)::DownCast(anOldIter.Value());
338 double aFound = 0, aNotFound = 0;
339 TColStd_ListOfInteger::Iterator aNewIndIter(aNewInd);
340 for (; aNewIndIter.More(); aNewIndIter.Next()) {
341 if (anOldIndices->Contains(aNewIndIter.Value())) {
344 else if (anOldIndices->Contains(-aNewIndIter.Value())) { // different orientation
351 if (aNotFound < aBestNotFound) {
352 if (aFound > aBestFound) {
353 aBestNotFound = aNotFound;
359 if (aBestTag != 0) { // found an appropriate face
360 aFacesOrder.Bind(aBestTag, aNewIter.Key());
362 anUnorderedFaces.Append(aNewIter.Key());
365 aShapeLab.ForgetAllAttributes(); // clear all previously stored
366 TDataStd_Name::Set(aShapeLab, aMyName.c_str()); // restore name forgotten
367 TNaming_Builder aBuilder(aShapeLab); // store the compound to get it ready on open of document
368 aBuilder.Generated(aShape);
369 aMyDoc->addNamingName(aShapeLab, aMyName);
370 // set new faces to the labels
372 NCollection_List<TopoDS_Face>::Iterator anUnordered(anUnorderedFaces);
373 for(int aCurrentTag = 1; !aFacesOrder.IsEmpty() || anUnordered.More(); aCurrentTag++) {
374 TopoDS_Face aFaceToPut;
375 if (aFacesOrder.IsBound(aCurrentTag)) {
376 aFaceToPut = aFacesOrder.Find(aCurrentTag);
377 aFacesOrder.UnBind(aCurrentTag);
378 } else if (anUnordered.More()){
379 aFaceToPut = anUnordered.Value();
383 if (!aFaceToPut.IsNull()) {
384 TopTools_MapOfShape aFaceEdges;
385 for(TopExp_Explorer anEdges(aFaceToPut, TopAbs_EDGE); anEdges.More(); anEdges.Next()) {
386 aFaceEdges.Add(anEdges.Current());
389 TDF_Label aLab = aShapeLab.FindChild(aCurrentTag);
390 TNaming_Builder aFaceBuilder(aLab);
391 aFaceBuilder.Generated(aFaceToPut);
392 // store also indices of the new face edges
393 Handle(TDataStd_IntPackedMap) aNewMap = TDataStd_IntPackedMap::Set(aLab);
394 const TColStd_ListOfInteger& aNewInd = aNewIndices.FindFromKey(aFaceToPut);
395 std::stringstream aName;
397 TopExp_Explorer aPutEdges(aFaceToPut, TopAbs_EDGE);
398 TNaming_Builder *anEdgesBuilder = 0, *aVerticesBuilder = 0;
399 for(TColStd_ListOfInteger::Iterator anIter(aNewInd); anIter.More(); anIter.Next()) {
400 int anIndex = anIter.Value();
401 int aModIndex = anIndex > 0 ? anIndex : -anIndex;
402 aNewMap->Add(anIndex);
403 aName<<"-"<<aComponentsNames[aModIndex];
404 if (anIter.Value() > 0)
408 // collect all edges of the face which are modified in sub-label of the face
409 if (anEdgeIndices.IsBound(aModIndex) &&
410 !aFaceEdges.Contains(anEdgeIndices.Find(aModIndex))) {
411 if (!anEdgesBuilder) {
412 TDF_Label anEdgesLabel = aLab.FindChild(1);
413 anEdgesBuilder = new TNaming_Builder(anEdgesLabel);
414 std::ostringstream aSubName;
415 // tag is needed for Test1922 to distinguish sub-edges of different faces
416 aSubName<<"SubEdge_"<<aCurrentTag;
417 TDataStd_Name::Set(anEdgesLabel, aSubName.str().c_str());
419 anEdgesBuilder->Modify(anEdgeIndices.Find(aModIndex), aPutEdges.Current());
421 // put also modified vertices, otherwise vertex of original edge has no history
422 if (anEdgeIndices.IsBound(aModIndex)) {
423 TopExp_Explorer aVExpOld(anEdgeIndices.Find(aModIndex), TopAbs_VERTEX);
424 TopExp_Explorer aVExpNew(aPutEdges.Current(), TopAbs_VERTEX);
425 for(; aVExpNew.More() && aVExpOld.More(); aVExpNew.Next(), aVExpOld.Next()) {
426 if (!aVExpOld.Current().IsSame(aVExpNew.Current())) {
427 if (!aVerticesBuilder) {
428 TDF_Label aVertLabel = aLab.FindChild(2);
429 aVerticesBuilder = new TNaming_Builder(aVertLabel);
430 std::ostringstream aSubName;
431 // tag is needed for Test1922 to distinguish sub-edges of different faces
432 aSubName<<"SubVertex_"<<aCurrentTag;
433 TDataStd_Name::Set(aVertLabel, aSubName.str().c_str());
435 aVerticesBuilder->Modify(aVExpOld.Current(), aVExpNew.Current());
443 delete anEdgesBuilder;
444 if (aVerticesBuilder)
445 delete aVerticesBuilder;
446 TDataStd_Name::Set(aLab, TCollection_ExtendedString(aName.str().c_str()));
447 aMyDoc->addNamingName(aLab, aName.str());
448 // put also wires to sub-labels to correctly select them instead of collection by edges
449 int aWireTag = 3; // first tag is for SubEdge-s, second - for vertices
450 for(TopExp_Explorer aWires(aFaceToPut, TopAbs_WIRE); aWires.More(); aWires.Next()) {
451 TDF_Label aWireLab = aLab.FindChild(aWireTag);
452 TNaming_Builder aWireBuilder(aWireLab);
453 aWireBuilder.Generated(aWires.Current());
454 std::ostringstream aWireName;
455 aWireName<<aName.str()<<"_wire";
457 aWireName<<"_"<<aWireTag - 2;
458 TDataStd_Name::Set(aWireLab, aWireName.str().c_str());
459 aMyDoc->addNamingName(aWireLab, aWireName.str());