I want to know the area capture information.

TeeChart for Microsoft Visual Studio .NET, Xamarin Studio (Android, iOS & Forms) & Monodevelop.
Post Reply
park si won
Newbie
Newbie
Posts: 9
Joined: Fri Oct 22, 2021 12:00 am

I want to know the area capture information.

Post by park si won » Thu Aug 22, 2024 7:42 am

Hi, I have a question about T-chart.(Steema TeeChart for .NET Source Code 2023 4.2023.06.14 (.NET8 Compile))
While dragging and specifing some points(series) in chart, I need to get (x,y) points value in dragged area.
Is there any function or a way that I can use for it?
If so, plz let me know.(With example would be the best)

Thank you.

Edu
Newbie
Newbie
Posts: 40
Joined: Tue Nov 28, 2023 12:00 am

Re: I want to know the area capture information.

Post by Edu » Thu Aug 22, 2024 10:02 am

Hello,

There are multiple ways to achieve this. I'll show you an example.
First, declare two Points, for example, "up" and "down".

Code: Select all

        Point down, up;
Then, through these events we can give values to those points.

Code: Select all

            tChart1.MouseDown += TChart1_MouseDown;
            tChart1.MouseUp += TChart1_MouseUp;

Code: Select all

            
         private void TChart1_MouseDown(object sender, MouseEventArgs e)
         {
            down = new Point(e.X, e.Y);
         }
        
         private void TChart1_MouseUp(object sender, MouseEventArgs e)
         {
            up = new Point(e.X, e.Y);
         }            
I made the trigger be a button-click, but you can do it however you need.
Like so:

Code: Select all

        private void button1_Click(object sender, EventArgs e)
        {
            double xMin = tChart1.Axes.Bottom.CalcPosPoint(down.X);
            double xMax = tChart1.Axes.Bottom.CalcPosPoint(up.X);
            double yMin = tChart1.Axes.Left.CalcPosPoint(down.Y);
            double yMax = tChart1.Axes.Left.CalcPosPoint(up.Y);
            
             //Example: storing what points(X,Y) are inside the rectangle.
            List<PointF> points = new List<PointF>();
            for (int i = 0; i< myPoints.Count; i++)
            {
                double x = myPoints.XValues[i];
                double y = myPoints.YValues[i];

                if (x >= xMin && x <= xMax && y >= yMin && y <= yMax)
                {
                     //The point x and y is inside the rectangle.
                    points.Add(new PointF((float)x,(float)y));
                }
            }

	    //Example
            label1.Text = $"Selected points: {points.Count}";
            foreach (PointF p in points)
            {
                Console.WriteLine($"({p.X},{p.Y})");
            }

        }
Please note that I'm only demonstrating how to display the number of selected points and outputting this to the console. You should handle your data in whatever way best suits your needs.


Additionally, when getting my "xMin, xMax, yMin, yMax", I am assuming I will always draw the rectangle (mouse down, mouse up) the same way: from bottom left to top right. If you plan to drag a rectangle in different ways, you may want to improve how you decide which value is which (x max, x min.. etc) to avoid possible errors. For example:

Code: Select all

        private void Alternative()
        {
            double xMin, xMax, yMin, yMax;
            if (tChart1.Axes.Bottom.CalcPosPoint(down.X) > tChart1.Axes.Bottom.CalcPosPoint(up.X))
            {
                xMin = tChart1.Axes.Bottom.CalcPosPoint(down.X);
                xMax = tChart1.Axes.Bottom.CalcPosPoint(up.X);
            }
            else
            {
                xMin = tChart1.Axes.Bottom.CalcPosPoint(up.X);
                xMax = tChart1.Axes.Bottom.CalcPosPoint(down.X);
            }
            // Then do the same with Y ... 
        }
If you need any further assistance, if the example wasn't clear enough, or if you notice any errors or mistakes, please don't hesitate to reach out.

Regards,
Edu
Edu
Steema Support

Edu
Newbie
Newbie
Posts: 40
Joined: Tue Nov 28, 2023 12:00 am

Re: I want to know the area capture information.

Post by Edu » Thu Aug 22, 2024 1:46 pm

Hi there,
I’d like to provide some additional methods to help you achieve your goal. I'll walk you through it step by step. You can find the complete code below.

1.- Make the trigger be, instead of a button click, the chart Afterdraw (in my previous reply I was using a click as an example)

