Graphic creation c#

Asked

Viewed 361 times

0

Good afternoon friends, I am creating a program in c# that should plot in a graph the current values of the serial port (Arduino) in a picture box. To draw the graph I am creating points(x,y) and connecting the same forming lines (drawline). In this way the graphic is drawn, but the "pen" that runs through the picturebox. I’m trying to make an effect similar to a seismograph (as if the paper(picturebox) dislocated, as in the video https://www.youtube.com/watch?v=0cSlxPeqHA8). I believe it is basically to shift several lines. Currently, I am doing it this way:

int contDRAW = 0, a; //contadores
private PointF[] vetpoint = new PointF[6000];//vetor para incrementar o X para fazer o deslocamento
float[] aquisicaoplot = new float[6000]; //vetor onde armazeno cada valor enviado pela serial

//a cada vez que for chamada a função desenhar já tenho um novo y
private void Desenhar()
        {
            vetpoint[contDRAW].Y = aquisicaoplot[contDRAW];
            for (a = 0; a <= contDRAW; a++)
            {
                if (contDRAW != a)
                {
                    vetpoint[a].X ++;
                }
                else vetpoint[a].X = 0;

                if (contDRAW == 0 || a==0) timerDRAW.Enabled = true;
                else graph.DrawLine(new Pen(Color.LightGreen, 0.1f), vetpoint[a - 1].X, 24 * aquisicaoplot[a-1], vetpoint[a].X, 24 *aquisicaoplot[a]);     
            }
            contDRAW++;
        }

private void timerDRAW_Tick(object sender, EventArgs e)
{
    Refresh();
}

This code comes close to the intended result. The chart is currently shifting, but it is reset and redrawn every time it enters "for". What can I do? Am I following the right path? If anyone knows or has any tips on how to do this please reply.

EDIT1:

I updated the Draw() function to the following:

private void Desenhar()
{
    System.Drawing.Pen myPen = new System.Drawing.Pen(System.Drawing.Color.LimeGreen, 0.1f);
    vetpoint[contDRAW].Y = 24*aquisicaoplot[contDRAW];
    for (a = 0; a <= contDRAW; a++)
    {
        if (contDRAW != a) vetpoint[a].X++;
        else vetpoint[a].X = 0;
    }
    if (contDRAW == 0) timerDRAW.Enabled = true;
    else graph.DrawLines(myPen, vetpoint);
    //else graph.DrawLine(myPen, vetpoint[contDRAW - 1].X, 24 * aquisicaoplot[contDRAW-1], vetpoint[contDRAW].X, 24 * aquisicaoplot[contDRAW]);
    contDRAW++;
    myPen.Dispose();
}

I had a small advance in relation to the effect I want to produce, because the graph moves without being redesigned from the beginning, however, now it is with a lot of Flicker and delay after drawing several points.

  • Whatever the method is Refresh() does? He was called in the method timerDRAW_Tick and was not posted. And where the function Desenhar is called?

  • The function Draw( ) is called every time I have a new data in the serial. That is, another y that must be placed on the graph. Refresh( ) would be to "update" the chart with the new points shifted.

  • Every time I read a serial data I draw the new point in the coordinate (0,0) and move the remaining points to the right.

1 answer

0


See if you can help him. Set the Minvalue and Maxvalue and Datacount properties - Amount of data that will come.. Just create a timer and call the Addvalue method every time.

using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace WindowsFormsApp2
{

public class GraphControl : Control
{
    private int _minValue = -100;
    private int _maxValue = 100;
    private int _dataCount = 25;
    private List<int> _values = new List<int>();

    public int MinValue
    {
        get => _minValue;
        set
        {
            _minValue = value;
            Invalidate();
        }
    }
    public int MaxValue
    {
        get => _maxValue;
        set
        {
            _maxValue = value;
            Invalidate();
        }
    }
    public int DataCount
    {
        get => _dataCount;
        set
        {
            _dataCount = value;
            Invalidate();
        }
    }

    public GraphControl()
    {
        Size = new System.Drawing.Size(500, 300);
        SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);
    }

    public void AddValue(int value)
    {
        _values.Add(value);
        Invalidate();
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        e.Graphics.FillRectangle(Brushes.White, ClientRectangle);
        e.Graphics.DrawRectangle(Pens.Gray, 0, 0, Width - 1, Height - 1);
        e.Graphics.DrawLine(Pens.LightGray, 0F, (float)Height * 0.5F, (float)Width, (float)Height * 0.5F);

        var wid = (float)Width / (float)_dataCount;

        for (var i = 0; i < _dataCount; i++)
            e.Graphics.DrawLine(Pens.LightGray, wid * i, 0F, wid * i, Height);

        var lastPoint = new PointF(0F, (float)Height * 0.5F);
        var idInit = _values.Count - _dataCount;

        if (idInit < 0)
            idInit = 0;

        for (int i = idInit, a = 0; i < _values.Count; i++, a++)
        {
            var value = (float)(_values[i] - MinValue);
            var total = (float)(MaxValue - MinValue);
            var porcent = value / total;
            var hg = porcent * Height;
            var newPoint = new PointF((a + 1) * wid, Height - hg);

            using (var p = new Pen(Color.Maroon, 2))
                e.Graphics.DrawLine(p, lastPoint, newPoint);

            lastPoint = newPoint;
        }

        base.OnPaint(e);
    }
}
}

Add control to your project, Compile and then add to Windows Forms via Toolbox.

Example with random values:

        private void timer1_Tick(object sender, EventArgs e)
    {
        var rd = new Random();
        var num = (rd.Next() % 200) - 100;

        graphControl1.AddValue(num);
    }
  • did you make any changes to the original code? What version . net are you using? Your msm ne Windowforms application? The code in the original version worked?

  • I made a test file and managed to add the control, but without any result in the execution, only appears a gray rectangle, which was created in control. Follow the code, I’m getting the 'y' from the serial port.

  • private void timer1_Tick(object sender, EventArgs e)&#xA;{&#xA; SerialCOM.DataReceived += new SerialDataReceivedEventHandler(SerialCOM_DataReceived);&#xA; graphControl1.AddValue(atualdata);&#xA;}&#xA;//atualdata é o que estou atualizando com o datareceived

  • made table test and the datareceived is working correctly, the desired value is added in 'actualdata'. However no data is plotted on the screen.

  • got it. take the test with the timer code I made just for test.

  • Maybe it’s just a matter of changing the minvalue and maxvalue of Graphcontrol, give me a list of values 10 to 20 more or less that are received by the serial port for me to take the test.

  • are float values from 0 to 5.

  • Pq does not use graphControl1.Addvalue() in the Serialcom_datareceived method?

  • Man, it apparently worked here, thank you very much!!

  • Saved a lot !!

  • blz! any doubt only post

  • Friend, just one more question, the graph being drawn very small on the screen, I would like to make the 0 to 5 correspond respectively to 0 to 120 height. What I may be changing in the control class??

  • I got it, buddy, thanks. It’s really good! The only problem is that the values of _minValue and _maxValue do not change to what I put, it is always 100 and -100. I checked through the inspection. I manually adjusted in the calculations for point determination and is working as expected now.

  • ok. to change the values of 100 and -100 do not change the code. Change by the control properties, as if it were any other control. Type a Textbox.

  • good afternoon buddy, all right? I needed to make 3 charts at the same time. Da to use the same control?

  • yes, you can use it

Show 11 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.