Open CASCADE notes

A blog about the Open Source 3D modeling kernel: notes from its former developer and project manager

(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...
Share
Tweet
Pin
Share
18 comments
(continued...)
Time remains the most scarce resource but I feel obliged to share some new materials with you as you may expect continuation of the series. As usual, many thanks for your patience and interest !

Skinning and lofting
This is a technique to create a model (surface or shell, or solid body) using curve constraints which the surface passes through. Below is an example of a surface built using skinning:



Skinning and lofting are actually the same technique and Open CASCADE does not make a difference between them. ACIS kernel does make a little difference (in terms of types of the inputs parameters and way of constraints definition) but also stresses that these are very similar.

Here is how you would create a surface using skinning technique:
Handle(Geom_Surface) ACISAlgo::MakeSkinSurface (
const NCollection_List<Handle(Geom_Curve)>& theSections)
{
//populate section generator
GeomFill_SectionGenerator aSecGenerator;
for (NCollection_List<Handle(Geom_Curve)>::Iterator anIt (theSections); anIt.More();
anIt.Next()) {
const Handle(Geom_Curve)& aCurve = anIt.Value();
aSecGenerator.AddCurve (aCurve);
}
aSecGenerator.Perform (Precision::PConfusion());

Handle(GeomFill_Line) aLine = new GeomFill_Line (theSections.Size());

//parameters
const Standard_Integer aMinDeg = 1, aMaxDeg = BSplCLib::MaxDegree(), aNbIt = 0;
Standard_Real aTol3d = 1e-4, aTol2d = Precision::Parametric (aTol3d);

//algorithm
GeomFill_AppSurf anAlgo (aMinDeg, aMaxDeg, aTol3d, aTol2d, aNbIt);
anAlgo.Perform (aLine, aSecGenerator);

Handle(Geom_Surface) aRes;
if (!anAlgo.IsDone()) {
return aRes;
}

aRes = new Geom_BSplineSurface(anAlgo.SurfPoles(), anAlgo.SurfWeights(),
anAlgo.SurfUKnots(), anAlgo.SurfVKnots(), anAlgo.SurfUMults(), anAlgo.SurfVMults(),
anAlgo.UDegree(), anAlgo.VDegree());

...

Curves must be bounded and consistently parameterized (so that parameter growth was in one direction). Final B-Spline will be parameterized [Umin, Umax; 0., 1.], where U parametrization is calculated depending on the curves parametrization. U parameter goes along the section curves, and V parameter goes across the curves.

When working at topological level you can use the following code:

Standard_Boolean anIsSolid = Standard_False;
Standard_Boolean anIsRuled = Standard_False;

BRepOffsetAPI_ThruSections aGenerator (anIsSolid,anIsRuled);

...
//add constraints
for (...) {
aGenerator.AddWire (TopoDS::Wire (aConstraint));
}


Standard_Boolean anIsCheck = ...;
aGenerator.CheckCompatibility (anIsCheck);

aGenerator.Build();

const TopoDS_Shape& aResult = Generator.Shape();


The topological algorithm may attempt to create a closed solid if the boundary constraints are planar.

Unlike ACIS, Open CASCADE only allows to specify curve constraints but does not give a way to specify tangential ones. So you will basically rely on underlying algorithms which try to smoothen the surface being built.

To be continued...
Share
Tweet
Pin
Share
17 comments
After individual Invitational Beta for ACIS-SAT support, public Beta Update2 is finally released.

Highlighted features: ACIS-SAT import and export (geometry and colors), Command Line mode and CAD Exchanger SDK. List of all changes is here. Go to download page to get the release.

As usual, feedback is very much appreciated and welcomed. Here in the blog, or on the forum, or directly at info@cadexchanger.com.

Thanks !



screenshot of the model imported from ACIS-SAT
Share
Tweet
Pin
Share
3 comments
(continued...)
Constructing arbitrary sweep surfaces
GeomFill algorithms considered in the previous posts internally use Approx_SweepFunction subclasses that define particular techniques to calculate cross-sections.
You can create your own subclass to model a particular sweep surface. Approx_SweepFunction has a few pure virtual methods which your subclass must redefine. For instance, in ACIS-SAT import for CAD Exchanger I use it to generate variable-radius surfaces. In ACIS, such surfaces are defined with the help of so called support surfaces (left and right), path curve, left and right radius functions, cross-section forms (circular, elliptical, chamfer, etc). Below is an example of such surface (in dark yellow) with elliptical section; support surfaces are in green and bright yellow, and path is red.



Here is an exceprt of the method that calculates a cross section at parameter along the path:

Standard_Boolean ACISAlgo_VarBlendSweepFunction::D0 (const Standard_Real theParam,
const Standard_Real,
const Standard_Real,
TColgp_Array1OfPnt& thePoles,
TColgp_Array1OfPnt2d&,
TColStd_Array1OfReal& theWeights)
{
gp_Pnt2d aLRad, aRRad;
// left radius
aLRad = myLRad->Value(theParam);
// right radius
if (myRRad == myLRad) {
aRRad = aLRad;
} else {
aRRad = myRRad->Value(R2Param);
}

gp_Pnt aPathPnt;
gp_Vec aNormal;
myPath->D1 (theParam, aPathPnt, aNormal);

const gp_Ax2 Axis (aPathPnt, aNormal);
const gp_Pln aSecPln (Axis);
Handle(Geom_Plane) aGSecPln = new Geom_Plane(Axis);

//intersection of a section plan with support surfaces
Handle(TColGeom2d_HArray1OfCurve) aLCArr, aRCArr;
if (!::Intersect (aGSecPln, aSecPln, myLSurf, aLCArr) || !::Intersect (aGSecPln, aSecPln, myRSurf, aRCArr))
return Standard_False;

//compute a section in 2D and restore it into 3D
const Standard_Integer n = thePoles.Upper();
TColgp_Array1OfPnt2d aPArr (1, n);
if (!mySec->ComputeSection (Axis, aLCArr, aLRad, aRCArr, aRRad, aPArr, theWeights))
return Standard_False;

for (Standard_Integer i = 1; i <= n; i++) {
thePoles.SetValue (i, ElCLib::To3d (Axis, aPArr (i)));
}
return Standard_True;
}

The above redefined virtual method D0() populates poles and weights arrays of the B-Spline curve which defines a cross-section.

Here is how to construct your surface using Approx_SweepFunction subclass:
...
Handle(ACISAlgo_VarBlendSweepFunction) aSweep = new ACISAlgo_VarBlendSweepFunction (...);

Approx_SweepApproximation anApprox (aSweep);
const Standard_Real aTol = 1e-4;
anApprox.Perform (aHPath->FirstParameter(), aHPath->LastParameter(), aTol, aTol,
Precision::Parametric (aTol), 1-3,
(GeomAbs_Shape)Min (GeomAbs_C1, aHPath->Continuity()), BSplCLib::MaxDegree(), aMaxSeg);
Handle(Geom_Surface) aRes;
if (anApprox.IsDone()) {
aRes = new Geom_BSplineSurface(anApprox.SurfPoles(),
anApprox.SurfWeights(), anApprox.SurfUKnots(), anApprox.SurfVKnots(),
anApprox.SurfUMults(), anApprox.SurfVMults(),
anApprox.UDegree(), anApprox.VDegree());

I guess using the above technique you can create arbitrary sweep surfaces, e.g. like the one below:




Topological algorithms
As mentioned in the first post of this series, as a rule Open CASCADE offers algorithms both at geometrical and topological level. Until now I was mainly focused on the geometrical level (just because I myself used it :-)). Let me try to give a brief mapping with a topo one:

1. Sweep of 1 curve along the other (including pipe with constant section)
a. GeomFill_Pipe (path, profile)
b. BRepOffsetAPI_Pipe (path, profile)
2. Pipe with constant radius (tube-like):
a. GeomFill_Pipe (path, radius):
b. BRepOffsetAPI_Pipe (path, profile) where profile must be pre-created
3. Pipe with constant radius with rail curves
a. GeomFill_Pipe (path, radius, rail1, rail2)
b. Unsupported ?
4. Pipe with variable radius
a. GeomFill_Sweep (path, radius_function)
b. Unsupported ?

Note that topo algorithms can use elements of diffent dimensions for profile object. This defines a type of a result. For instance, a pipe of vertex will generate an edge, edge – face, wire – shell, face-solid, etc.

Brief note on BRepOffsetAPI_MakePipeShell. As it works with wire spines, it must be able to deal with discontinuties between the edges (when edges intersect with sharp edges). It offers 3 modes:
- intersection and rounding
- extension and intersection
- modifying trihedron mode used when sweeping a profile along the following edge in a spine.
The following picture shows these modes respectively in red, green and blue:



To be continued...
Share
Tweet
Pin
Share
No comments
Newer Posts
Older Posts

Subscribe for the new posts

Blog Archive

  • August 2015 (2)
  • May 2014 (1)
  • November 2013 (1)
  • June 2013 (1)
  • May 2013 (1)
  • November 2012 (2)
  • November 2011 (1)
  • June 2011 (3)
  • May 2011 (2)
  • March 2011 (1)
  • February 2011 (1)
  • November 2010 (2)
  • October 2010 (2)
  • September 2010 (1)
  • August 2010 (1)
  • July 2010 (1)
  • June 2010 (1)
  • May 2010 (1)
  • April 2010 (2)
  • March 2010 (2)
  • January 2010 (2)
  • December 2009 (1)
  • November 2009 (2)
  • October 2009 (3)
  • August 2009 (2)
  • July 2009 (3)
  • June 2009 (4)
  • May 2009 (3)
  • April 2009 (2)
  • March 2009 (5)
  • February 2009 (5)
  • January 2009 (5)
  • December 2008 (11)
  • November 2008 (8)

Loading...

Followers

Created by ThemeXpose