Correct location of parts after a STEP import

Hello everyone,

I have been struggling with a very basic task for quite a while. I would like to import a STEP file such as the one from here https://github.com/stepcode/stepcode/blob/master/data/ap214e3/as1-oc-214.stp, triangulate each of its parts and save it in a separate STL file.
Minimal (not really) Working Example:

Handle(TDocStd_Document) doc;
Handle(XCAFApp_Application) anApp = XCAFApp_Application::GetApplication();
anApp->NewDocument(filename,doc);

STEPCAFControl_Reader reader;
reader.ReadFile(filename);
reader.Transfer(doc);

Handle(XCAFDoc_ShapeTool) myAssembly = XCAFDoc_DocumentTool::ShapeTool(doc->Main());

TDF_LabelSequence FreeShapes;
myAssembly->GetFreeShapes(FreeShapes);
TDF_LabelSequence shapes;
myAssembly->GetComponents(FreeShapes.Value(1),shapes,true);

for(Standard_Integer i=1; i<=shapes.Length(); ++i)
{
	TDF_Label label = shapes.Value(i);
	TopoDS_Shape shape;
	myAssembly->GetShape(label,shape);

	TopAbs_ShapeEnum type = shape.ShapeType();
	if(type!=0)
	{
		BRepMesh_IncrementalMesh BMesh(shape,0.1,Standard_True);
		StlAPI_Writer STLwriter;
// For the sake of the MWE simply i.stl.
		std::stringstream stream;
		stream << i;
		std::string filename;
		stream >> filename;
		filename += ".stl";
		STLwriter.Write(shape,filename.c_str());
	}
}

However, if I open the meshes in Meshlab, the parts are not placed correctly, see the enclosed printscreen. As far as I understand, in the STEP file the parts are created in some initial position and then copied to various places. How do I move them to where they should be? Certainly I am missing something very basic. Many thanks!

Attachments: 
Patrik Mueller's picture

Hi Dominik,

you could get the locations from XCAFDoc_ShapeTool:

myAssembly->GetLocation(theLabel);

and multiply the Shape with the location.

Greets,

Patrik

Dominik Mokris's picture

Dear Patrik,

thank you very much. I have tried this already before asking and, unfortunately, it had not really worked out.
Updated code:

Handle(TDocStd_Document) doc;
Handle(XCAFApp_Application) anApp = XCAFApp_Application::GetApplication();
anApp->NewDocument(filename,doc);

STEPCAFControl_Reader reader;
reader.ReadFile(filename);
reader.Transfer(doc);

Handle(XCAFDoc_ShapeTool) myAssembly = XCAFDoc_DocumentTool::ShapeTool(doc->Main());

TDF_LabelSequence FreeShapes;
myAssembly->GetFreeShapes(FreeShapes);
TDF_LabelSequence shapes;
myAssembly->GetComponents(FreeShapes.Value(1),shapes,true);

for(Standard_Integer i=1; i<=shapes.Length(); ++i)
{
	TDF_Label label = shapes.Value(i);
	TopLoc_Location location = myAssembly->GetLocation(label);
	gp_Trsf transformation = location.Transformation();

	TopoDS_Shape shape;
	myAssembly->GetShape(label,shape);

	TopAbs_ShapeEnum type = shape.ShapeType();
	if(type!=0)
	{
		BRepBuilderAPI_Transform xform(shape, transformation);
		shape = xform.Shape();

		BRepMesh_IncrementalMesh BMesh(shape,0.1,Standard_True);
		StlAPI_Writer STLwriter;
		std::stringstream stream;
		stream << i;
		std::string filename;
		stream >> filename;
		filename += ".stl";
		STLwriter.Write(shape,filename.c_str());
	}
}

The positions change, but not really the way I would like them to, see the new printscreen.
I have even tried inverting the transformation with transformation.Invert(), without success.

Thanks for hints!

Attachments: 
Patrik Mueller's picture

Hi Dominik,

don't forget the checking for references, they also can have transformations!

Standard_Boolean isRef = myAssembly->GetReferredShape(theLabel, refLabel);

Greets,

Patrik

Dominik Mokris's picture

Hi Peter,
thanks, I did not think of this! Do I just multiply the transformations then or is there something more elaborate to do?
When I multiply them like this:

Handle(TDocStd_Document) doc;
Handle(XCAFApp_Application) anApp = XCAFApp_Application::GetApplication();
anApp->NewDocument(filename,doc);

STEPCAFControl_Reader reader;
reader.ReadFile(filename);
reader.Transfer(doc);

Handle(XCAFDoc_ShapeTool) myAssembly = XCAFDoc_DocumentTool::ShapeTool(doc->Main());

