0

I am using Embarcadero C++ Builder (an engineer not programmer)

I find the Sleep function is only working for me in debug mode and not in release mode. I see references in StackOverFlow not to use Sleep, and rather to use TTimer.

I simply want my app to pause for a few seconds between drawing objects as per the Sleep(500); in the code below, so that I can see each object being drawn and can check they are correct, else it happens to fast to check.

DrawSelectedShape(k,Side,AddOrDeduct,Color); in the below code, is the process that needs pausing

for (int n=0; n<LiquidLoads->TankBasicData->NoLiquidTypes; ++n){
  for (int m=0; m<LiquidLoads->TankBasicData->NumberOfTanks[n]; ++m)
  {
    for (int l=1; l<LongStrengths->TotalNumberOfParts+1; ++l)
     {
       if (LiquidLoads->TankHeaderArray[n][m]->GhsName == LongStrengths->PartHeader[l]->PartName)
       {

         for (int j=0; j<LongStrengths->PartHeader[l]->NoOfComponents; ++j)
         {
            int k = LongStrengths->PartData[l][j]->ShapeNumber;
            int Side = LongStrengths->PartData[l][j]->Side;
            float AddOrDeduct = LongStrengths->PartData[l][j]->Effectiveness;
            AnsiString Color = LiquidLoads->TankBasicData->LiquidTypeColor[n];
            DrawSelectedShape(k,Side,AddOrDeduct,Color);

            Canvas->TextOut(1200, 300+(n*25),LiquidLoads->TankBasicData->LiquidType[n]);
            Sleep(300);
         }
       break;
       }
   }
}  }

The above code works perfectly in debug mode, but in release mode, it works fine through the first few shapes being drawn to canvas, then you get a spinning wheel mouse cursor for a while followed by a blank canvas.

So I am looking for an alternative to Sleep.

When using a TTimer (no experience) one would use the OnTimer event, and place code that runs repeatedly in the event with a delay related to the Timer1 Interval, which is not quite the same as just looking for a few seconds delay in the middle of a for-loop

This is how my rendering looks like:

animation

Any advise, most appreciated.

Spektre
  • 49,595
  • 11
  • 110
  • 380
BarryK
  • 11
  • 5
  • 2
    `Sleep()` is a Win32 API function, it operates the *exact* same way in debug and release modes. It is just very difficult to debug painting operations in this manner to begin with. As soon as you switch between the UI and another window, like the debugger or code editor, the UI gets signaled to repaint itself, wiping out previous drawings. Also, it is really bad to introduce delays during paint handling, as the OS is likely to complain about the painting taking too long and slowing down subsequent UI message processing. – Remy Lebeau Oct 15 '18 at 18:56
  • 1
    You really need to re-think your debugging approach. I would suggest updating `DrawSelectedShape()` to give it a `TCanvas` input parameter, and let it draw to that canvas as needed. Then, you can create a `TBitmap` for each shape you want to draw and pass the `TBitmap.Canvas` to `DrawSelectedShape()`. Then, you can view the `TBitmap`s as needed, such as by saving them to `.bmp` files, or displaying them in `TImage` controls, etc. – Remy Lebeau Oct 15 '18 at 18:58
  • Many Thanks Remy. I will need to investigate the TBitmap option for reporting purposes, as I use FastReports, and need to get the Canvas onto a report, however for this onscreen scenario, I need the delay effect not only for debugging but also as a user option. Reason being it is drawing ship's tanks in plan view and some tanks are located above others. If you let it draw all at once you only see what is ontop, whereas the user needs the option to let it draw them in a slightly more animated manner. I have no idea why Sleep works well in Debug and not in release mode. – BarryK Oct 15 '18 at 19:48
  • Then you need a completely different approach to your drawing. Using `Sleep()` is wrong, `TTimer` makes more sense. It would be best to have the timer draw everything onto a single `TBitmap`, and then you can draw the current `TBitmap` onto your chosen target `Canvas` whenever it needs to be painted. Draw one tank per timer event until finished. For instance, in the case of a `TForm`, you could have the timer update the `TBitmap` as needed and then `Invalidate()` the Form to trigger a repaint, and then draw the current `TBitmap` onto the Form's `Canvas` in the Form's `OnPaint` event. – Remy Lebeau Oct 15 '18 at 20:33
  • 1
    Many thanks Remy. Appreciated as always. I will develop the TTimer & TBitmap route. (In my previous three questions, there was a way to accept an answer. It is not apparent here. Pls consider the answer accepted and the post closed.) – BarryK Oct 16 '18 at 12:52
  • "*In my previous three questions, there was a way to accept an answer. It is not apparent here*" - because no answer has been posted, only comments making suggestions – Remy Lebeau Oct 16 '18 at 16:05
  • @BarryK take a look at these: [How to slow down opengl animation read from BVH files?](https://stackoverflow.com/a/52812154/2521214) and [Sprites sequence control through DeltaTime](https://stackoverflow.com/a/41920247/2521214) for some basics about animation timing. Sadly without preview we have no idea what and how are you rendering. There might be another ways of presenting your scene (without animation) like switching to 3D, using Transparency, or some Slider selecting visible layer etc ... Also in which event is the code chunk of yours? – Spektre Oct 18 '18 at 07:41
  • @BarryK Because I also encountered that In some cases Timing between running inside embarcadero IDE and running directly exe without IDE is **very** different. I got a project with many forms from which each has rather expensive timing/update needs. In IDE all timings are OK but without it the timers and events frequency drops down to less than a half of programed values... But in my case I suspect it could be also some hidden access violation problem overwriting original data/code which does not manifest in IDE (had seen such behavior few times already...) – Spektre Oct 18 '18 at 07:48
  • @Spektre To give you a crude presentation of the animation see this dropbox link, as done in the IDE. (I assume it is allowed to share links). https://www.dropbox.com/s/ex4cm89mk0zpwdq/BARRYK%202018_10_19%2011_12_40%20AM.mp4?dl=0 The plan view draws each tank by content type. One tank at a time. Later the profile view will also draw in the colours, but only up to the level to which the tank is filled for the given loading condition. Options will follow to just draw selected tanks. I have not had time (due to other non coding projects) to try the TTimer route for the animation. – BarryK Oct 19 '18 at 09:22
  • If you need to visually check that a graphic element is painted correctly, your best bet is to introduce a way to paint *just* that element (or at least not to paint elements that might obscure it). – n. m. could be an AI Oct 19 '18 at 11:54
  • @BarryK yes sharing additional off site links is OK but having answer or question that depends only on off site link is not OK as they tend to break with time and such Q or A would be of no use then. In such case is good to recapitulate what the link does too and refer to it as a source of the knowledge... I created an GIF from your Video and addet it to your question so we do nto need to click on external links :) – Spektre Oct 19 '18 at 11:58

