Synchronizing Axes
Synchronizing Axes
My standard chart consists on an independent axis (e.g. Depth on the left axis) and two dependent axes (e.g. Pressure and Temperature) plotted on the two horizontal axes. The dependent axes are usually set to Automatic scaling to handle the pressure and temperature data range.
To make the graphs look nicer, I would like to be able to synchronize the top axis with the bottom axis  e.g. go to each major tick on the bottom axis, calculate the corresponding value on the top axis and write the axis label accordingly.
My code works only if both axes are set to Automatic. I cannot get the axes to synchronize at all if one or both axes are set to Logarithmic (note I use custom labels for Logarithmic axes), and I am not succeeding with Inverted.
I have attached a test program and would appreciate any assistance you can offer.
Thanks and regards
Errol
To make the graphs look nicer, I would like to be able to synchronize the top axis with the bottom axis  e.g. go to each major tick on the bottom axis, calculate the corresponding value on the top axis and write the axis label accordingly.
My code works only if both axes are set to Automatic. I cannot get the axes to synchronize at all if one or both axes are set to Logarithmic (note I use custom labels for Logarithmic axes), and I am not succeeding with Inverted.
I have attached a test program and would appreciate any assistance you can offer.
Thanks and regards
Errol
 Attachments

 DeviatedPT.zip
 (78.84 KiB) Downloaded 289 times
Re: Synchronizing Axes
Hello,
I'm not sure if this is a test project or you final project, but it starts to have quite a lot of options and combinations. If you need help in one combination (ie synchronizing top and bottom axes when they are logarithmic) we need to focus on it, so please arrange a simple example project we can run asis to reproduce the problem here.
Once we find a way to make that simple example to work as expected, then you'll be able to investigate what's the best way to insert it into your logic.
I'm not sure if this is a test project or you final project, but it starts to have quite a lot of options and combinations. If you need help in one combination (ie synchronizing top and bottom axes when they are logarithmic) we need to focus on it, so please arrange a simple example project we can run asis to reproduce the problem here.
Once we find a way to make that simple example to work as expected, then you'll be able to investigate what's the best way to insert it into your logic.
Best Regards,
Yeray Alonso Development & Support Steema Software Av. Montilivi 33, 17003 Girona, Catalonia (SP)  
Please read our Bug Fixing Policy 
Re: Synchronizing Axes
Hi Yeray
Thank you for your reply. The project is a test project which reflects (some of) the complexity of the main project. I was hoping to pick up some design principles which I could apply to the main project.
I have attached a simplified project, concentrating solely on synchronizing the top and bottom axes. These axes synchronize only if Autoscaling is selected on one or both axes. When I invert an axis, the corresponding series vanish. I have wrestled with this for some time without success.
I look forward to any suggestions you might offer.
Thanks and regards
Errol
Thank you for your reply. The project is a test project which reflects (some of) the complexity of the main project. I was hoping to pick up some design principles which I could apply to the main project.
I have attached a simplified project, concentrating solely on synchronizing the top and bottom axes. These axes synchronize only if Autoscaling is selected on one or both axes. When I invert an axis, the corresponding series vanish. I have wrestled with this for some time without success.
I look forward to any suggestions you might offer.
Thanks and regards
Errol
 Attachments

 Sync_Axes.zip
 (62.33 KiB) Downloaded 286 times
