Generating Bezier/BSpline Surface of STEP file.

Hi all,

 

I have been slowly learning how to do some things with OpenCASCADE, but it is still a large library and I have difficulty finding how to do things properly.

Currently, I can read STEP files, and explore the different shapes, using a STEPCAFControl_Reader.

 

What I really need (and have trouble figuring out), is to take the contents of a STEP file, and "convert" it to bezier or bspline patches.

Ideally, I would love to simply generate an array of patches for a given compound or other shape types.

When I look at the planes, cylinders, ... (i.e. types of TopAbs_FACE), they are not cut/trimmed, but it is crucial for me that the patches from the needed conversion incorporate all of this.

 

As a super simple example, I created a plate with a hole in FreeCAD and exported to STEP (see simpleObjectInFreeCAD.png).

When I read this file, the large faces naturally have no hole, since they are "just" Geom_Planes.

I would like to have a way of splitting the entire thing into patches. Even if the face is just a quad. For example, I imagine my simple example might be split as my expert drawing skills indicate in the attached examplePatches.png, although the way surfaces are split is not very important to me right now.

What is important, is that I can get Bezier/BSpline surfaces, and not polygonal meshes.

 

I have tried a number of things, such as GeomConvert::SurfaceToBSplineSurface(), BRepBuilderAPI_NurbsConvert, but I keep running into problems (plenty crashes).

 

Is there a good approach for this? Let's say, I have a Handle(TopoDS_Shape), which is a TopAbs_COMPOUND.

Nathaniel Essenberg's picture

Okay, so I think what I should do is this:

Use a TopExp_Explorer on the Compound to iterate over all faces (TopAbs_Face):

TopExp_Explorer exp;
for (exp.Init(shape, TopAbs_FACE); exp.More(); exp.Next())
{
	ProcessSingleFace(exp.Current());
}

Then use BRepBuilderAPI_MakeFace:

void ProcessSingleFace(const TopoDS_Shape& shape)
{
	if (!shape.IsNull())
	{
		TopoDS_Face face = TopoDS::Face(shape);

		TopLoc_Location loc;
		Handle(Geom_Surface) aSurf = BRep_Tool::Surface(face, loc);
		aSurf = Handle(Geom_Surface) { static_cast<Geom_Surface*>(aSurf->Transformed(loc.Transformation()).get()) };

		Standard_Real u0, u1, v0, v1;
		BRepTools::UVBounds(face, u0, u1, v0, v1);

		if (!aSurf.IsNull())
		{
			// Make face. Result is NURBS surface(s) (right?)
			TopoDS_Wire outerWire = BRepTools::OuterWire(face);

			BRepBuilderAPI_MakeFace faceMaker{face, outerWire};
			
			// Add other wires
			TopExp_Explorer exp;
			for (exp.Init(shape, TopAbs_WIRE); exp.More(); exp.Next())
			{
				TopoDS_Wire wire = TopoDS::Wire(exp.Current());
				if(wire != outerWire)
				{
					faceMaker.Add(wire);
				}
			}

			const TopoDS_Shape& result = faceMaker.Shape();

			if(result.IsNull())
			{
				return;
			}

			Handle(Geom_Surface) surf = BRep_Tool::Surface(TopoDS::Face(result));
			Handle(Geom_BSplineSurface) bsSurf { static_cast<Geom_BSplineSurface*>(surf.get()) };

			if(bsSurf.IsNull())
			{
				return;
			}

			int uKnots = bsSurf->NbUKnots(); // -> CRASH
		}
	}
}

The problem is, that when I try to access the bsSurf after it gets created, my application crashes.

For example, even some simple like bsSurf->NbUKnots() already crashes my application. I haven't set breakpoints to check the variables, because I'm compiling this to a DLL which I use indirectly on another thread, and I don't really want to set that up if I don't need to.

Is there anything I'm doing wrong here? Or misunderstand?

Nathaniel Essenberg's picture

I just set up debugging anyway, and I think I cannot assume that BRepBuilderAPI_MakeFace::Shape() returns a BSplineSurface?

When I look at the bsSurf variable, the first thing I notice is a mention of CylindricalSurface. Which makes me think it is not actually a BSplineSurface.

Nathaniel Essenberg's picture

Well. I can make it stop crashing now:

// ... Use Make Face

TopoDS_Shape result = faceMaker.Shape();

BRepBuilderAPI_NurbsConvert nc{};
nc.Perform(result);
result = nc.Shape();
				
if (result.IsNull())
{
	return;
}

