2009-01-29

How do you measure your progress ? Part 3

(continued)

Performance considerations
Using progress indicator obviously adds certain overhead as calculating current position and redrawing a widget eats some CPU time. So, be careful to not abuse an indicator by updating it after every tiny operation. Instead you might prefer to request its update after some chunks of operations (e.g. after every 10th).

int aNbOps = ...;
int aThreshold = 10;
int aNbChunks = aNbOps / aThreshold;

anIndicator->SetRange (0, aNbChunks);
for (i = 1; i <= aNbOps; i++) {
if (i % aThreshold)
anIndicator->Increment();
}

Inserting your indicator into Open CASCADE algorithms
Once you have implemented your subclass, you can pass it to OCC algorithms. As I said above, at the moment, Open CASCADE currently supports progress indicators in IGES, STEP and BRep readers and writers.
Here are examples of how you can provide your indicators into IGES and BRep readers.

IGESCAFControl_Reader aReader;
Handle(Message_ProgressIndicator) anIndicator = ...;

//to make progress indicator work more smoothly divide the transfer into
//two steps: loading (30%) and mapping (70%), including shape healing
anIndicator->NewScope (30, (Standard_CString)qApp->translate ("Exchanger", "Loading file").toAscii());
Standard_Boolean aRes = (aReader.ReadFile (theFileName.ToCString()) == IFSelect_RetDone);
anIndicator->EndScope ();
if (aRes) {
//setting progress into the reader object to track every entity
//(using TransientProcess() is possible only after ReadFile())
aReader.WS()->MapReader()->SetProgress (anIndicator);
anIndicator->NewScope (70, (Standard_CString)qApp->translate ("Exchanger", "Translating file").toAscii());
aRes = aReader.Transfer (theDoc);
anIndicator->EndScope ();
//work-around to decremenet reference counter inside TransientProcess
aReader.WS()->MapReader()->SetProgress (NULL);
}

Importing native BRep file is more straightforward:
Standard_Boolean aResult = BRepTools::Read (aShape, theFileName.ToCString(), aBuilder, anIndicator);


Multi-threading issues
In its current implementation Message_ProgressIndicator is exclusively tailored for use within a single thread. This stems from its implementation concept based on a stack of nested sub-ranges – every time you signal a progress (e.g. with Increment() or EndScope()) it applies to the current open sub-range. This makes it unusable for multi-threaded environment where concurrent threads can perform their own chunks of tasks and report progress. I first encountered this working on parallelizing IGES import.

Upon part 1 publication, Andrey from OCC was very kind to let me know that they are prototyping multi-threaded version of the progress indicator. So chances are it can appear in near releases. Anyway, as long as you use the indicator in a single thread even within a multi-threaded environment (e.g. if your OCC-based algorithms run in a separate worker thread) you are safe to go.

So, go and plan your goal, set your range, make your next step and report how closer you are now ;-).

2009-01-28

How do you measure your progress ? Part 2

(continued)

GUI coupling
You will want to subclass Message_ProgressIndicator to couple with your favorite GUI toolkit you are using to develop your application. An alternative choice is to provide simple text output (e.g. for debugging purposes). Either way, you will have to redefine Show() and UserBreak() methods.

In my QOLib (Qt/Open CASCADE Library) I used Qt's QProgressDialog widget to visualize a progress bar and a cancel button. You might want to use your own QWidget consisting of QProgressBar, QLabel and optionally QButton.



Here are some code snippets from QLib:

class QOBase_ProgressIndicator : public Message_ProgressIndicator
{
public:
//! Creates an object.
Standard_EXPORT QOBase_ProgressIndicator (QWidget* theParent,
int theMinVal = 0, int theMaxVal = 100, Qt::WindowFlags theFlags = 0);

//! Deletes the object.
Standard_EXPORT virtual ~QOBase_ProgressIndicator ();

//! Updates presentation of the object.
Standard_EXPORT virtual Standard_Boolean Show (const Standard_Boolean theForce);

//! Returns True if the user has signaled to cancel the process.
Standard_EXPORT virtual Standard_Boolean UserBreak();

protected:

QProgressDialog* myProgress;

public:
DEFINE_STANDARD_RTTI(QOBase_ProgressIndicator)
};