Code: Select all

 
 tChart1.AfterDraw += TChart1_AfterDraw; // AfterDraw instead of a click, this way it updates every time a rectangle is drawn
2.- You can disable the zooming when drawing rectangles through click/move:

Code: Select all

            tChart1.Zoom.Direction = Steema.TeeChart.ZoomDirections.None;
Doing so will not draw a rectangle anymore, which won't trigger AfterDraw.
Simply add "tChart1.Invalidate();" after the MouseUp event is done.
Again, this is optional. If zooming doesn't bother you, there's no need for this.

Code: Select all

        private void TChart1_MouseUp(object sender, MouseEventArgs e)
        {
            up = new Point(e.X, e.Y);
            tChart1.Invalidate();
        }
3.- You can also draw a rectangle that "stays locked" to make clear what zone was selected.
First, declare a rectangle

Code: Select all

        Rectangle rectangle;
Then, when tChart1_AfterDraw is called:

Code: Select all

            int startX = Math.Min(down.X, up.X);
            int endX = Math.Max(down.X, up.X);
            int startY = Math.Min(down.Y, up.Y);
            int endY = Math.Max(down.Y, up.Y);
            rectangle = new Rectangle(startX, startY, endX - startX, endY - startY);
            rectangle.Location = new Point(startX, startY);

            using (Pen pen = new Pen(Color.Red, 2))
            {
                tChart1.Chart.Graphics3D.Pen.Color = Color.Red;
                tChart1.Chart.Graphics3D.Pen.Width = 1;
                tChart1.Graphics3D.Brush.Transparency = 100;
                tChart1.Graphics3D.Rectangle(rectangle);
            }
4.- Improved the loop functionality and efficiency
First, thanks to this, you can draw rectangles in many directions, not just bottomLeft -> topRight

Code: Select all


            double minX = tChart1.Axes.Bottom.CalcPosPoint(startX);
            double maxX = tChart1.Axes.Bottom.CalcPosPoint(endX);
            double minY = tChart1.Axes.Left.CalcPosPoint(endY);
            double maxY = tChart1.Axes.Left.CalcPosPoint(startY);
