Page 1 of 1

Interactive data point selection?

Posted: Fri Jan 05, 2007 10:28 pm
by 9641603
Hi,

I'm trying to figure out how to provide the following feature to my users. Let's say I have a scatter plot displayed (a Points series). The user wants to select a number of points by clicking and dragging the mouse. Selected points will then be retrieved and some additional information about them will be provided in a separate window.

How can I achieve this?

Best,
Michal Blazejczyk

Posted: Mon Jan 08, 2007 10:28 am
by narcis
Hi Michal,

Yes, you need to do this manually using chart events, for example:

Code: Select all

		private void Form1_Load(object sender, EventArgs e)
		{
			tChart1.Aspect.View3D = false;
			points1.FillSampleValues();

			tChart1.Panning.MouseButton = MouseButtons.Middle;

			ResetCoords();
		}

		private void ResetCoords()
		{
			X0 = -1;
			Y0 = -1;
		}

		private int X0, Y0;
		
		private void tChart1_MouseDown(object sender, MouseEventArgs e)
		{
			if (e.Button == MouseButtons.Right)
			{
				X0 = e.X;
				Y0 = e.Y;
			}
		}		

		private void tChart1_MouseUp(object sender, MouseEventArgs e)
		{
			listBox1.Items.Clear();

			if ((X0 != -1) && (Y0 != -1))
			{
				for (int i = 0; i < points1.Count; i++)
				{
					if ((points1.CalcXPos(i) >= X0) && (points1.CalcXPos(i) <= e.X) &&
							(points1.CalcYPos(i) >= Y0) && (points1.CalcYPos(i) <= e.Y))
					{
						listBox1.Items.Add("Point " + i.ToString() + ": " + points1.XValues[i].ToString() +
																", " + points1.YValues[i].ToString());
					}
				}

				ResetCoords();
			}
		}

		private void tChart1_MouseMove(object sender, MouseEventArgs e)
		{
			if ((X0 != -1) && (Y0 != -1))
			{
				tChart1.Graphics3D.Brush.Visible = false;
				tChart1.Graphics3D.Pen.Color = Color.Black;
				tChart1.Graphics3D.Rectangle(X0, Y0, e.X, e.Y);
			}			
		}

Posted: Mon Jan 08, 2007 9:01 pm
by 9641603
Hi,

I found a way that is easier to program:

Code: Select all

private void OnZoomed( object sender, System.EventArgs e )
{
  // Here, _chart.Axes.Bottom.Minimum, _chart.Axes.Bottom.Maximum
  // _chart.Axes.Left.Minimum, and _chart.Axes.Left.Maximum
  // delimit the selected area.
  
  // Do whatever needs to be done...

  _chart.Zoom.Undo();
}
It works very well because, as the Help says:
This event gets called BEFORE the Chart class is repainted to show the new Axis scales.
:D

Best,
Michal

Posted: Wed Mar 21, 2007 1:25 pm
by 9643849
I'm looking for the same kind of behaviour, but I have found some problems with both the above methods. The "simple one" implementing OnZoomed will unzoom any previously zooming. I want my users to be able to zoom AND select lines. So let's say they have zoomed on an area, and then tries to select some lines, Zoom.Undo() will reset the zoom.

