Sylvestre's blog

OpenCascade : How to make money with free software ?

· Sly · Développement, Linux

I wrote this article 6 mois ago. I was waiting for some stuffs around this. However, it is too late so I release it.

My main concern about free software as a way of doing business is "how do you make money out of it ?".
Unfortunatelly, I still don't see any solution ... Of course, it is possible by selling services, maintenance, help, more (costly) advanced version, side development (usually proprietary) but it is not doing money only on free software/development.

Let's me describe on of the solution that uses Opencascade, a former department of Matra.

They produces for a quite long time (about 10 years) a big project called "Open CASCADE". It is a framework used to modelise, visualise 3D models (and many other things that I hardly understand)... It has an aspect of gas factory ("Usine à gaz" as we say in French) because the size/age/complexity of the project. However, it is a very convenient way of doing 3D (many famous corporations use it).

At work, I am working on a project launched by the BRGM (Bureau de recherche géologique et minière ... the equivalent of the french CNRS but for earth sciences) about 10 years ago. The aim of the project is to provide to geologists and geophysists a way of representing the underground. The corporation where I work is doing the work of adding new features, packaging and selling the software. For this project, we use Opencascade.

My work on this project was to provide a visualisation of drillholes in a 3D space (holes that are dug in the ground to get the geological structure of the underground). Basically, I was supposed to represent a (drunk) worm hole from a serie of points.
I tried many way (from the ugliest to the sharpest) but I found some problems with my favorite. When I used too much points, the framework start to reject me (technical description is following my message). As I don't like when a software rejects me, I spent time one this issue (I thought that it was my fault because it is a quite basic feature in the software). I finally successed to isolate the issue and send a kind of SOS/bug report to someone at OpenCascade.
This request cost us 2 units of support from our contract.

Then, one week after, they get back to us with a "There is a bug in Pipe algorithm.". OK, cool. I was a bit proud to find a bug in this kind of big software (for the courageus/crazy, the reason of the bug is : "This bug is caused by complex structure of the curve approximated from 14 (15) points (some discontinuities of derivatives of high levels). Such structure of the curve causes turbulence of the local coordinate system of section along the curve. That is why the algorithm can not build a pipe where the section is orthogonal to the curve in each point all along the curve.", obvious no ?). However, I was expecting a small patch which will fix the issue and a "thank for the bug report". Instead of this, I saw :

The bug fix production can be started after your confirmation.
Please note that the standard price of a query is 15 units of your support program.

OK, now, I know how they make cash (at least a part of it).
You submit what you think to be a bug, it costs you 2 units. They confirm that is a bug, you have to pay 15 units to get the fix. Otherwise, you have to wait for the new release planned whenitwillbeready.
I am not saying it is a bad solution (and I do understand why they do that ... it is called support) but it could be more respectful for the user who spent many time to isolate a bug and report it and consequently helped to improve their software...
(For their defense, they provided me a workaround for my issue).

[teaserbreak]

If anyone is faced to the same problem as I (which I strongly doubt), here is a description of my problem :

I have X points (gp_Pnt) added into a TColgp_HArray1OfPnt.
I build a Geom_BSplineCurve with the TColgp_HArray1OfPnt.
I "transform" the Geom_BSplineCurve into an edge which is transformed into a wire.

I create a wire (from circle) which will be the base of my pipe.
I create a plan perpendicular to the vector made by the two first point of the Geom_BSplineCurve.
I make a face from the wire (circle) and the plan.

I create the pipe with the method : BRepOffsetAPI_MakePipe with my wire (the one from Geom_BSplineCurve).

All this works correctly with many data BUT with some data I get this :
An exception was caught 0xb5de9e47 : TopoDS_UnCompatibleShapes: TopoDS_Builder::Add:insertion of non FACE in SHELL