Then, thanks to properties like myPoints.FirstVisibleIndex and LastvisibleIndex, we can exclude the points that are not shown on screen (maybe you scrolled or zoomed, you don't want to analyze the points that are 100% not selected inside your rectangle)
Also, the condition to check if a point has been selected or not, also improved to work with rectangles drawn in different directions

Code: Select all

List<PointF> pointsInsideRectangle = new List<PointF>();
// Improved loop range, excluding those that don't appear on screen
for (int i = myPoints.FirstVisibleIndex; i <= myPoints.LastVisibleIndex; i++)
{
//Get X & Y values of the point at index i
double xValue = myPoints.XValues[i];
double yValue = myPoints.YValues[i];




if (minX <= xValue 
    && maxX >= xValue
    && minY <= yValue 
    && maxY >= yValue)
{
    //The point x and y is inside the rectangle.
    pointsInsideRectangle.Add(new PointF((float)xValue, (float)yValue));
}
}
Finally, an example on how to work with the data obtained:

Code: Select all

//Example (data treatment is up to you)
label1.Text = $"Selected points: {pointsInsideRectangle.Count}";

foreach (PointF p in pointsInsideRectangle)
{
    Console.WriteLine($"({p.X},{p.Y})");
}

As promised, here is the full code of the example.

Code: Select all

public partial class Form1 : Form
{
    Point down, up;
    Points myPoints;
    Rectangle rectangle;


    public Form1()
    {
        InitializeComponent();
        //Events setup
        tChart1.MouseDown += TChart1_MouseDown;
        tChart1.MouseUp += TChart1_MouseUp;
        tChart1.AfterDraw += TChart1_AfterDraw; // AfterDraw instead of a click, this way it updates every time a rectangle is drawn
        //Series setup
        myPoints = new Points(tChart1.Chart);
        myPoints.FillSampleValues(30);

        tChart1.Zoom.Direction = Steema.TeeChart.ZoomDirections.None;
    }
    
    private void TChart1_AfterDraw(object sender, Steema.TeeChart.Drawing.Graphics3D g)
    {
        int startX = Math.Min(down.X, up.X);
        int endX = Math.Max(down.X, up.X);
        int startY = Math.Min(down.Y, up.Y);
        int endY = Math.Max(down.Y, up.Y);
        rectangle = new Rectangle(startX, startY, endX - startX, endY - startY);
        rectangle.Location = new Point(startX, startY);


        tChart1.Chart.Graphics3D.Pen.Color = Color.Red; // Color
        tChart1.Chart.Graphics3D.Pen.Width = 1; // Width
        tChart1.Graphics3D.Brush.Transparency = 100; // Make Brush transparent so it doesn't cover the chart
        tChart1.Graphics3D.Rectangle(rectangle); // Finally draw rectangle



        double minX = tChart1.Axes.Bottom.CalcPosPoint(startX);
        double maxX = tChart1.Axes.Bottom.CalcPosPoint(endX);
        double minY = tChart1.Axes.Left.CalcPosPoint(endY);
        double maxY = tChart1.Axes.Left.CalcPosPoint(startY);

        //Example: storing what points(X,Y) are inside the rectangle.
        List<PointF> pointsInsideRectangle = new List<PointF>();

        // Improved loop range, excluding those that don't appear on screen
        for (int i = myPoints.FirstVisibleIndex; i <= myPoints.LastVisibleIndex; i++)
        {
            //Get X & Y values of the point at index i
            double xValue = myPoints.XValues[i];
            double yValue = myPoints.YValues[i];

            if (minX <= xValue 
                && maxX >= xValue
                && minY <= yValue 
                && maxY >= yValue)
            {
                //The point x and y is inside the rectangle.
                pointsInsideRectangle.Add(new PointF((float)xValue, (float)yValue));
            }
        }


        //Example (data treatment is up to you)
        label1.Text = $"Selected points: {pointsInsideRectangle.Count}";

        foreach (PointF p in pointsInsideRectangle)
        {
            Console.WriteLine($"({p.X},{p.Y})");
        }
    }

    private void TChart1_MouseDown(object sender, MouseEventArgs e)
    {
        down = new Point(e.X, e.Y);
        rectangle = new Rectangle(0,0,0,0); //Optional, not really needed
    }


    private void TChart1_MouseUp(object sender, MouseEventArgs e)
    {
        up = new Point(e.X, e.Y);
        tChart1.Invalidate();
    }
}
And this is how it looks like:
AreaCapture1.png
AreaCapture1.png (40.99 KiB) Viewed 43026 times
If you have any further questions or if there's anything else I can assist you with, please don't hesitate to reach out.

Regards,
Edu
Edu
Steema Support

park si won
Newbie
Newbie
Posts: 9
Joined: Fri Oct 22, 2021 12:00 am

Re: I want to know the area capture information.

Post by park si won » Thu Aug 22, 2024 11:32 pm

I'm sorry I didn't tell you in advance. My environment is wpf and c#.net8.

private void TChart1_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
up= new System.Drawing.Point(e.X); // An error occurs in the e.X part.
}

Edu
Newbie
Newbie
Posts: 40
Joined: Tue Nov 28, 2023 12:00 am

Re: I want to know the area capture information.

Post by Edu » Fri Aug 23, 2024 7:02 am

Hello,