TDF_LabelSequence FreeShapes;
myAssembly->GetFreeShapes(FreeShapes);
TDF_LabelSequence shapes;
myAssembly->GetComponents(FreeShapes.Value(1),shapes,true);

for(Standard_Integer i=1; i<=shapes.Length(); ++i)
{
	TDF_Label label = shapes.Value(i);
	TopLoc_Location location = myAssembly->GetLocation(label);
	gp_Trsf transformation = location.Transformation();

// The new part:
	TDF_Label refLabel;
	if(myAssembly->GetReferredShape(label, refLabel))
	{
		TopLoc_Location refLocation=myAssembly->GetLocation(refLabel);
		gp_Trsf refTransformation=location.Transformation();
		transformation=refTransformation * transformation;
	}

// The same as before:
	TopoDS_Shape shape;
	myAssembly->GetShape(label,shape);

	TopAbs_ShapeEnum type = shape.ShapeType();
	if(type!=0)
	{
		BRepBuilderAPI_Transform xform(shape, transformation);
		shape = xform.Shape();
		BRepMesh_IncrementalMesh BMesh(shape,0.1,Standard_True);
		StlAPI_Writer STLwriter;
		std::stringstream stream;
		stream << i;
		std::string filename;
		stream >> filename;
		filename += ".stl";
		STLwriter.Write(shape,filename.c_str());
	}
}

they are still not correctly positioned (printscreen attached). I have also tried changing the order of the transformations and --- somewhat surprisingly --- the result looks the same (shouldn't they be non-commutative?).

Thanks for suggestions!

Attachments: 
Patrik Mueller's picture

Hmm,

I also solve assemblies using their transformations, to get the whole structure.

But another idea:

your sample file is the same as in the XDE documentation. You have references and referenced assemblies (see the XDE screenshots)

BRepBuilderAPI_Transform

would modify the transformation for the original (referenced) shape.

Have you tried to copy the shape inside:

BRepBuilderAPI_Transform xform(shape, transformation, Standard_True);

?

Greets,

Patrik

Dominik Mokris's picture

Dear Patrik,
thank you for your hint and sorry for my late reply.

I tried adding the "Standard_True" parameter to BRepBuilderAPI_Transform as you had suggested.
I did so both in my code from 11 July, 2017 19:37 and the code from 12 July, 2017 - 10:38.
Unfortunately, it does not change anything in either of the situations.

I've had a closer look at the transformations and there is a thing I do not understand.
The shapes corresponding to i=6 and i=9 both represent the same geometrical shape and they both have the same transformation, represented by the matrix
-1 0  0  2.5
0  1  0  -17.5
0  0 -1  -20
0  0  0  1
Consequently, they both end up in the same location, which is not what I want.
Multiplication by the transformation of the referred shape does not change anything about it.
I feel I am missing something rather basic ...
 

Thanks for further suggestions.

Patrik Mueller's picture

Hi Dominik,

I still don't see assemblies usage in your code.

Something like this:

    if (isAssembly)
    {
        TDF_LabelSequence shapeLabels;
        pShapeTool->GetComponents(realLabel, shapeLabels, Standard_False);
        Standard_Integer nbShapes = shapeLabels.Length();
        for (Standard_Integer i = 1; i <= nbShapes; i++)
        {
            const TDF_Label& childLabel = shapeLabels.Value(i);
            if (m_xdeUtils.GetShapeTool()->IsFree(childLabel))
            {
                theMat = GetMaterial(childLabel);

                ReadShape(childLabel, tmpResult, theMat);
            }

        }
        if (tmpResult->NbChildren() > 0)
            result = tmpResult;
    }

ReadShape is my method (recursive call from assemblies). You can store the transformations to a stack or something to calculate the absolute transformation. An assembly can have a transformation and the children too (and also be referenced)...

See the screenshot for the whole model structure...

Greets,

Patrik

Attachments: 
David Neu's picture

Hi Patrik,

I#m also interested in loading step files and exploring the xcaf datastructure, especially for material information. What exactly do you mean by store the transformations to a stack? Is the variable tmpResult in your example the TopoDS_Location of the current Shape? Where to you calculate and apply the transformation for each shape?

Thnank you in advance!

Best regards

Patrik Mueller's picture

Hi David,

tmpResult is an instance of my node class. Dominik wants to have absolute shapes, so he has to calculate these matrices. He can build a matrix stack for calculating the absolute matrix for each shape. It really depends on his implementation.

I use for myself a kind of scene graph and can reuse the relative matrices. I need the absolute matrices for example when doing an OBJ output.

Best regards,

Patrik