2010-03-31

Mixing run-times

A brief post not related to Surface modeling but still hopefully useful...

Last week-end I was testing a first version of the X3D converter to be added to CAD Exchanger. X3D stands for eXtensible 3D format (ISO19975) which could be seen as a successor of VRML format. Check http://www.web3d.org for more details.

The persistent representation uses XML as a file format, and I use Open CASCADE XML formatter for that. Once the XML document has been created, it is fed into LDOM_XmlWriter (part of Open CASCADE) which dumps contents to the file. LDOM_XmlWriter accepts FILE* as a file object.

Now beings the problem. When OCC is compiled with vc7.1 (Visual Studio 2003) and the X3D converter – with vc8 (VS2005), the application crashes during output to an XML file (inside the fwrite() function). I was trying to use VS2003 for OCC binaries as some CAD Exchanger SDK users preferred that. The same problem shows up if OCC binaries are compiled with VS2005 and the X3D converter is compiled with VS2008.

The root cause of the crash is that a FILE pointer is checked against pre-defined constant pointers to file objects (including stdout, stderr, etc) inside the run-time library. And when this pointer has been created in one run-time (vc8) and is checked inside the other (vc7.1), a different code branch is executed eventually leading to null pointer access and hence crash.

Thus, interoperability between run-times is really very limited and you should do as much as you can to avoid mixing different run-times in one application. In particular, if you are using open source software (Open CASCADE and others), make sure you rebuild them with the same run-time. If you are distributing closed source, make sure you prebuild binaries for multiple run-times.

Hope this helps if you ever encounter with such an issue, or better yet - *before* you encounter ;-).

2010-03-02

Surface modeling. Part6

(continued...)

Plating
This is one of the advanced techniques of surface modeling that Open CASCADE offers. Other names for it are hole filling, or constrained filling. ACIS calls it covering.

It involves creation of a surface that meets several constraints:
- point constraints – the surface must pass through given points;
- point constraints and tangency, and (optionally) curvature – the same as above + extra Gn requirements with respect to another surface;
- curve constrains – the surface must pass through given curves;
- curve with tangency constraint – the same + extra Gn requirements.

The latter type of constraint is used to produce class A surfaces, G1 tangent to adjacent surfaces. This is important, for instance, in auto industry when designing nice-looking car bodies, to ensure correct smooth refraction of light to avoid sharp edges.

Constraints can be mixed (e.g. 3 curve constraints, 1 curve with tangency constraint, 2 point constraints).

The main algorithm to construct plate surface is GeomPlate_BuildPlateSurface. I use it in CAD Exchanger to translate vertex_blend ACIS surfaces as well as net surfaces. The former is supposed to result from vertex blending and producing a surface G1-continous to neighbours. The image below shows one:



Net surface in ACIS is defined through two sets of curves going in approximately perpendicular directions and forming a grid-like structure. So the resulting surface is supposed to cover that grid as if a roof covered roof-timbers. The image below illustrates this:



As usual, here is an excerpt from the CAD Exchanger code:

/*! The objects in \a theBoundaries must be of the type Adaptor3d_HCurveOnSurface or
GeomAdaptor_HCurve indicating type of a constraint. Otherwise an exception
Standard_TypeMismatch is thrown.

If the \a theBoundaries list is empty then Standard_ConstructionError is thrown.

If the algorithm fails returns a null surface.
*/
Handle(Geom_Surface) ACISAlgo::MakeSurface (const TColStd_ListOfTransient& theBoundaries,
const Standard_Real theTol,
const Standard_Integer theNbPnts,
const Standard_Integer theNbIter,
const Standard_Integer theMaxDeg)
{
//constants for algorithm
const Standard_Integer aNbIter = theNbIter; //number of algorithm iterations
const Standard_Integer aNbPnts = theNbPnts; //sample points per each constraint
const Standard_Integer aDeg = 3; //requested surface degree ?
const Standard_Integer aMaxDeg = theMaxDeg;
const Standard_Integer aMaxSeg = 10000;
const Standard_Real aTol3d = 1.e-04;
const Standard_Real aTol2d = 1.e-05;
const Standard_Real anAngTol = 1.e-02; //angular
const Standard_Real aCurvTol = 1.e-01; //curvature

Handle(Geom_Surface) aRes;
GeomPlate_BuildPlateSurface aPlateBuilder (aDeg, aNbPnts, aNbIter, aTol2d, aTol3d,
anAngTol, aCurvTol);

TColStd_ListIteratorOfListOfTransient anIt (theBoundaries);
if (anIt.More()) {
int i = 1;
for (; anIt.More(); anIt.Next(), i++) {
const Handle(Standard_Transient)& aCur = anIt.Value();
if (aCur.IsNull()) {
assert (0);
Standard_ConstructionError::Raise ("ACISAlgo::MakeSurface()");
} else if (aCur->IsKind (STANDARD_TYPE (Adaptor3d_HCurveOnSurface))) {
//G1 constraint
const Handle(Adaptor3d_HCurveOnSurface)& aHCOS =
Handle(Adaptor3d_HCurveOnSurface)::DownCast (aCur);
Handle (GeomPlate_CurveConstraint) aConst =
new GeomPlate_CurveConstraint (aHCOS, 1 /*GeomAbs_G1*/,
aNbPnts, aTol3d, anAngTol, aCurvTol);
aPlateBuilder.Add (aConst);
} else if (aCur->IsKind (STANDARD_TYPE (GeomAdaptor_HCurve))) {
//G0 constraint
const Handle(GeomAdaptor_HCurve)& aHC =
Handle(GeomAdaptor_HCurve)::DownCast (aCur);
Handle (GeomPlate_CurveConstraint) aConst =
new GeomPlate_CurveConstraint (aHC, 0 /*GeomAbs_G0*/, aNbPnts, aTol3d);
aPlateBuilder.Add (aConst);
} else {
Standard_TypeMismatch::Raise ("ACISAlgo::MakeSurface()");
}
}
} else {
Standard_ConstructionError::Raise ("ACISAlgo::MakeSurface()");
}

//construct
aPlateBuilder.Perform();

if (!aPlateBuilder.IsDone()) {
return aRes;
}

const Handle(GeomPlate_Surface)& aPlate = aPlateBuilder.Surface();
//approximation (see BRepFill_Filling - when no initial surface was given)
Standard_Real aDMax = aPlateBuilder.G0Error();
TColgp_SequenceOfXY aS2d;
TColgp_SequenceOfXYZ aS3d;
aPlateBuilder.Disc2dContour (4, aS2d);
aPlateBuilder.Disc3dContour (4, 0, aS3d);
Standard_Real aMax = Max (aTol3d, 10. * aDMax);
GeomPlate_PlateG0Criterion aCriterion (aS2d, aS3d, aMax);
{
//data races in AdvApp2Var used by GeomApprox_Surface, use global mutex
Standard_Mutex::Sentry aSentry (theBSMutex);
GeomPlate_MakeApprox aMakeApprox (aPlate, aCriterion, aTol3d, aMaxSeg, aMaxDeg);
aRes = aMakeApprox.Surface();
}
return aRes;
}


The code above deals with curve or curve-with-tangency constraints only but you could add point constraints in the same way.

The algorithm starts with creation of initial approximation which you can either specify or it will create a plane otherwise. I have not come across a need of pre-specifying an initial surface but perhaps that would give some hints to the algorithm in complex cases. If there is anyone with such experience, it would be interesting to hear.

To be continued...

P.S. Writing this post in a hotel on the US west coast, Hillsboro, Oregon. The nature here is the greatest I have ever seen. Always enjoying when returning to Oregon...