Page 1 of 3

Getting pixel values from axis label values

Posted: Thu Aug 28, 2008 10:14 pm
by 10545848
I am trying to save the coordinates of a polygon that I marked on an image
displayed on a TColorGrid so that I can redraw the polygon at a later time.
My users save the polygon vertex values, and at a later date, draw a new image
using a different target and want to redraw the saved polygon.

Below is some example code that I use...


//
// This is the way I put data into the TColorGridSeries. My image data is
// stored in a 2D array of doubles called DblImageData. Note that IrrGrid is
// NOT defined because this code is still VERY slow.
//
for i := 0 to High(DblImageData) do
for j := 0 to High(DblImageData[0]) do
begin
{$ifndef IrrGrid}
// NOTE:
// We are adding 1 to the "X" and "Z" parameters as a work around for a bug
// steema has in their code that is suppose to be fixed in the next release.
// Setting IrregularGrid := True cause images to update REALLY slowly!
(Chart1.Series[SIndex] as TColorGridSeries).AddXYZ(i+1, DblImageData[i,j], j+1);
{$else}
// The axis here is much nicer, but redrawing the image is very slow when
// resizing.
(Chart1.Series[SIndex] as TColorGridSeries).AddXYZ(xScaleOff+i*xScaleMul,
DblImageData[i,j], zScaleOff+j*zScaleMul);
{$endif}
end;

//
// This is the way I fill in the Axis labels when IrregulatGrid is set to false
// which it is in my application.
//
{$ifndef IrrGrid}
// Fill in labels for the X axis.
for i := 0 to High(DblImageData) do
begin
x := i * xScaleMul + xScaleOff;
if (i mod (Length(DblImageData) Div 10) = 0) then
Chart1.Axes.Bottom.Items.add(i,Format('%-.2f',[x]));
end;

// Fill in labels for the Y axis.
for j := 0 to High(DblImageData[0]) do
begin
x := j * zScaleMul + zScaleOff;
if (j mod (Length(DblImageData) Div 10) = 0) then
Chart1.Axes.Left.Items.add(j,Format('%-.2f',[x]));
end;
{$Endif}


//
// I then draw a polygon on the image by getting mouse clicks and saving the
// axis values.
//
// So in procedure TPlotForm.Chart1MouseDown, I get the axis values of points
// clicked on the screen to draw a polygon. I save these points so that later I
// can redraw the polygon on another image with a different zoom and different
// screen size.
//
// NOTE: This is only a code snippet, it is not complete. Unessary application
// specific code and initialization was removed to simplify this example.
//
procedure TPlotForm.Chart1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
fIER_Array[High(fIER_Array)].X := X;
fIER_Array[High(fIER_Array)].Y := Y;

// Add the first vertex to the end of the list so we can close the polygon
// when we draw it
SetLength(fIER_Array, Length(fIER_Array)+1);
fIER_Array[High(fIER_Array)].X := fIER_Array[0].X;
fIER_Array[High(fIER_Array)].Y := fIER_Array[0].Y;

// Save polygon if there is more than 2 points in it.
if Length(fIER_Array) > 2 then
begin
// Draw the polygon.
Chart1.Canvas.Pen.Width := 1;
Chart1.Canvas.Polyline(fIER_Array);

// Save the current polygon in the IER shape list.
SetLength(fIERRecList, Length(fIERRecList)+1);
j := High(fIERRecList);
i := Length(fIER_Array);

// Pixel locations of each vertex
SetLength(fIERRecList[j].Point_List, i);
SetLength(fIERRecList[j].X_Values, i); // X axis values
SetLength(fIERRecList[j].Y_Values, i); // Y axis values

for i := 0 to High(fIER_Array) do
begin
// Save the X and Y pixel positions or the vertex points. This is
// really useless because the images sizez will be different!!
fIERRecList[j].Point_List := fIER_Array;

// Get the index of the point clicked.
k := Chart1.Series[0].Clicked(fIER_Array.X,fIER_Array.Y);

// Save the value of the axis label of the point clicked.
fIERRecList[j].X_Values := (k div (Chart1.Series[0] as TColorGridSeries).NumZValues) *
xScaleMul + xScaleOff;
fIERRecList[j].Y_Values := (k mod (Chart1.Series[0] as TColorGridSeries).NumZValues) *
zScaleMul + zScaleOff ;
end;
end;
end;



Now that I have the axis label values of the points in the polygon, how can I
go backwards to the pixel locations on a new image made sometime inthe future?

For example, if I collect data on a different target and display the image, I
want to redraw the polygon on the new target at the same x/y positions.

How do I get the pixel locations from the fIERRecList[j].X_Value and
fIERRecList[j].Y_Value arrays that I saved earlier?

Is there a better way to do this with Irregular grid set to False?

Posted: Fri Aug 29, 2008 8:03 am
by narcis
Hi dpatch,
How do I get the pixel locations from the fIERRecList[j].X_Value and
fIERRecList[j].Y_Value arrays that I saved earlier?
You can use CalcPosValue method for that:

Code: Select all

  XPos:=Chart1.Axes.Bottom.CalcPosValue(fIERRecList[j].X_Value);
  YPos:=Chart1.Axes.Left.CalcPosValue(fIERRecList[j].Y_Value);
Is there a better way to do this with Irregular grid set to False?
Not that I can think of. It is recommended to use IrregularGrid=false whenever possible as it makes series much more eficient.

Posted: Fri Aug 29, 2008 2:25 pm
by 10545848
I saw the CalcPosValue method however, it takes an integer as input and my arrays are floating point. Is there an overloaded version that I did not see? I really need to be able to do this. Thanks...