2 Answers2

0

well from the video you posted its clear animation using timer is the way to go...

From your description You should have some value ix for each visible object on your ship. Telling the layer or rendering order of the object.

So add a render_ix value to your rendering code (either global or a member of the form where are you rendering) then recode your draw routine to this:

int render_ix=0; // order/layer number for object to render up to

void draw()
 {
 // here clear&render background
 for (int i=0;i<objects;i++)
  if (object[i].ix<=render_ix)
   object[i].draw(); // render i-th object
 // here render HUD
 }

now just somwhere in your timer with desired interval

void OnTimerAnimation()
 {
 render_ix++;
 if (render_ix>=objects) render_ix=0;
 // here force repaint either by calling Paint, Refresh,Update ... or what ever way you are using
 }

You can also change the render_ix by scroll bar or programaticaly. As you can see no Sleep or Delay is needed hence no blocking of execution.

If your rendering is not order of object based but radher by some filtering than just change the render_ix with the variable you are filtering. This way you can nest more variables together (Volume, mass , position...) into nested if statements.

Another but very useful scheme is to use selections. So create a list of object indexes to render... and render/highlight only those that are present in the list. That is very useful for mouse selections and operation with more containers at once.

Spektre
  • 49,595
  • 11
  • 110
  • 380
  • Thank you. This is most useful advice. As a test I can try the render_ix incrementing in a timer as you indicated, however for the actual application I will have to follow your selection proposal as the objects to be rendered are not necessarily in order. Appreciated as always. – BarryK Oct 19 '18 at 20:57
0

To avoid the for loops and if statements, that was part of the process of drawing to the canvas using the Sleep method to pause the loops, I now first had to generate a render sequence to a Struct, that I could then use with a single sequential counter in TTimer

as below

//---------------------------------------------------------------------------
void __fastcall TShipGraphic::DrawSelectedTanksBtnClick(TObject *Sender)
{
    GenerateRenderSequence();
    Timer1->Interval = StrToInt(Edit1->Text);
    CloseButton->Enabled = false;
    render_ix=0; //Initialised globally

     Timer1->Enabled = true;

}
//---------------------------------------------------------------------------
void __fastcall TShipGraphic::Timer1Timer(TObject *Sender)
{
 render_ix++;
 Timer1->Interval = StrToInt(Edit1->Text);
 if (render_ix<=TankShapeCounter)
 {
    DrawSelectedComponentShape(render_ix); // render i-th object
    Canvas->Refresh();
 }
 else
 {
   Timer1->Enabled = false;
   CloseButton->Enabled = true;
 }
}
//---------------------------------------------------------------------------
void TShipGraphic::GenerateRenderSequence()
{
   TankShapeCounter = 0;

  for (int n=0; n<LiquidLoads->TankBasicData->NoLiquidTypes; ++n)
  {
    if ((CheckListBox1->Checked[n]) || (CheckListBox1->Checked[LiquidLoads->TankBasicData->NoLiquidTypes]))
    {
      for (int m=0; m<LiquidLoads->TankBasicData->NumberOfTanks[n]; ++m)
      {

        for (int l=1; l<LongStrengths->TotalNumberOfParts+1; ++l)
         {
           if (LiquidLoads->TankHeaderArray[n][m]->GhsName == LongStrengths->PartHeader[l]->PartName)
           {

             for (int j=0; j<LongStrengths->PartHeader[l]->NoOfComponents; ++j)
             {
                ++TankShapeCounter;
                int k = LongStrengths->PartData[l][j]->ShapeNumber;
                int Side = LongStrengths->PartData[l][j]->Side;
                float AddOrDeduct = LongStrengths->PartData[l][j]->Effectiveness;
                AnsiString Color = LiquidLoads->TankBasicData->LiquidTypeColor[n];
                RenderSequence[TankShapeCounter]->ShapeNumber = k;
                RenderSequence[TankShapeCounter]->Side = Side;
                RenderSequence[TankShapeCounter]->AddOrDeduct = AddOrDeduct;
                RenderSequence[TankShapeCounter]->Color = Color;
             }
           break;
           }
         }
      }
    }
  }
}
//---------------------------------------------------------------------------

I wanted to use @Spectre's neat animated gif method to show the result, but my Game Bar is too temperamental to capture the screen animation to a video. "Win G" to activate the Game Bar is doing nothing.

Thanks to Remy and Spektre for the valuable advise.

BarryK
  • 11
  • 5