0028055: Add UpdateAssemblies() method for top-down update of assembly compounds
authorssv <ssv@opencascade.com>
Thu, 19 Jan 2017 13:22:32 +0000 (16:22 +0300)
committerapn <apn@opencascade.com>
Thu, 19 Jan 2017 13:22:47 +0000 (16:22 +0300)
- Implement top-down update for assemblies in XCAFDoc_ShapeTool
- Get rid of UpdateAssembly() method used for partial (parent-only) update
- Adjust STEP and IGES translators to use the top-down update after filling OCAF
- Implement XUpdateAssemblies Draw command
- Add test cases bug28055_1 and bug28055_2 for component removal and part update
- Update XDE documentation to mention the new UpdateAssemblies() method

Correction for issue CR28055
Adjusting of test case bugs/fclasses/bug28217

dox/user_guides/draw_test_harness/draw_test_harness.md
dox/user_guides/xde/xde.md
src/IGESCAFControl/IGESCAFControl_Reader.cxx
src/QABugs/QABugs_19.cxx
src/STEPCAFControl/STEPCAFControl_Reader.cxx
src/XCAFDoc/XCAFDoc_ShapeTool.cxx
src/XCAFDoc/XCAFDoc_ShapeTool.hxx
src/XDEDRAW/XDEDRAW_Shapes.cxx
tests/bugs/fclasses/bug28217
tests/bugs/xde/bug28055_1 [new file with mode: 0644]
tests/bugs/xde/bug28055_2 [new file with mode: 0644]

index fae482f..d58ff06 100644 (file)
@@ -8910,6 +8910,19 @@ Sets a shape at the indicated label.
 XSetShape D 0:1:1:3 b 
 ~~~~~
 
+@subsubsection occt_draw_8_6_15  XUpdateAssemblies
+
+Syntax:      
+~~~~~
+XUpdateAssemblies <document>
+~~~~~
+
+Updates all assembly compounds in the XDE document.
+
+**Example:**
+~~~~~
+XUpdateAssemblies D
+~~~~~
 
 @subsection occt_draw_8_7_  XDE color commands 
 
index 7d8db82..092ba00 100644 (file)
@@ -156,14 +156,16 @@ You can then query or edit this Assembly node, the Main Item or another one (*my
 
 **Note** that for the examples in the rest of this guide, *myAssembly* is always presumed to be accessed this way, so this information will not be repeated.
 
-@subsubsection occt_xde_2_2_3 Updating the Assembly after Filling or Editing
+@subsubsection occt_xde_2_2_3 Updating the Assemblies after Filling or Editing
 Some actions in this chapter affect the content of the document, considered as an Assembly. As a result, you will sometimes need to update various representations (including the compounds). 
 
 To update the representations, use: 
 ~~~~~
-myAssembly->UpdateAssembly(aLabel); 
+myAssembly->UpdateAssemblies(); 
 ~~~~~
-Since this call is always used by the editing functions, you need not apply it for such functions. However, you will need this call if special edits, not using XCAF functions, are used on the document. 
+This call performs a top-down update of the Assembly compounds stored in the document.
+
+**Note** that you have to run this method manually to actualize your Assemblies after any low-level modifications on shapes.
 
 @subsubsection occt_xde_2_2_4 Adding or Setting Top Level Shapes
 
index 9dfb658..062bf3c 100644 (file)
@@ -319,6 +319,9 @@ Standard_Boolean IGESCAFControl_Reader::Transfer (Handle(TDocStd_Document) &doc)
 
   // end added by skl 13.10.2003
 
+  // Update assembly compounds
+  STool->UpdateAssemblies();
+
   return Standard_True;
 }
   
index aedea99..8f3db98 100644 (file)
@@ -1774,6 +1774,7 @@ static Standard_Integer OCC23950 (Draw_Interpretor& di, Standard_Integer argc, c
   TDataStd_Name::Set(labelA0, "ASSEMBLY");
 
   TDF_Label component01 = XCAFDoc_DocumentTool::ShapeTool (aDoc->Main ())->AddComponent (labelA0, lab1, location0);
+  XCAFDoc_DocumentTool::ShapeTool (aDoc->Main ())->UpdateAssemblies();
 
   Quantity_Color yellow(1,1,0, Quantity_TOC_RGB);
   XCAFDoc_DocumentTool::ColorTool (labelA0)->SetColor (component01, yellow, XCAFDoc_ColorGen);
index 01e2a50..b318b48 100644 (file)
@@ -689,6 +689,9 @@ Standard_Boolean STEPCAFControl_Reader::Transfer (STEPControl_Reader &reader,
   // names) if requested
   ExpandSubShapes(STool, map, ShapePDMap);
 
+  // Update assembly compounds
+  STool->UpdateAssemblies();
+
   return Standard_True;
 }
 
index 75f010b..36b0479 100644 (file)
@@ -45,6 +45,7 @@
 #include <TopoDS_Compound.hxx>
 #include <TopoDS_Iterator.hxx>
 #include <TopoDS_Shape.hxx>
+#include <TopTools_ListOfShape.hxx>
 #include <XCAFDoc.hxx>
 #include <XCAFDoc_GraphNode.hxx>
 #include <XCAFDoc_Location.hxx>
@@ -415,13 +416,6 @@ void XCAFDoc_ShapeTool::SetShape (const TDF_Label& L, const TopoDS_Shape& S)
   if(!myShapeLabels.IsBound(S)) {
     myShapeLabels.Bind(S,L);
   }
-  
-  //:abv 31.10.01: update assemblies that refer a shape
-  TDF_LabelSequence Labels;
-  if ( GetUsers ( L, Labels, Standard_True ) ) {
-    for ( Standard_Integer i=Labels.Length(); i >=1; i-- ) 
-      UpdateAssembly ( Labels(i) );
-  }
 }
 
 //=======================================================================