Don't worry! The difference isn't really that big.
Things to note:
1.- The point constructor for WPF takes two doubles
2.- When dealing with MouseButtonEventArgs, we can't use e.X or e.Y.
>> Instead, we can use e.GetPosition((UIElement)sender

For example:

Code: Select all

        private void TChart1_MouseDown1(object sender, MouseButtonEventArgs e)
        {
            down = e.GetPosition((UIElement)sender);
        }
        private void TChart1_MouseUp(object sender, MouseButtonEventArgs e)
        {
            up = e.GetPosition((UIElement)sender);
            tChart1.Invalidate();
        }
3.- When getting the values for startX, endX, startY and endY, since we get those from a Point, now we have to add a cast to int

Code: Select all

            int startX = (int)Math.Min(down.X, up.X);
            int endX = (int)Math.Max(down.X, up.X);
            int startY = (int)Math.Min(down.Y, up.Y);
            int endY = (int)Math.Max(down.Y, up.Y);
4.- If you wanted to create a visible Rectangle (like I showed before), the method is a bit different too:

Code: Select all

//(inside TChart1_AfterDraw(object sender, IGraphcis3D g)
 g.Pen.Color = System.Drawing.Color.Blue; // How to change color
 g.Brush.Transparency = 100;  // Transparency so it doesn't block vision
 g.Rectangle(startX, startY, endX, endY); // Draw rectangle
Everything else is pretty much exactly as in the examples I mentioned above.
If there's anything else I can help you with, please let me know.

Regards,
Edu
Edu
Steema Support

park si won
Newbie
Newbie
Posts: 9
Joined: Fri Oct 22, 2021 12:00 am

Re: I want to know the area capture information.

Post by park si won » Mon Aug 26, 2024 12:27 am

Thank you for your response.

When I tested the sample, I had some additional inquiries.
How do I erase the square?
function does not have a clear option.

I only want to highlight some points in the series included in the square and try to redraw the chart.
Is it possible to refresh only the corresponding points or series from a large amount of data?

Thanks

Edu
Newbie
Newbie
Posts: 40
Joined: Tue Nov 28, 2023 12:00 am

Re: I want to know the area capture information.

Post by Edu » Mon Aug 26, 2024 8:01 am

Hello again,
When I tested the sample, I had some additional inquiries.
How do I erase the square?
function does not have a clear option.
You can always use g.Rectangle again to erase the previous rectangle. If your values are 0,0,0,0 the rectangle will be invisible (so basically erasing the previous one)
Made a showcase for you: In this example, I want to have a button that erases the rectangle after I click it

Code: Select all

        bool shouldRectangleBeDeleted = false;
        private void button_Click(object sender, RoutedEventArgs e)
        {
            shouldRectangleBeDeleted = true;
            tChart1.Invalidate();
        }
        private void TChart1_AfterDraw(object sender, IGraphics3D g)
        {
            int startX = (int)Math.Min(down.X, up.X);
            int endX = (int)Math.Max(down.X, up.X);
            int startY = (int)Math.Min(down.Y, up.Y);
            int endY = (int)Math.Max(down.Y, up.Y);

            g.Brush.Transparency = 100;
            g.Pen.Color = System.Drawing.Color.Blue;
            if (shouldRectangleBeDeleted)
            {
                g.Rectangle(0, 0, 0, 0); // inviisble rectangle
                shouldRectangleBeDeleted = false; // reset boolean check after erasing
            }
            else
                g.Rectangle(startX, startY, endX, endY);
}
        
Please note that this is a basic example to demonstrate how you can control the flow of your program. You have the flexibility to trigger actions using various methods, such as key presses, different conditions, or other input mechanisms beyond a simple boolean check. The specific approach you choose will depend on how you want your program to behave and the user experience you envision. Feel free to explore and implement the method that best suits your needs.

I only want to highlight some points in the series included in the square and try to redraw the chart.
Is it possible to refresh only the corresponding points or series from a large amount of data?
You can, for example, create another Series and populate them as you "find data points inside the rectangle". Then, make the first series ("myPoints") invisible, for example. Here are some lines of code to guide you:

1.- Setting up series

Code: Select all


        Points myPoints;
        Points myHighlightedPoints;
         myPoints = new Points(tChart1.Chart);
         myHighlightedPoints = new Points(tChart1.Chart);
2.- Inside the loop in the afterdraw function, add the values to our new highlighted series

Code: Select all

           if (minX <= xValue    && maxX >= xValue   && minY <= yValue   && maxY >= yValue)
              {
                    //The point x and y is inside the rectangle.
                    pointsInsideRectangle.Add(new Point((float)xValue, (float)yValue));
                    myHighlightedPoints.Add(xValue, yValue); // add to highlighted series
              }
3.- inside a button or after drawing a rectangle or wherever you want, make the first series invisible.

Code: Select all

       
                  myPoints.Visible = false;     
Keep in mind that if you use this approach, you'll have to empty the highlightedSeries after every other area selection, or else you'd be carrying over values from your previous select.

Also note that if you make the first series invisible, the chart will automatically move its axes to fit the new data points, making your square be "in the wrong place". To solve this issue, you can use properties like the following to lock in place your chart

Code: Select all

  
tChart1.Axes.Bottom.Maximum = 100;  // Example value
tChart1.Axes.Left.Maximum = 100;
tChart1.Axes.Automatic = false;
Other approaches: You could even have a secondary tChart, which would be empty at first and then you could add the points highlighted on the main tChart.

There are many ways to implement these features, and the best approach will ultimately depend on your specific needs and preferences. I wanted to provide you with a few options to consider, but feel free to explore and choose the one that works best for you.

If there's anything else I can assist you with, or if you'd like to explore other approaches, please don't hesitate to let me know!


Regards,
Edu
Edu
Steema Support

Post Reply