/*! Creates a widget using specified paramters to initialize QProgressIndicator.
\a theMin and \a theMax are also used to set the range for \a this progress
indicator.
*/
QOBase_ProgressIndicator::QOBase_ProgressIndicator (QWidget* theParent,
int theMinVal, int theMaxVal,
Qt::WindowFlags theFlags)
{
QOLib_ASSERT (theMinVal < theMaxVal);
myProgress = new QProgressDialog (theParent, theFlags);
myProgress->setWindowModality (Qt::WindowModal);
myProgress->setMinimum (theMinVal);
myProgress->setMaximum (theMaxVal);
myProgress->setMinimumDuration (500); //the dialog will pop up if operation takes >500ms

SetScale (theMinVal, theMaxVal, 1); //synch up ranges between Qt and Open CASCADE
}

/*! Destroys the associated progress dialog.*/
QOBase_ProgressIndicator::~QOBase_ProgressIndicator ()
{
if (myProgress) {
delete myProgress;
myProgress = 0;
}
}

/*! Updates visual presentation according to currently achieved progress.
The text label is updated according to the name of a current step.

Always returns TRUE to signal that the presentation has been updated.
*/
Standard_Boolean QOBase_ProgressIndicator::Show (const Standard_Boolean theForce)
{
Handle(TCollection_HAsciiString) aName = GetScope(1).GetName(); //current step
if (!aName.IsNull())
myProgress->setLabelText (aName->ToCString());

Standard_Real aPc = GetPosition(); //always within [0,1]
int aVal = myProgress->minimum() + aPc *
(myProgress->maximum() - myProgress->minimum());
myProgress->setValue (aVal);
QApplication::processEvents(); //to let redraw and keep GUI responsive

return Standard_True;
}

/*! Returns True if the user has clicked the Cancel button in QProgressDialog.
*/
Standard_Boolean QOBase_ProgressIndicator::UserBreak()
{
return myProgress->wasCanceled();
}

Open CASCADE ships with a Tcl/Tk-based implementation (see class Draw_ProgressIndicator). Unfortunately, version 6.3.0 has (unintentionally) eliminated the XProgress Draw command that you could use to activate it and use inside the Draw session. I reported this to the folks at OCC, so hopefully they will restore it in upcoming releases. If you are impatient you could simply port it from previous versions.

(To be continued...)

2009-01-27

How do you measure your progress ? Part 1

It's important to set measurable goals in your life and to periodically check how you advance. Your application should also communicate its status in execution of a long operation, otherwise your users may decide the application hung up and to kill it.

Open CASCADE offers a mean to help you to signal that your app is still alive. It's Message_ProgressIndicator, a class manipulated by handle that you can pass to some algorithms. Currently it is supported by IGES and STEP translators, as well as by BRep loader (starting from version 6.3). Well, not much but still is a good start. OCC team might want to extend its use into some CPU-hungry algorithms (e.g. Boolean operations or others).

Nonetheless, you might want to use it even in your own algorithms thanks to its rich capabilities.
Let's consider them more closely.

Message_ProgressIndicator extends a simple concept of a progress indicator as a container of integer [min, max] range and a current value.

First nice thing is that Message_ProgressIndicator offers a range of double (not int) range and a custom step.

Handle(MyProgressIndicator) anIndicator = new MyProgressIndicator;
Standard_Real aStartAngle = 0.5 * PI;
Standard_Real anEndAngle = 1.5 * PI;
Standard_Real aDelta = PI / 6.;
anIndicator->SetRange (aStartAngle, anEndAngle);
anIndicator->SetStep (aDelta);

