How to rotate or translate assemblies

mmaenz

Looking around for some CAD
Hi,

I'm having a really hard time rotating or translating assemblies. I have a bunch of step files and need to have them equally aligned. I'm loading the document with opencascade STEPCAFControl_Reader. To understand the overall principle I watched youtube (Quaoar - Lesson12&15) several times:
xde-structure.png

In simple cases where there is only one TopoDS_Solid as root node I do a BRepBuilderAPI_Transform on the shape and save it back. I'm not sure but I think this recalculates the whole geometry. This is not viable if the shape is referenced from multiple instances. The instance (wheelaxle-ref) has a TopLoc_Location attribute with its world-location in parents local location (child-parent-location is build up in traversal):
instance-location.png
(Had to use TKInspector as AS asm-xde-dfbrowse is not showing Transformation)

As a example to understand how it could work I only try to change 1 wheelaxle-ref to see if it works!

In my project source I do the following to get the TopLoc_Location of TDF_Label after loading and writing it back to label:
C++:
void rotateByLabel(double angle, Handle(XCAFDoc_ShapeTool) aShapeTool) {
    TDF_LabelSequence rootLabels;
    aShapeTool->GetFreeShapes(rootLabels); // <-- retuns 1 chassis
    for (int index = 1; index <= rootLabels.Length(); index++) {
        for (TDF_ChildIterator itr(rootLabels.Value(index)); itr.More(); itr.Next()) { // has 2 items
            TDF_Label label = itr.Value(); // returns wheelaxle-ref
            gp_Trsf aTrsf = aShapeTool->GetLocation(label).Transformation();  
            // ... Do something with loc / transformation
            aTrsf .SetTranslationPart(gp_Vec(0, 0, 50));
            XCAFDoc_Location::Set(label, TopLoc_Location(aTrsf));
            break; // Only one axle to see difference!
        }
    }
    aShapeTool->UpdateAssemblies();
}

This does work in viewer when running in lesson15_compose-XDE code as base but the exported STEP has no location showing same result. It seems only to work in direct viewer when running from memory. Also the Label gets a new name / tag:
locationSetByLabel.png
TKInspector-locationSetByLabel.png

I also tried to rotate with using TopoDS_Shape:
C++:
void rotateByShape(double angle, Handle(XCAFDoc_ShapeTool) aShapeTool) {
    TDF_LabelSequence rootLabels;
    aShapeTool->GetFreeShapes(rootLabels); // <-- retuns 1 chassis
    for (int index = 1; index <= rootLabels.Length(); index++) {
        for (TDF_ChildIterator itr(rootLabels.Value(index)); itr.More(); itr.Next()) { // has 2 items
            TDF_Label label = itr.Value(); // returns wheelaxle-ref
            TopoDS_Shape shape = aShapeTool->GetShape(label);
            gp_Trsf aTrsf = aShapeTool->GetLocation(label).Transformation();
            // ... Do something with loc / transformation
            aTrsf .SetTranslationPart(gp_Vec(0, 0, 50));
            shape.Location(TopLoc_Location(aTrsf)); // Also tried shape.Move(aTrsf);
            // Possibly set Shape back??
            // aShapeTool->SetShape(label, shape);
            break; // Only one axle to see difference!
        }
    }
    aShapeTool->UpdateAssemblies();
}
But this never worked.

I did have a look at OpenCascade CAD-Builder. It does rotation but it seems to create a new rotated Shape and all Information like color and so on are missing. It even renames:
CADBuilder.png

Now I'm totally confused. So these are my questions:
  • Is it possible to rotate or translate a instance/assembly (not the referenced shape itsself) without copying?
  • Or how to (re)set the location transformation to a new value?
  • Can root-labels (e.g. FreeShapes) have a location?

Please let me know if you need any more information for closer examination!!

Thank you very much! I very much appreciate the youtube videos of quaoar and your amount of work of Analysis Situs as I'm regularly having a peek into it's sources! Keep up the great work.

-Michael
 
Last edited:

JSlyadne

Administrator
Staff member
Hello Michael,

