Page 1 of 1

TeEngine.InternalDraw.ApplyMaxMinOffsets in conjuction with

Posted: Fri Apr 30, 2010 5:09 pm
by 10054750
In ApplyMaxMinOffsets, you calculate tmpR to see whether to adjust the axis.IMInimum and IMaximum.
In our case, tmpR is always (0,0,0,0). The problem with that is that you are calling Axis.AdjustMaxMinRect(0,0,0,0), probably assuming it will not do anything. But it does. In my case, before this method call, the Axis.MinimumValue is zero, as is the Axis.IMinimum.

However, when calling RecalcAdjustedMinMax, you recalculate IMinimum based on the the "adjusted" IStart value, which is the same as the regular IStart (in my case, pixel 37). However, due to rounding errors that occur when converting from pixel to double, the new IMinimum becomes ~0.002.... This would not be a problem, except that in OnGetNextAxisLabel, I use the Axis.MimimumValue as the basis for my calculations, and return 0 as my first label. When I return 0 as the next label value, DoCustomLabels stops drawing any more labels, because 0<0.002 and LabelInside=False;

see in DoCustomLabels

Code: Select all

        LabelInside:=(tmpValue>=(IMinimum-DifFloat)) and
                     (tmpValue<=(IMaximum+DifFloat));
In short, I made the following changes. Please let me know if they are appropriate and I think you may want to consider them in your release.

Code: Select all

  procedure ApplyMaxMinOffsets;
      var tmpHasOffsets : Boolean;
      begin
        tmpHasOffsets:=(Axis.MaximumOffset<>0) or (Axis.MinimumOffset<>0);

        if tmpHasOffsets then
        [b]begin // begin added by dns[/b]
             if Axis.Horizontal then ApplyOffsets(tmpR.Left,tmpR.Right)
                                else ApplyOffsets(tmpR.Bottom,tmpR.Top);

          Axis.AdjustMaxMinRect(tmpR);

          Axis.CalcRoundScales;

          if tmpHasOffsets then
          with Axis do
               FPosTitle:=InflateAxisPos(FPosLabels,SizeLabels);
    [b]    end;// end added by dns[/b]
      end;

Code: Select all

Procedure TChartAxis.AdjustMaxMinRect(Const Rect:TRect);
var tmpMin : Double;
    tmpMax : Double;

  Procedure RecalcAdjustedMinMax(Pos1,Pos2:Integer);
  var OldStart : Integer;
      OldEnd   : Integer;
  Begin
    OldStart :=IStartPos;
    OldEnd   :=IEndPos;
    Inc(IStartPos,Pos1);
    Dec(IEndPos,Pos2);

    IAxisSize:=IEndPos-IStartPos;

    tmpMin:=CalcPosPoint(OldStart);
    tmpMax:=CalcPosPoint(OldEnd);
  end;

Begin
[b]  // added by dns
  if (rect.Left=0) and (rect.Top=0) and (rect.right=0) and (rect.Bottom=0) then
    exit;[/b]

  with Rect do
  if Horizontal then ReCalcAdjustedMinMax(Left,Right)
                else ReCalcAdjustedMinMax(Top,Bottom);

  InternalCalcPositions;

  IMaximum:=tmpMax;
  IMinimum:=tmpMin;

  if IMinimum>IMaximum then
     SwapDouble(IMinimum,IMaximum);

  InternalCalcRange;
end;

Re: TeEngine.InternalDraw.ApplyMaxMinOffsets in conjuction with

Posted: Tue May 04, 2010 2:02 pm
by narcis
Hi davidnovo,

Thank you very much for your valuable feedback :!:

Could you please attach a simple example project we can run "as-is" to reproduce the circumstances you describe? We will then analize your code modifications and consider them for inclusion.

Thanks in advance.

Re: TeEngine.InternalDraw.ApplyMaxMinOffsets in conjuction with

Posted: Tue May 04, 2010 3:16 pm
by 10054750
Its very difficult to reproduce, as teh chart has to be exactly the correct size that when rounding from pixel coordinate back to real value in Axis.AdjustMaxMinRect.RecalcAdjustedMinMax that the IMinimum becomes slightly greater than FMinimum, then try to pass FMinimum back in OnGetNextAxisLabel as the next label value. I will try to do it with a simple application. I found it primarily by luck in a much more complicated application.

Re: TeEngine.InternalDraw.ApplyMaxMinOffsets in conjuction with

Posted: Fri May 07, 2010 2:02 pm
by narcis
Hi davidnovo,