for (exp.Init(result, TopAbs_FACE); exp.More(); exp.Next())
{
	face = TopoDS::Face(exp.Current());

	if (face.IsNull())
	{
		return;
	}

	Handle(Geom_Surface) surf = BRep_Tool::Surface(face);

	if (surf.IsNull())
	{
		return;
	}

	Handle(Geom_BSplineSurface) bsSurf { static_cast<Geom_BSplineSurface*>(surf.get()) };
	
	// Deal with surface. WORKS
}

Unfortunately, the surface do not use the wires like I need. They are still the full surfaces...

Does the Make Face object take these wires into account at all?

Benjamin Bihler's picture

With

surf->DynamicType()->Name()

you can check what the actual type is.

Benjamin

Nathaniel Essenberg's picture

Thank you :).

The thing that still eludes me is how to create (Bezier/NURBS) faces so that they incorporate the holes and contours correctly. I know the information is available. I can get the curves, and if I triangulate the shape, it is also correct. Only I don't want a triangulation.

Benjamin Bihler's picture

I am not very much familiar with shape creation. Still at a first glance your face creation code looks correct. But you should be aware of the difference between the topology (a TopoDS_Face that may possibly contain holes) and the geometry (like a Geom_Surface that models the parametrization and will to my knowledge never contain holes). See http://opencascade.blogspot.de/2009/02/topology-and-geometry-in-open-cas.... So I guess you should visualize the face and check there whether the holes are present.

Nathaniel Essenberg's picture

Ah that is interesting. I am not yet very clear yet on the differences between Shape, Topo, Geom, etcetera.  I will spend some more time reading that blog and the documentation.

Thanks

Actually, I think I noticed that for a cylinder, the uvs of the Geom_CylindricalSurface were always [0,2pi] and [-inf,inf], while the uvs of the TopoDS_Face for this cylinder were correct.

Nathaniel Essenberg's picture

So the Face contains the wires, and the Geometry is/contains a somewhat general surface of a certain type.

But do I really need to manually trim/clip the surface? I hoped OpenCASCADE would be able to do that. Because it is also able to create a triangulated mesh for the Face through BRepMesh_IncrementalMesh.

For example: suppose you're writing a plugin for Maya, and you want to import .stp files, and create nurbs surfaces from it in Maya. You would have to somehow create the nurbs surfaces, either by using OpenCASCADE functions for a given TopoDS_Face (or better yet, for any TopoDS_Shape), or by doing it manually.

One way or another, I will be exploring OCCT in the next months, but it's actually pretty important for me to just get this nurbs-ification working asap, by any means.

Benjamin Bihler's picture

Even if you have a B-spline surface (Maybe you have already? Have you checked the dynamic type?), there will be different aspects. The topological aspects (like boundaries) have to be cared for with classes working on TopoDS_... objects and the pure geometrical aspects have to cared for with classes working on Geom_... objects.

Two examples:

If you want to know whether a point is on your B-spline surface or not (taking holes into account), you can use BRepTopAdaptor_FClass2d::Perform(...) after initializing it with the face.

The 2d-coordinates you can get by projecting onto the manifold with ShapeAnalysis_Surface::ValueOfUV(...). But if you execute ShapeAnalysis_Surface::Value() to get a 3d-point from 2d-coordinates you might also get points that are within holes of your face, since this method will not take that additional information (topology!) into consideration.

Benjamin

Roman Lygin's picture

Responded on the blog https://opencascade.blogspot.nl/2009/02/continued.html.

The image is enclosed here to (hopefully) ease understanding.

Attachments: 
Nathaniel Essenberg's picture

Just to close this and inform others how this ended up (in case others are trying the same thing). My conversation with Roman can be read in the comments here: https://opencascade.blogspot.nl/2009/02/continued.html

Roman has been very helpful and explained a lot to me which makes me understand topology/geometry and brep a lot better. He also confirmed that OCC will not help me with splitting a complex face into "regular" nurbs patches (please correct me if I understood it wrong).

Breaking down a brep face into simple nurbs patches now seems to me not a trivial task at all in general, and I'm not sure if any software offers that kind of functionality right now.

For my own project, I'm going to try some more "creative" solutions that I have in mind :P.

Thanks Roman, and also Benjamin for the help :). It's really appreciated because this project I'm working on might actually be pretty important for us over here

Roman Lygin's picture

Hi Nathaniel,

Thanks for your wrapping up. Good luck with your next steps!

Thanks Ben for pointing to the blog, good to know it was useful.

Kind regards,

Roman