It seems the procedure of writing XDE document to Step ignores location attributes. To have the parts properly transformed in Step, you need to update the shapes stored on labels. This means get a shape from the label, apply transformation to it and pass back to the document. Thus, for your case, the trsf should be applied to the compounds stored on "wheel-axle" labels. Actually, you were near to true trying with the second approach you posted. But the tricky point is how to update the shape in the document structure. And this is where UpdateAssemblies() comes up - it checks whether the document structure was changed, and if so, it updates the shapes sitting on labels. Why it didn't work for you that was the question. It seems there is a bug or unimplemented logic to treat the changing of locations as well. If you follow the code step-by-step in Debug mode, you'll see that "isModified" flag is always false for your case. And if switch it to true forcibly, the magic will happen.

As a workaround you might want to reuse the code of UpdateAssemblies() at the side of your application and call it whenever you need.
 

mmaenz

Looking around for some CAD
Thank you so much for your work!

So much people are using OpenCascade and I can't imagine no one ever faced this problem. I had a really hard time searching forums and the internet. I thought I had a wrong understanding of how OpenCascade does things.

I also did some more research (I never had the idea to debug OpenCascade itsself) and came up with another solution to force updating of the document (without knowing that isModified = false is the reason):
C++:
void rotateByComponent(double angle, Handle(XCAFDoc_ShapeTool) aShapeTool) {
    TDF_LabelSequence rootLabels;
    aShapeTool->GetFreeShapes(rootLabels); // <-- retuns 1 chassis
    for (int index = 1; index <= rootLabels.Length(); index++) {
        for (TDF_ChildIterator itr(rootLabels.Value(index)); itr.More(); itr.Next()) { // has 2 items
            // I know this code is in no way complete nor makes any checks
            TDF_Label child = cit.Value(), ref;
            gp_Trsf m = ShapeTool->GetLocation(child);
            m.SetRotation(gp::OZ(),  90. * M_PI / 180.);
            ShapeTool->GetReferredShape(child, ref);
            Handle(TDataStd_Name) nameAttr;
            child.FindAttribute(TDataStd_Name::GetID(), nameAttr);
            TDF_Label result = ShapeTool->AddComponent(chassisPrototype.label, ref, TopLoc_Location(m));
            ShapeTool->UpdateAssemblies();
            ShapeTool->RemoveComponent(child);
            ShapeTool->UpdateAssemblies();
            TDataStd_Name::Set(result, nameAttr->Get());
            break;
        }
    }
}

It preserves colors and all information as it reuses the topLevel wheelAxlePrototype, makes isModified set to true and forces updating the graph:
isModifiedSetTrue.png

I'm not sure about the overhead of repeatedly calling UpdateAssemblies() but for now it works.
rotatedaxle.png

Thank you all and best regards!
-Michael
 

Quaoar

Administrator
Staff member
So much people are using OpenCascade and I can't imagine no one ever faced this problem.
Not so many people use XDE. Most of them, like @blobfish, prefer using OpenCascade for pure geometry while implementing additional data structures from scratch. I think you now understand why :)

Your approach is quite okay, I'd say. Btw, what if you get rid of the first invocation of UpdateAssemblies() before you remove the component? Is it really necessary to update assemblies there? Other than that, updating compounds is very fast. I remember testing it on a quite large assembly, and its overhead was negligible.
 

mmaenz

Looking around for some CAD
It only sets isModified to true when there is a difference between the count of label-childs that are components (!) and children in the actual TopoDS_Shape. If I only do UpdateAssemblies() once after RemoveComponent() then there is equal count again and it won't work. When doing after adding it recognizes 3 component labels but only 2 in its internal TopoDS_Shape hierarchy and starts updating. Afterwards I need to remove and update again to get the required actual result.

I thought about using a dummy component each time, adding to all root-shapes only to trigger updating and then removing but that seems to be even more work. If updating is performant enough for now I only need to look for another solution if it is getting a bottleneck.
 
Last edited:

mmaenz

Looking around for some CAD
Sometimes its a long way to go.

I do now use your approach with a custom and forced updateComponent() after translating/rotating.
My way with Add/RemoveComponent() adds everytime a new Label to the assembly that I can't get rid off. I didn't notice that in the example/debug application. I recognized it first in my main project after getting TDF_Label is null exceptions and when finally opening the rotated step in TKInspector.

Thank your for the link to your commit!

Best regards
-Michael
 

Quaoar

Administrator
Staff member
My way with Add/RemoveComponent() adds everytime a new Label to the assembly that I can't get rid off.
Labels cannot be removed in an OCAF document unless it is saved to a file and opened again. Such behavior was implemented for a purpose to keep an anchor for undo/redo, but that's also a good source of memory leaks.
 
Top