The other example (overriding mouseUp/down/move seems to work, but the drawn box disappers as soon as the mousepointer remains on the same spot for a short while. I would like the box to always be shown (like the zoom box).

Has anyone implemented such a solution?

Regards Andreas

Posted: Wed Mar 21, 2007 3:26 pm
by narcis
Hi Andreas,

Yes, this can be achieved using TeeChart's AfterDraw event and something like this:

Code: Select all

		private void Form1_Load(object sender, EventArgs e)
		{
			tChart1.Aspect.View3D = false;
			points1.FillSampleValues();

			tChart1.Panning.MouseButton = MouseButtons.Middle;

			ResetCoords();
		}

		private void ResetCoords()
		{
			X0 = -1;
			Y0 = -1;
		}

		private int X0, Y0, X1, Y1;
		
		private void tChart1_MouseDown(object sender, MouseEventArgs e)
		{
			if (e.Button == MouseButtons.Right)
			{
				X0 = e.X;
				Y0 = e.Y;
			}
		}		

		private void tChart1_MouseUp(object sender, MouseEventArgs e)
		{
			listBox1.Items.Clear();

			if ((X0 != -1) && (Y0 != -1))
			{
				for (int i = 0; i < points1.Count; i++)
				{
					if ((points1.CalcXPos(i) >= X0) && (points1.CalcXPos(i) <= e.X) &&
							(points1.CalcYPos(i) >= Y0) && (points1.CalcYPos(i) <= e.Y))
					{
						listBox1.Items.Add("Point " + i.ToString() + ": " + points1.XValues[i].ToString() +
																", " + points1.YValues[i].ToString());
					}
				}

				ResetCoords();
			}
		}

		private void tChart1_MouseMove(object sender, MouseEventArgs e)
		{			
			if (e.Button == MouseButtons.Right)
			{
				X1 = e.X;
				Y1 = e.Y;
			}

			tChart1.Invalidate();
		}

		private void tChart1_AfterDraw(object sender, Steema.TeeChart.Drawing.Graphics3D g)
		{
			if ((X0 != -1) && (Y0 != -1))
			{
				g.Brush.Visible = false;
				g.Pen.Color = Color.Black;
				g.Rectangle(X0, Y0, X1, Y1);
			}
		}

Posted: Wed Mar 21, 2007 3:50 pm
by 9643849
Oh, great, I came up with something similiar (using the OnPaint) method, but I changed to use After draw. However, tChart1.Invalidate(); should be done inside if(e.Button == MouseButtons.Right), otherwise the zoombox (leftclicking and dragging) will flipp out... (don't ask me why) =)

Well, my problem is solved.

My code for checking if any point/line is selected is this:
foreach (Line line in Series)
{
for (int i = 0; i < line.Count; i++)
{
if ((line.CalcXPos(i) >= X0) && (line.CalcXPos(i) <= e.X) &&
(line.CalcYPos(i) >= Y0) && (line.CalcYPos(i) <= e.Y))
{
// Found a selected point, add it to some collection
break;
}
}
}

I haven't played with a large scale of lines yet, but I guess this method could become timeconsuming... or? Is there a better way? I need to know witch lines my "selection box" includes.

Posted: Wed Mar 21, 2007 4:32 pm
by narcis
Hi Wicket,
I haven't played with a large scale of lines yet, but I guess this method could become timeconsuming... or? Is there a better way?
Yes, you are right. I can't think of a better way to achieve that at the moment.
I need to know witch lines my "selection box" includes.
Sorry but I don't understand what do you exactly mean. Could you please give us some more information?

Thanks in advance.

Posted: Wed Mar 21, 2007 9:22 pm
by 9643849
Lets say I have 10 lines drawn on the chart. Then I make a 'selectionbox'. After releasing the mousebutton I want to find out witch of my 10 lies that passes through the box. As my implementation is made right now, I'm only checking the points of the lines, so if a line is passing through the box, but doesn't have a point inside the box, the line will not be selected.

In fact, individual points are not interested for me at all, it's either the complete line or nothing at all. So if there exist a mote efficient way to tell if some part of a line (serie) passes through a rectangualr box, I'd be glad to hear about it.

Regards Andreas

Posted: Thu Mar 22, 2007 8:44 am
by narcis
Hi Andreas,

To achieve what you request you should implement MouseUp event as shown below. To optimize the implementation you could add an array and mark which series have been already selected and check this before doing redundant processing.

Code: Select all

		private void tChart1_MouseUp(object sender, MouseEventArgs e)
		{
			listBox1.Items.Clear();

			if ((X0 != -1) && (Y0 != -1))
			{
				for (int x = X0; x <= X1; x++)
					for (int y = Y0; y <= Y1; y++)
						for (int i = 0; i < tChart1.Series.Count; i++)
							if (tChart1[i].Clicked(x, y) != -1)
								listBox1.Items.Add(tChart1[i].Title);

				ResetCoords();
			}
		}