for (Standard_Real aCurrentAngle = aStartAngle; aCurrentAngle <= aStartAngle; aCurrentAngle += aDelta, anIndicator->Increment()) {
...
}

To get a current value on the global range use Message_ProgressIndicator::GetPosition() which returns a value in the range [0, 1].

Next cool thing is that the indicator supports nested regions. That is extremely valuable if your operation is a subpart of a larger operation it may have no idea of. Imagine that your algorithm of building custom visual presentation (AIS_InteractiveObject subclass) can be an ending step of both your complex modeling algorithm and of simple restoring a model from a file.

An operation can allocate a sub-range for its sub-operations so that they report progress within their sub-ranges. The progress indicator will take care to map local value into a global range.

void MyLongOperation ()
{
Handle(Message_ProgressIndicator) anIndicator = new MyIndicator;
anIndicator->SetRange (0, 100); //100% complete

anIndicator->NewScope (60., "The longest suboperation"); //first sub operation takes 60%
MyLongSubOperation1 (anIndicator);
anIndicator->EndScope ();

anIndicator->NewScope (30.); // 30%
MyLongSubOperation2 (anIndicator);
anIndicator->EndScope ();

anIndicator->NewScope (10.); // 10%
MyLongSubOperation3 (anIndicator);
anIndicator->EndScope ();

}

void MyLongSubOperation1 (const Handle(Message_ProgressIndicator)& theIndicator)
{
Standard_Integer aNbSteps = ...; //small chunks of work
theIndicator->SetRange (0, aNbSteps);
for (int i = 1; i <= aNbSteps; i++, theIndicator->Increment()) {
...
}

}

In the above code the entire global range has been split into 3 non-equal chunks proportionate to estimated duration of each MyLongSubOperation...(), and each is executed in its own subrange. Thus SetRange() in MyLongSubOperation1() affects its allocated subrange.

Third, sub-ranges can have their own names what can be used by displaying widgets to reflect a current phase. The name can be specified with SetName() or directly in NewScope().

Indicator can also be used to signal a user's action to break the operation. Redefine and use UserBreak() virtual method.

There are other nice features like support of infinite regions or Message_ProgressSentry class, which is a convenient wrapper and especially useful for cases when an indicator can be a null handle. Check how the latter is used in IGESToBRep_CurveAndSurface.cxx for example.

Designing your own indicator
Message_ProgressIndicator is an abstract class, so you must subclass it and redefine two pure virtual methods – Show() and UserBreak(). They define how progress indicator must be displayed and whether the user signaled that operation needs to be canceled. Let me show you an example of that in the next part.

To be continued...

2009-01-14

The King is dead. Long live the King !

This post will be about Qt. If you are the fan of Trolltech's products or the company itself as much as I was, you will likely be astonished if you have not been yet. Today, Jan 14, the company announced they would license Qt under LGPL. Everywhere including Windows. Read the announcement here.



Qt Software (new name of Trolltech, much lacking its previous brightness, imho) management states this was a logical move after multiple years of practicing a dual model (GPL & commercial) and that it will boost further Qt proliferation. I'm shocked and excited at the same time, and this seems to be a common reaction based among the comments flooding the announcement.