It could be normal if the data were a bit "weird" but data are perfectly normal and coherent. It is close to a straight line (the difference between each point is about the same).
And the weirdest thing is the difference between my two functions :
In the buggy one, I have 15 points.
In the right one, I have 14 points (the same as in the buggy one) the only difference is :
gp_Pnt aPnt15( 2009034.12574533 , 108227.718816724 , 365.625934962419 );
a->SetValue(15,aPnt15);

This point is causing the problem despite the fact that it is coherent with the rest.

The source code :

static void makePipe(Handle(TColgp_HArray1OfPnt) a) {
Handle(Geom_BSplineCurve) SPL1 = GeomAPI_PointsToBSpline(a->Array1()).Curve();

TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(SPL1);
cout < < "Edge created" << endl;
TopoDS_Wire w;
w = BRepBuilderAPI_MakeWire(edge);
cout << "Wire created" << endl;

///////////// Generation of the pipe around the wire ///////////
gp_Vec v(a->Value(1),a->Value(2));
gp_Dir nv(v);

// axe
gp_Ax2 ax2 (a->Value(1),nv);
// cercle
gp_Circ circ (ax2,10);

// edge
TopoDS_Edge edgecirc = BRepBuilderAPI_MakeEdge(circ);

// wire
TopoDS_Wire wire_ = BRepBuilderAPI_MakeWire(edgecirc);
// face
gp_Pln pln(a->Value(1),nv);

TopoDS_Face face = BRepBuilderAPI_MakeFace(pln, wire_);

TopoDS_Shape tube = BRepOffsetAPI_MakePipe(w,face);
///////////// END Generation of the pipe around the wire ///////////

Standard_CString tub("tubebyarray");
DBRep::Set(tub,tube);

}

static int bugSample (Draw_Interpretor& di,Standard_Integer argc, char** argv)
{
int i;
Handle(TColgp_HArray1OfPnt) a =new TColgp_HArray1OfPnt(1,15);

gp_Pnt aPnt1( 2009000 , 108200 , 90 );
a->SetValue(1,aPnt1);
gp_Pnt aPnt2( 2009000.2904475 , 108200.289079067 , 109.954502138921 );
a->SetValue(2,aPnt2);
gp_Pnt aPnt3( 2009001.80446215 , 108201.545557615 , 129.758914645819 );
a->SetValue(3,aPnt3);
gp_Pnt aPnt4( 2009004.49790241 , 108203.726662541 , 149.414499672202 );
a->SetValue(4,aPnt4);
gp_Pnt aPnt5( 2009007.19134268 , 108205.907767467 , 169.070084698585 );
a->SetValue(5,aPnt5);
gp_Pnt aPnt6( 2009009.88478294 , 108208.088872392 , 188.725669724969 );
a->SetValue(6,aPnt6);
gp_Pnt aPnt7( 2009012.57822321 , 108210.269977318 , 208.381254751352 );
a->SetValue(7,aPnt7);
gp_Pnt aPnt8( 2009015.27166347 , 108212.451082244 , 228.036839777735 );
a->SetValue(8,aPnt8);
gp_Pnt aPnt9( 2009017.96510374 , 108214.63218717 , 247.692424804119 );
a->SetValue(9,aPnt9);
gp_Pnt aPnt10( 2009020.658544 , 108216.813292095 , 267.348009830502 );
a->SetValue(10,aPnt10);
gp_Pnt aPnt11( 2009023.35198427 , 108218.994397021 , 287.003594856885 );
a->SetValue(11,aPnt11);
gp_Pnt aPnt12( 2009026.04542453 , 108221.175501947 , 306.659179883269 );
a->SetValue(12,aPnt12);
gp_Pnt aPnt13( 2009028.7388648 , 108223.356606872 , 326.314764909652 );
a->SetValue(13,aPnt13);
gp_Pnt aPnt14( 2009031.43230506 , 108225.537711798 , 345.970349936035 );
a->SetValue(14,aPnt14);
gp_Pnt aPnt15( 2009034.12574533 , 108227.718816724 , 365.625934962419 );
a->SetValue(15,aPnt15);
makePipe(a);

return 0;
}