Re: Synchronizing Axes
Hello,
When the "Synchronize dependent axes" checkbox is checked, you are setting the Top axis with custom labels (SyncTopAxis method) and immediately after it, you are calling ApplyChartLogLabels(True) which resets the Top axis to use Automatic labels.
Then, at SyncTopAxis, instead of using the Bottom Minimum&Maximum values, you could try using the last&first Bottom Axis Items to populate the Top Axis Labels. Ie:
When the "Synchronize dependent axes" checkbox is checked, you are setting the Top axis with custom labels (SyncTopAxis method) and immediately after it, you are calling ApplyChartLogLabels(True) which resets the Top axis to use Automatic labels.
Then, at SyncTopAxis, instead of using the Bottom Minimum&Maximum values, you could try using the last&first Bottom Axis Items to populate the Top Axis Labels. Ie:
Code: Select all
BMin:=DBChart1.Axes.Bottom.Items[DBChart1.Axes.Bottom.Items.Count1].Value;
BMax:=DBChart1.Axes.Bottom.Items[0].Value;
TPos:=BMin;
while (BMax >= TPos) do
begin
DBChart1.Axes.Top.Items.Add(TPos, FloatToStr(TPos)); //convert bottom axis values to top axis values
TPos:=TPos+BInc;
end;
Best Regards,
Yeray Alonso Development & Support Steema Software Av. Montilivi 33, 17003 Girona, Catalonia (SP)  
Please read our Bug Fixing Policy 
Re: Synchronizing Axes
Thanks for the reply. I have fixed the reset caused by ApplyLogLabels by calling ApplyLogLabels(False). However, I do not understand your proposed solution using bottom axis positions and values to populate the top axis. In the example in the Sync_Axes project, the position range of the bottom axis is from 25 to 110, while the top axis is from 0 to 350. When I try your code, the top axis labels are bunched up at the left.
In my revised SyncTopAxis code, I obtain the position of each label on the bottom axis and convert it to the correct position on the top axis using simple ratio calculations. I do not understand why the top axis labels are not drawn at exactly the position of the bottom axis labels.
In my revised SyncTopAxis code, I obtain the position of each label on the bottom axis and convert it to the correct position on the top axis using simple ratio calculations. I do not understand why the top axis labels are not drawn at exactly the position of the bottom axis labels.
Code: Select all
// ========== TfrmPTChart.SyncTopAxis ==========================================
// Uses bottom axis increments to locate top axis labels
procedure TfrmPTChart.SyncTopAxis;
var
i, iMin, iMax, Factor: integer;
d: double;
Tval, TPos, TMin, TMax, TDiff, BMin, BMax, BDiff, BInc: double;
begin
DBChart1.Axes.Top.Items.Clear;
if DBChart1.Axes.Top.Automatic then
begin
TMin := DBChart1.Axes.Top.Minimum;
TMax := DBChart1.Axes.Top.Maximum;
end
else
begin
TMin := 0; // arbitrary values
TMax := 350;
end;
TDiff := TMax  Tmin;
if DBChart1.Axes.Bottom.Automatic then
begin
BMin := DBChart1.Axes.Bottom.Minimum;
BMax := DBChart1.Axes.Bottom.Maximum;
BInc := DBChart1.Axes.Bottom.CalcIncrement;
end
else
begin
DBChart1.Axes.Bottom.Minimum := 25;
DBChart1.Axes.Bottom.Maximum := 110;
DBChart1.Axes.Bottom.Increment := 20;
BMin := 25;
BMax := 110;
BInc := 20;
end;
BDiff := BMax  BMin;
iMax := DBChart1.Axes.Bottom.Items.Count;
for i := 0 to iMax  1 do
begin
Tval := Round((DBChart1.Axes.Bottom.Items[iMax  i  1].Value  BMin)/BDiff*TDiff + TMin);
TPos := (DBChart1.Axes.Bottom.Items[iMax  i  1].Value  BMin)/BDiff*TDiff + TMin;
DBChart1.Axes.Top.Items.Add(TPos, FloatToStr(Tval)); // adding custom label
end;
end;
Re: Synchronizing Axes
I apologize for my last post  the error was mine, by assigning different Axis minimum and maximum values at different parts of the code, now corrected. I still have a problem whenever I change the bottom axis Automatic status with Synchronize checked. The code draws the top axis labels using the previous bottom axis labels, and then redraws the bottom axis labels, so they are not synchronized. I clearly have events not firing in the correct order  please advise.
 Attachments

 Sync_Axes.zip
 (58.94 KiB) Downloaded 280 times
Re: Synchronizing Axes
Hello,
I wouldn't call SyncTopAxis at OnBeforeDrawAxes or any other event being fired during the paint process unless necessary.
Instead, you could try to call it at the end of cbxAutoBottomClick and cbxAutoTopClick.
On the other hand, SyncTopAxis uses some axis variables that need the chart to be at advanced stages of the drawing routine to get the correct values, so you should force a chart repaint at the start of that method. At the same time, you are changing the Bottom axis scale when it's not Automatic, and this needs another repaint in order to have the correct internal Items later, so you should force another repaint here. Here the complete method that seems to work for me:
I wouldn't call SyncTopAxis at OnBeforeDrawAxes or any other event being fired during the paint process unless necessary.
Instead, you could try to call it at the end of cbxAutoBottomClick and cbxAutoTopClick.
On the other hand, SyncTopAxis uses some axis variables that need the chart to be at advanced stages of the drawing routine to get the correct values, so you should force a chart repaint at the start of that method. At the same time, you are changing the Bottom axis scale when it's not Automatic, and this needs another repaint in order to have the correct internal Items later, so you should force another repaint here. Here the complete method that seems to work for me:
Code: Select all
procedure TfrmPTChart.SyncTopAxis;
var
i, iMin, iMax, Factor: integer;
d: double;
Tval, TPos, TMin, TMax, TDiff, BMin, BMax, BDiff, BInc: double;
begin
DBChart1.Draw;
Factor := 1;
if DBChart1.Axes.Top.Automatic then
begin
TMin := DBChart1.Axes.Top.Minimum;
TMax := DBChart1.Axes.Top.Maximum;
end
else
begin
TMin := TopMin; // arbitrary values
TMax := TopMax;
end;
TDiff := TMax  Tmin;
if DBChart1.Axes.Bottom.Automatic then
begin
BMin := DBChart1.Axes.Bottom.Minimum;
BMax := DBChart1.Axes.Bottom.Maximum;
BInc := DBChart1.Axes.Bottom.CalcIncrement;
end
else
begin
DBChart1.Axes.Bottom.Minimum := BotMin;
DBChart1.Axes.Bottom.Maximum := BotMax;
DBChart1.Axes.Bottom.Increment := 20;
BMin := DBChart1.Axes.Bottom.Minimum;
BMax := DBChart1.Axes.Bottom.Maximum;
BInc := 20;
DBChart1.Draw;
end;
BDiff := BMax  BMin;
DBChart1.Axes.Top.Items.Clear;
iMax := DBChart1.Axes.Bottom.Items.Count;
for i := 0 to iMax  1 do
begin
Tval := Round((DBChart1.Axes.Bottom.Items[iMax  i  1].Value  BMin)/BDiff*TDiff + TMin);
TPos := (DBChart1.Axes.Bottom.Items[iMax  i  1].Value  BMin)/BDiff*TDiff + TMin;
DBChart1.Axes.Top.Items.Add(TPos, FloatToStr(Tval)); // adding custom label
end;
end;
Best Regards,
Yeray Alonso Development & Support Steema Software Av. Montilivi 33, 17003 Girona, Catalonia (SP)  
Please read our Bug Fixing Policy 
Re: Synchronizing Axes
Good evening
I have implemented your suggestions and have the top and bottom axes synchronizing nicely when changing the autoscaling of each axis. However I am baffled by the Invert behaviour  the relevant series disappear (nothing to do with axis synchronization). Clearly doing something wrong again. Also I do not know how to proceed with the logarithmic option  I hope to be able to avoid using an event  simply step through the logarithmic labels on the bottom axis and calculate the labels on the top axis, but it is not working for me.
I have attached a revised test program.
Thanks and regards
Errol
I have implemented your suggestions and have the top and bottom axes synchronizing nicely when changing the autoscaling of each axis. However I am baffled by the Invert behaviour  the relevant series disappear (nothing to do with axis synchronization). Clearly doing something wrong again. Also I do not know how to proceed with the logarithmic option  I hope to be able to avoid using an event  simply step through the logarithmic labels on the bottom axis and calculate the labels on the top axis, but it is not working for me.
I have attached a revised test program.
Thanks and regards
Errol
 Attachments

 Sync_Axes.zip
 (65.61 KiB) Downloaded 280 times