My first shout the second I heard this from a colleague was "they went crazy, this will suck up their revenue". (If you are not familiar with GPL and LGPL, let me describe it in a couple of words. GPL forces you to disclose your source code, while LGPL – not, if you are using it correctly. That's why Trolls were successful in their dual model – their commercial customers preferred to buy expensive licenses, while Open Source community was supportive of GPL. I touched licensing issues in one of my previous posts).

I immediately jumped to their web-site, watched the video and read the FAQ, and then went to their blog where their VP announced it. While I was typing their url in Firefox, I kept on thinking "how will they earn their money now, home come ?". A guess popped up very quickly – Nokia (I hope you know that Nokia acquired Trolltech last year). With such a Sugar Daddy behind you, you can afford such things. Yep, that seems to be it – the video confirmed that (citation). So, is revenue no longer a concern ? Quite possibly, and Nokia rather puts a stake on Qt's farther penetration. The company intends to reinforce Open Source community's contribution by simplifying some contribution processes and introducing new web tools. Let's see if that works.

More gloomy thoughts on this. Perhaps this all makes sense for Nokia, I don't know. I am still puzzled with a business sense for it to have acquired Trolltech for amazing $153M. Neither the business strategy, nor the price did not sound reasonable for me. Have anyone clearly understood the logic supporting this deal ? I'd be interested to hear your comments. If you read Trolltech's financial statements for 2 last years (after they went public on Oslo stock exchange in 2006), you might have noticed they never reported positive net income for the period of being a public company. Why buy such a company if you can't convert it into a cash generator ? What also concerns me is lack of understanding of a long-term strategy of Nokia with respect to Qt and the company. If they give up an idea of earning money with it, what can be the next step ? Can it be that they say, OK, Qt is now fully free (as in beer and in speech), you, the Community, go and maintain it ? Here are the better procedures and tools for that. And we, at Nokia, will assign brilliant Troll developers to internal projects. Possible ? Or, after a few years when Qt is everywhere, they could say, well, that loose license model we announced in 2009 undermines our revenue too much and we have to revisit it, we are now back to the old model (GPL & commercial). Free drug trick. I hope Nokia is not (that) evil, and it has a plan. Maybe I'm too paranoid but when such sound moves happen I try to understand where a real point is. I don't see it now :-(. If you do please share, that would be really interesting.

Now back to the ground. What does today's announcement mean for us ? If you are a small or a mid-size company then I suggest that you consider transition to the LGPL licensed edition (at least I would do this). This can save you several thousands or dozens of thousands dollars a year. To stay compliant you must dynamically link with the LGPL'ed Qt library and must not strip your binaries or otherwise prevent reverse-engineering.

I myself also hope to benefit from this Troll's decision. Currently I am resurrecting own previously developed toolkit – QOLib – Qt/Open CASCADE Library – I used to develop some tools about 6 years ago with Qt3. With the LGPL'ed edition there are no more concerns that previously forced me to cautiously package the toolkit in order not to breach the former license. In this sense, I cannot but welcome today's decision. No more viral threat, Qt the Great remains.

The King is dead. Long live the King !

P.S. Typing this post using a laptop while sitting in my car at a service station waiting to change oil in the car (previous one did not let the engine start at -15C). Will upload when back home.

2009-01-10

Call for action. Building CAD/CAM/CAE development resource index

After I have started this blog there were a few inquiries from people asking to share resources that would help novices to better understand the CAD/CAM/CAE (CAx) world. I shared what I had (well, some links got broken over time), and thought this could be of a broader interest, virtually for anyone in the development community.

Can we all together try this out and build some useful list ? Let's think how to make and to maintain it manageable. For instance, is there any ready-to-use rating mechanism that visitors could use to rate the usefulness of every resource ? Please advise if you know such.

Anyway, let's start with something simple and see how it goes.

As a first step, I suggest a simple one-level classification. Let's first focus on CAx.

- Theory (books, papers, articles, courses on mathematical apparatus and models)
- Software (geometric kernels, libraries, components, etc)
- Developers communities (forums, blogs, wiki's etc dedicated to CAx development)
- Informational resources (portals, newsletters, etc)
- Models (2D or 3D models in neutral or proprietary formats that can be freely used)

Provide your links using comments to this post. Include category, brief description and advantages of the resource (up to 50 words). The goal is not to have it exhausted but to contain most useful links. So, if there are resources that outshine others, we will not hesitate to remove some.

If you have any ideas how to make this most useful, please speak up.

So, let's try...