Posted: Fri Aug 29, 2008 2:37 pm
by narcis
Hi dpatch,

No, CalcPosValue takes a double value for me and returns an integer. In a similar way, you can use CalcXPosValue and CalcYPosValue methods:
XPos:=Chart1.Axes.Bottom.CalcXPosValue(fIERRecList[j].X_Value);
YPos:=Chart1.Axes.Left.CalcYPosValue(fIERRecList[j].Y_Value);
Hope this helps!

Posted: Fri Aug 29, 2008 8:22 pm
by 10545848
Yes, I see that you are correct. I was looking at the series1.CalcPosValue example in your help file.

When I call the example code you supplied, I did not get the right answer. I wonder if I did not save the axis value in the array X_Value correctly. Could you look at the end of the example code where I calculated this value and tell me if it is wrong?

X and Y are gotten in the OnMouseDown event.

// Get the index of the point clicked.
k := Chart1.Series[0].Clicked(X, Y);

// Save the value of the axis label of the point clicked.
fIERRecList[j].X_Values := (k div (Chart1.Series[0] as TColorGridSeries).NumZValues) * xScaleMul + xScaleOff;
fIERRecList[j].Y_Values := (k mod (Chart1.Series[0] as TColorGridSeries).NumZValues) * zScaleMul + zScaleOff ;

Thank you.

Posted: Mon Sep 01, 2008 8:36 am
by narcis
Hi dpatch,

You could try something easier:

Code: Select all

  fIERRecList[j].X_Values[i] := Chart1[0].XValue[k];
  fIERRecList[j].Y_Values[i] := Chart1[0].YValue[k];

Posted: Mon Sep 01, 2008 3:32 pm
by 10545848
There are 2 problems with the solution that you recommend.

The first is that your solution returns the "integer" index of the X/Y pixel locations on the original axis when the Mouse Down event was generated. This index will not longer be valid on the next image that the uses generages...especially if it is at a different zoom. I also do not believe that calculating the axis value works with this index.

Secondly, I am not trying to get the index of the chart, I am trying to get the floating point value that I put into the axis label for that index. If you look carefully at my code segment below, you will see that I have a for loop that generates a floating point axis label value starting at an offset and multiplying by a scaling factor.

Is there a way to get directly to the floating point value of the axis from the X/Y coordinates in the Mouse Down event? Also, I need to get back from the floating point to the X/Y coordinate (or the pixel location) to be able to redraw the polygon an o different plot.

If this explanation is not clear, please make me explain myself better. I really need to be able to do this. I am afraid that this is not working because I am using IregularGrid := False.

Posted: Tue Sep 02, 2008 8:15 am
by narcis
Hi dpatch,

Thanks for the information.
Is there a way to get directly to the floating point value of the axis from the X/Y coordinates in the Mouse Down event?
Yes, in that case you can use CalcPosPoint method:

Code: Select all

  XVal:=Chart1.Axes.Bottom.CalcPosPoint(X);
  YVal:=Chart1.Axes.Left.CalcPosPoint(Y);
Also, I need to get back from the floating point to the X/Y coordinate (or the pixel location) to be able to redraw the polygon an o different plot.
For the inverse operation you can use this:

Code: Select all

  X:=Chart1.Axes.Bottom.CalcXPosValue(XVal);
  Y:=Chart1.Axes.Left.CalcYPosValue(YVal);

Posted: Tue Sep 02, 2008 5:15 pm
by 10545848
Initially I tried this and it did not work. However, It was a problem on my side.

This works GREAT! Thank you very much!

Posted: Wed Sep 10, 2008 1:35 pm
by 10545848
OK, maybe I replied too fast...

The methods CalcPosPoint and CalcXPosValue do seem to work in that they allow me to redraw polygons in absoultuely the correct positions on different images. However, if you look at the axis positions values CalcPosPoint returns, it does not match the value on the x or y axis. Meaning, I have an axis scale that goes from -4.00 to +4.00 and CalcXPoint returns a number like 48.35475.

It's good that I can redraw the polygons, but I can't display the vertex values because they still are not correct.

Any thoughts on what I might be doing wrong?

Posted: Wed Sep 10, 2008 1:43 pm
by narcis
Hi dpatch,

Could you please send us a simple example project we can run "as-is" to reproduce the problem here?

You can either post your files at news://www.steema.net/steema.public.attachments newsgroup or at our upload page.

Thanks in advance.

Posted: Wed Sep 10, 2008 2:00 pm
by 10545848
Yes, I will try to, but it is quite a lot of code...

It seems that the disconnect is between the values I load into the axis using:
Chart1.Series[SIndex].AddXYZ(i+1, DblImageData[i,j], j+1)

and the values I load into the x axis labels using:
Chart1.Axes.Bottom.Items.add(i,Format('%-.2f',[x]))

The code solution you gave me works, but it is calculating positions based on the values in chart.XValues array, not the values in the Chart.Axes label.

Is there a way to tie back to the values displayed onthe label? If so, I will put together some sample code and get it to you...

Thanks

Posted: Wed Sep 10, 2008 2:20 pm
by narcis
Hi dpatch,

I might be not understanding what you are trying to do. That's why a little code example would help.

Anyway, you could try using series' XScreenToValue and YScreenToValue which gives an axis value from a screen coordinate.

Posted: Wed Sep 10, 2008 6:30 pm
by 10545848
OK, I sent a zip file with sample code to your upload site called "Image Test.zip".

I build this with Delphi 2006.

Let me know if you have any questions...

Thanks!

Posted: Fri Sep 12, 2008 1:12 pm
by 10545848
Sorry to be a bother, I know you are busy. But did you receive the example code that I posted for you? Can you rebuild it and is it self explanitory enough?

Thanks