static int noBug (Draw_Interpretor& di,Standard_Integer argc, char** argv)
{
int i;
Handle(TColgp_HArray1OfPnt) a =new TColgp_HArray1OfPnt(1,14);

gp_Pnt aPnt1( 2009000 , 108200 , 90 );
a->SetValue(1,aPnt1);
gp_Pnt aPnt2( 2009000.2904475 , 108200.289079067 , 109.954502138921 );
a->SetValue(2,aPnt2);
gp_Pnt aPnt3( 2009001.80446215 , 108201.545557615 , 129.758914645819 );
a->SetValue(3,aPnt3);
gp_Pnt aPnt4( 2009004.49790241 , 108203.726662541 , 149.414499672202 );
a->SetValue(4,aPnt4);
gp_Pnt aPnt5( 2009007.19134268 , 108205.907767467 , 169.070084698585 );
a->SetValue(5,aPnt5);
gp_Pnt aPnt6( 2009009.88478294 , 108208.088872392 , 188.725669724969 );
a->SetValue(6,aPnt6);
gp_Pnt aPnt7( 2009012.57822321 , 108210.269977318 , 208.381254751352 );
a->SetValue(7,aPnt7);
gp_Pnt aPnt8( 2009015.27166347 , 108212.451082244 , 228.036839777735 );
a->SetValue(8,aPnt8);
gp_Pnt aPnt9( 2009017.96510374 , 108214.63218717 , 247.692424804119 );
a->SetValue(9,aPnt9);
gp_Pnt aPnt10( 2009020.658544 , 108216.813292095 , 267.348009830502 );
a->SetValue(10,aPnt10);
gp_Pnt aPnt11( 2009023.35198427 , 108218.994397021 , 287.003594856885 );
a->SetValue(11,aPnt11);
gp_Pnt aPnt12( 2009026.04542453 , 108221.175501947 , 306.659179883269 );
a->SetValue(12,aPnt12);
gp_Pnt aPnt13( 2009028.7388648 , 108223.356606872 , 326.314764909652 );
a->SetValue(13,aPnt13);
gp_Pnt aPnt14( 2009031.43230506 , 108225.537711798 , 345.970349936035 );
a->SetValue(14,aPnt14);
makePipe(a);

return 0;
}

The workaround for my function :

le(TColgp_HArray1OfPnt) a) {

Handle(Geom_BSplineCurve) SPL1 = GeomAPI_PointsToBSpline(a->Array1()).Curve();

TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(SPL1);
cout < < "Edge created" << endl;
TopoDS_Wire Spine;
Spine = BRepBuilderAPI_MakeWire(edge);
cout << "Wire created" << endl;
Standard_CString strspine("sp");
DBRep::Set(strspine, Spine);

///////////// Generation of the pipe around the wire ///////////
gp_Vec v(a->Value(1),a->Value(2));
gp_Dir nv(v);

// axe
gp_Ax2 ax2 (a->Value(1),nv);
// cercle
gp_Circ circ (ax2,10);


// edge
TopoDS_Edge edgecirc = BRepBuilderAPI_MakeEdge(circ);

// wire
TopoDS_Wire Profile = BRepBuilderAPI_MakeWire(edgecirc);
Standard_CString strprofile("pr");
DBRep::Set(strprofile, Profile);

BRepOffsetAPI_MakePipeShell Sweep(Spine);

// This string defines parallel shifting of the section along spine
Sweep.SetMode(ax2);

Sweep.Add(Profile);
Sweep.Build();
Sweep.MakeSolid();

TopoDS_Shape tube = Sweep.Shape();
Standard_CString tub("tubebyarray");
DBRep::Set(tub,tube);
}

And now, everybody is sleeping...