Re: Synchronizing Axes
Good evening
I am pleased to report that I have enabled axis synchronization with changes of autoscaling, axis inversion, and logarithmic axes. I have attached some relevant code below as there is some tricky code when dealing with log axes, if other people are interested. This does not apply to my previous test project, but I feel the intention is clear enough.
Thank and regards
Errol
I am pleased to report that I have enabled axis synchronization with changes of autoscaling, axis inversion, and logarithmic axes. I have attached some relevant code below as there is some tricky code when dealing with log axes, if other people are interested. This does not apply to my previous test project, but I feel the intention is clear enough.
Code: Select all
// ========== TPBQuickGraph.SyncDep2Axis =======================================
// Uses 1st dependent axis increments to locate 2nd dependent axis labels
procedure TPBQuickGraph.SyncDep2Axis;
var
i, iMin, iMax, Factor: integer;
d: double;
TVal, TPos, TMin, TMax, BVal, BMin, BMax, BInc: double;
begin
Factor := 1;
if not SyncAxes_1 or not bLineGraph then
Exit;
Chart.Draw;
TMax := Chart.Axes.Top.Maximum;
TMin := Chart.Axes.Top.Minimum;
BMax := Chart.Axes.Bottom.Maximum;
BMin := Chart.Axes.Bottom.Minimum;
BInc := Chart.Axes.Bottom.CalcIncrement;
if (fDependent1Auto and not fDependent2Auto) then
begin
Chart.Axes.Bottom.Automatic := False;
Chart.Axes.Bottom.Maximum := BMax;
Chart.Axes.Bottom.Minimum := BMin;
end
else
if fDependent2Auto and not fDependent1Auto then
begin
Chart.Axes.Top.Automatic := False;
Chart.Axes.Top.Maximum := TMax;
Chart.Axes.Top.Minimum := TMin;
end;
Chart.Axes.Top.Items.Clear;
// this seems to work when inverting axes
if (fInvertDep1 <> fInvertDep2) then
Chart.Draw;
iMax := Chart.Axes.Bottom.Items.Count;
for i := 0 to iMax  1 do
begin
BVal := Chart.Axes.Bottom.Items[iMax  i  1].Value;
if (fLogDep1 and not fLogDep2) then
TPos := ((log10(BVal)  log10(BMin))/(log10(BMax)  log10(BMin)))*(TMaxTMin) + TMin
else
if (not fLogDep1 and fLogDep2) then
TPos := power(10,(BVal  BMin)/(BMaxBMin)*(log10(TMax)  log10(TMin)) + log10(TMin))
else
if (fLogDep1 and fLogDep2) then
TPos := power(10,((log10(BVal)log10(BMin))/(log10(BMax)log10(BMin))*(log10(TMax)log10(TMin))+log10(TMin)))
else
TPos := (Chart.Axes.Bottom.Items[iMax  i  1].Value  BMin)/(BMaxBMin)*(TMaxTMin) + TMin;
TVal := Round(TPos);
Chart.Axes.Top.Items.Add(TPos, FloatToStr(TVal)); // adding custom label
end;
end;
Errol