@@ -954,8 +948,6 @@ TDF_Label XCAFDoc_ShapeTool::AddComponent (const TDF_Label& assembly,
   L = aTag.NewChild(assembly);
   MakeReference ( L, compL, Loc );
 
-  // update assembly`s TopoDS_Shape
-  UpdateAssembly ( assembly );
   return L;
 }
 
@@ -986,34 +978,31 @@ TDF_Label XCAFDoc_ShapeTool::AddComponent (const TDF_Label& assembly,
 
 void XCAFDoc_ShapeTool::RemoveComponent (const TDF_Label& comp) const
 {
-  if ( IsComponent(comp) ) {
+  if ( IsComponent(comp) )
+  {
     comp.ForgetAllAttributes();
-    UpdateAssembly(comp.Father());
   }
 }
 
 //=======================================================================
-//function : UpdateAssembly
+//function : UpdateAssemblies
 //purpose  : 
 //=======================================================================
 
-void XCAFDoc_ShapeTool::UpdateAssembly (const TDF_Label& L) const
+void XCAFDoc_ShapeTool::UpdateAssemblies()
 {
-  if ( ! IsAssembly(L) ) return;
+  // We start from the free shapes (roots in the assembly structure)
+  TDF_LabelSequence aRootLabels;
+  GetFreeShapes(aRootLabels);
 
-  TopoDS_Compound newassembly;
-  BRep_Builder b;
-  b.MakeCompound(newassembly);
+  // Iterate over the free shapes
+  for ( TDF_LabelSequence::Iterator anIt(aRootLabels); anIt.More(); anIt.Next() )
+  {
+    const TDF_Label& aRootLab = anIt.Value();
 
-  TDF_ChildIterator chldLabIt(L);
-  for (; chldLabIt.More(); chldLabIt.Next() ) {
-    TDF_Label subLabel = chldLabIt.Value();
-    if ( IsComponent ( subLabel ) ) {
-      b.Add(newassembly, GetShape(subLabel));
-    }
+    TopoDS_Shape anAssemblyShape;
+    updateComponent(aRootLab, anAssemblyShape);
   }
-  TNaming_Builder tnBuild(L);
-  tnBuild.Generated(newassembly);
 }
 
 //=======================================================================
@@ -1907,3 +1896,114 @@ void XCAFDoc_ShapeTool::makeSubShape (const TDF_Label& Part, const TopoDS_Shape&
     makeSubShape(Part, aChildShape);
   }
 }
+
+//=======================================================================
+//function : updateComponent
+//purpose  :
+//=======================================================================
+
+Standard_Boolean XCAFDoc_ShapeTool::updateComponent(const TDF_Label& theItemLabel,
+                                                    TopoDS_Shape&    theUpdatedShape) const
+{
+  if ( !IsAssembly(theItemLabel) )
+    return Standard_False; // Do nothing for non-assemblies
+
+  // Get the currently stored compound for the assembly
+  TopoDS_Shape aCurrentRootShape;
+  GetShape(theItemLabel, aCurrentRootShape);
+
+  // Get components of the assembly
+  TDF_LabelSequence aComponentLabs;
+  GetComponents(theItemLabel, aComponentLabs);
+
+  // This flag indicates whether to update the compound of the assembly
+  Standard_Boolean isModified = Standard_False;
+
+  // Compare the number of components in XDE structure with the number of
+  // components in topological structure. A component may happen to be removed,
+  // so we have to update the assembly compound
+  Standard_Integer aNumTopoComponents = 0;
+  for ( TopoDS_Iterator aTopIt(aCurrentRootShape); aTopIt.More(); aTopIt.Next() )
+    aNumTopoComponents++;
+  //
+  if ( aNumTopoComponents != aComponentLabs.Length() )
+    isModified = Standard_True;
+
+  // Iterate over the assembly components. If at least one component is
+  // modified (this is the recursive check), then the actually stored
+  // compound has to be updated
+  TopTools_ListOfShape aComponentShapes;
+  //
+  for ( TDF_LabelSequence::Iterator aCompIt(aComponentLabs); aCompIt.More(); aCompIt.Next() )
+  {
+    const TDF_Label& aComponentLab = aCompIt.Value();
+
+    // Take the referred assembly item (ultimately, a part for an instance)
+    TDF_Label aComponentRefLab;
+    GetReferredShape(aComponentLab, aComponentRefLab);
+
+    // Shape comes with some placement transformation here
+    TopoDS_Shape aComponentShape;
+    GetShape(aComponentLab, aComponentShape);
+    TopLoc_Location aComponentLoc = aComponentShape.Location();
+
+    // If the component is a sub-assembly, then its associated compound
+    // has to be processed in the same manner
+    if ( IsAssembly(aComponentRefLab) )
+    {
+      // Recursive call
+      if ( updateComponent(aComponentRefLab, aComponentShape) )
+      {
+        if ( !isModified )
+          isModified = Standard_True;
+
+        aComponentShape.Location(aComponentLoc); // Apply placement
+      }
+    }
+    else
+    {
+      // Search for a part in the actual compound of the ultimate assembly.
+      // If the part is there, then the compound is up-to-date, so it does
+      // not require rebuilding
+      Standard_Boolean isPartFound = Standard_False;
+      for ( TopoDS_Iterator aTopoIt(aCurrentRootShape); aTopoIt.More(); aTopoIt.Next() )
+      {
+        if ( aTopoIt.Value() == aComponentShape )
+        {
+          isPartFound = Standard_True;
+          break;
+        }
+      }
+
+      if ( !isPartFound && !isModified )
+        isModified = Standard_True; // Part has been modified somewhere, so the compound
+                                    // has to be rebuilt
+    }
+
+    // Fill the list of shapes composing a new compound for the assembly
+    aComponentShapes.Append(aComponentShape);
+  }
+
+  // If any component is modified, we update the currently stored shape
+  if ( isModified )
+  {
+    TopoDS_Compound anUpdatedCompound;
+    BRep_Builder aBB;
+    aBB.MakeCompound(anUpdatedCompound);
+
+    // Compose new compound
+    for ( TopTools_ListIteratorOfListOfShape aShapeIt(aComponentShapes); aShapeIt.More(); aShapeIt.Next() )
+    {
+      aBB.Add( anUpdatedCompound, aShapeIt.Value() );
+    }
+
+    // Store the updated shape as an output
+    theUpdatedShape = anUpdatedCompound;
+
+    // Use topological naming services to store the updated shape in XDE
+    TNaming_Builder NB(theItemLabel);
+    NB.Generated(theUpdatedShape);
+  }
+
+  return isModified;
+}
index bc7ddd8..204cb52 100644 (file)
@@ -286,8 +286,8 @@ public:
   //! Removes a component from its assembly
   Standard_EXPORT void RemoveComponent (const TDF_Label& comp) const;
   
-  //! Update an assembly at label <L>
-  Standard_EXPORT void UpdateAssembly (const TDF_Label& L) const;
+  //! Top-down update for all assembly compounds stored in the document.
+  Standard_EXPORT void UpdateAssemblies();
   
   //! Finds a label for subshape <sub> of shape stored on
   //! label shapeL
@@ -421,7 +421,12 @@ protected:
 
 private:
 
-  
+  //! Checks recursively if the given assembly item is modified. If so, its
+  //! associated compound is updated. Returns true if the assembly item is
+  //! modified, false -- otherwise.
+  Standard_EXPORT Standard_Boolean updateComponent(const TDF_Label& theAssmLabel,
+                                                   TopoDS_Shape&    theUpdatedShape) const;
+
   //! Adds a new top-level (creates and returns a new label)
   //! For internal use. Used by public method AddShape.
   Standard_EXPORT TDF_Label addShape (const TopoDS_Shape& S, const Standard_Boolean makeAssembly = Standard_True);
index 20bfb0c..51e5355 100644 (file)
@@ -815,6 +815,31 @@ static Standard_Integer setStyledComponent (Draw_Interpretor& di, Standard_Integ
   
   return 0;
 }
+
+static Standard_Integer updateAssemblies(Draw_Interpretor& di, Standard_Integer argc, const char** argv)
+{
+  if (argc != 2)
+  {
+    di << "Use: " << argv[0] << " Doc\n";
+    return 1;
+  }
+
+  // Get XDE document
+  Handle(TDocStd_Document) aDoc;
+  DDocStd::GetDocument(argv[1], aDoc);
+  if ( aDoc.IsNull() )
+    return 1;
+
+  // Get XDE shape tool
+  Handle(XCAFDoc_ShapeTool)
+    aShapeTool = XCAFDoc_DocumentTool::ShapeTool( aDoc->Main() );
+
+  // Update assemblies
+  aShapeTool->UpdateAssemblies();
+
+  return 0;
+}
+
 //=======================================================================
 //function : InitCommands
 //purpose  : 
@@ -913,4 +938,6 @@ void XDEDRAW_Shapes::InitCommands(Draw_Interpretor& di)
   di.Add ("XSetInstanceSHUO","Doc shape \t: sets the SHUO structure for indicated component",
                    __FILE__, setStyledComponent, g);
   
+  di.Add ("XUpdateAssemblies","Doc \t: updates assembly compounds",
+                   __FILE__, updateAssemblies, g);
 }
index 8d5fe26..2fbfe70 100644 (file)
@@ -7,7 +7,7 @@ if { ( [regexp {Compiler: MS Visual C[+][+] ([0-9]+)} $dver res ver] && $ver < 1
      ( [regexp {Compiler: GCC ([0-9]+[.][0-9]+)} $dver res ver] && $ver < 4.8 ) } {
   puts "TODO OCC28217 ALL: TEST INCOMPLETE"
   puts "TODO OCC28217 Windows: An exception was caught"
-  puts "TODO OCC28217 Windows: \\*\\* Exception \\*\\*.*"
+  puts "TODO OCC28217 Windows: \\*\\* Exception \\*\\*"
   puts "TODO ?OCC28217 Linux: An exception was caught"
   puts "TODO ?OCC28217 Linux: \\*\\* Exception \\*\\*.*"
   puts "TODO ?OCC28217 Linux: \\*\\*\\* Abort"
diff --git a/tests/bugs/xde/bug28055_1 b/tests/bugs/xde/bug28055_1
new file mode 100644 (file)
index 0000000..223f12e
--- /dev/null
@@ -0,0 +1,29 @@
+puts "============"
+puts "CR28055"
+puts "============"
+puts ""
+
+##########################################################################
+# Add UpdateAssemblies() method for top-down update of assembly compounds
+##########################################################################
+
+ReadStep d [locate_data_file trj3_as1-tc-214.stp]
+XShow d
+vfit
+vsetdispmode 1
+
+# First check verifies that originally there is 18 solid instances
+XGetOneShape model_before d
+checknbshapes model_before -solid 18 -t
+
+checkview -screenshot -3d -path ${imagedir}/${::casename}_1.png
+
+XRemoveComponent d 0:1:1:1:1
+XUpdateAssemblies d
+XShow d
+
+checkview -screenshot -3d -path ${imagedir}/${::casename}_2.png
+
+# Second check verifies that after component removal 7 instances go away
+XGetOneShape model_after d
+checknbshapes model_after -solid 11 -t
diff --git a/tests/bugs/xde/bug28055_2 b/tests/bugs/xde/bug28055_2
new file mode 100644 (file)
index 0000000..4879b22
--- /dev/null
@@ -0,0 +1,60 @@
+pload QAcommands
+
+puts "============"
+puts "CR28055"
+puts "============"
+puts ""
+
+##########################################################################
+# Add UpdateAssemblies() method for top-down update of assembly compounds
+##########################################################################
+
+##########################################################################
+proc IsSame {s1 s2} {
+  global $s1 $s2
+  if {[IsSameShapes $s1 $s2] == 1} {return 1}
+  return 0
+}
+##########################################################################
+
+ReadStep d [locate_data_file trj3_as1-tc-214.stp]
+XShow d
+vfit
+vsetdispmode 1
+
+# Explode on solids for comparison
+XGetOneShape model1 d
+explode model1 So
+
+checkview -screenshot -3d -path ${imagedir}/${::casename}_1.png
+
+# Update part's geometry
+box box_nut -2 -2 0 22 18 8;
+XSetShape d 0:1:1:5 box_nut
+XUpdateAssemblies d
+XShow d
+
+# Explode on solids for comparison
+XGetOneShape model2 d
+explode model2 So
+
+# 0 means "different", 1 means "same"
+set same_mask {1 0 1 0 1 0 1 1 1 0 1 0 1 0 1 1 0 0}; list
+set n [llength $same_mask]
+
+# Compare subshapes (exploded compounds) with respect to the reference mask
+set i 1
+foreach same_flag $same_mask {
+  set same [IsSame model1_$i model2_$i]
+  puts "Comparison result for subshape $i: $same"
+  if {$same!=$same_flag} {
+    if {$same_flag==1} {
+      puts "Error: shapes are expected to be identical"
+    } else {
+      puts "Error: shapes are expected to be different"
+    }
+  }
+  incr i
+}
+
+checkview -screenshot -3d -path ${imagedir}/${::casename}_2.png