It would be really helpful. If you can find a consistent way to reproduce the issue we will be very interested in knowing it and checking if your code modifications help solving it.

Thanks in advance.

Re: TeEngine.InternalDraw.ApplyMaxMinOffsets in conjuction with

Posted: Fri May 07, 2010 2:27 pm
by 10054750
Hello,

They absolutely do help, because it fixed my problem :-) I am just wondering if that could cause other problems.

To reproduce, I would just set up a chart where the # of pixels of the axis is not equally divisible by the axis range. You have to find a size where the start pixel returns a value slightly greater than FMinimum. Then, in OnGetNextAxisLabel, you just return FMinimum as the starting label.

I am not sure when I will have time to try and set this up, but I will try.

Re: TeEngine.InternalDraw.ApplyMaxMinOffsets in conjuction with

Posted: Mon May 10, 2010 9:31 am
by narcis
Hi davidnovo,

Thanks for your feedback. Ok, using code below I get a chart similar to what you described. Am I in the right way? However, it's unclear to me what I should do in the OnGetNextAxisLabel event. Can you please elaborate on that?

Code: Select all

uses Series;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Chart1.Width:=800;
  Chart1.Height:=600;

  Chart1.AddSeries(TLineSeries.Create(Self));
  Chart1[0].FillSampleValues;

  Chart1.Draw;
end;

procedure TForm1.Chart1GetNextAxisLabel(Sender: TChartAxis;
  LabelIndex: Integer; var LabelValue: Double; var Stop: Boolean);
begin
  if Sender = Chart1.Axes.Bottom then
  begin
    LabelValue:=Sender.Minimum;
  end;
end;

procedure TForm1.Chart1AfterDraw(Sender: TObject);
var range: Double;
    length: Integer;
    division: Double;
begin
  range:=Chart1.Axes.Bottom.Maximum - Chart1.Axes.Bottom.Minimum;
  length:=Chart1.Axes.Bottom.IAxisSize;
  division:=length/range;

  With Chart1.Title.Text do
  begin
    Clear;
    Add('Axis length: ' + IntToStr(length));
    Add('Axis range: ' + FloatToStr(range));
    Add('Axis length/range: ' + FloatToStr(division));
  end;
end;
Thanks in advance.

Re: TeEngine.InternalDraw.ApplyMaxMinOffsets in conjuction with

Posted: Tue May 18, 2010 6:56 pm
by 10047094
Hi Narcis,

That is exactly correct. When you pass back the label value as sender.Minimum, it checks to make sure the value that you passed back was >Iminimum or else it stops drawing the labels. the problem I has was that sender.minumum was 0.0 but IMimimum was 0.02 because AdjustMaxMinRect was being called with (0,0,0,0). If you look at AdjustMaxMinRect, it calls RecalcAdjustMinMax with (0,0) in this case which calls calcPosPoint. CalcPosPoint seems to be returning a rounded value. So even though the AdjustMaxMinRect was being called with (0,0,0,0) the IMinimum was being returned slightly differently from what it was before and returning sender.Minimum from GetNextLabelValue was stopping all the rest of teh labels from being drawn because sender.Minimum<sender.IMinimum

Re: TeEngine.InternalDraw.ApplyMaxMinOffsets in conjuction with

Posted: Wed May 19, 2010 10:17 am
by narcis
Hi dave,

Thanks for the info. Does this solve the problem for you or what I should do to get IMinium=0.02? I have debugged code above and I get IMinimum=0.

Thanks in advance.

Re: TeEngine.InternalDraw.ApplyMaxMinOffsets in conjuction with

Posted: Wed May 19, 2010 7:00 pm
by 10047094
Hello.

I did not debug into CalcPosPoint to see under what conditions it rounds to a different value than IMinimum. The problem was fixed for me by not calling ApplyOffsets if the offsets were (0,0). I do not know how to recreate an exact example where CalcPosPoint returns a slightly different IMinimum than the FMinimum :D. Best I can do is what I have done, is walk you through the code path, so you can see that it is potentially possible if CalcPosPoint returns a slightly different value ....

Re: TeEngine.InternalDraw.ApplyMaxMinOffsets in conjuction with

Posted: Thu May 20, 2010 8:33 am
by narcis
Hi dave,

The only place where I think this can occur is in CalcPosPoint, at this line:

Code: Select all

    tmp:=tmp*IRange/IAxisSize;
And the option for this occurring I guess is very specific to certain chart and/or axis dimensions and possibly a combination of certain values. Could you export problematic chart into a *.tee file so that we can load and debug it here?

Thanks in advance.