ScottPlot에 해당하는 글 3

ScottPlot Multi Axis 그리기 - C#

Software/C#|2024. 1. 18. 09:19
반응형

ScottPlot 가 버젼 5로 업데이트 되고 사용하는 방법이나 함수가 많이 변경이 된듯 합니다. 

이번 포스팅에서는 버젼 5를 기준으로 멀티 축을 그리는 방법에 대한 코드를 공유합니다. 

 

MultiAxis.zip
0.13MB

        (소스 코드)

    public partial class Form1 : Form
    {
        private ScottPlot.AxisPanels.RightAxis? YRight2 = null;  // 오른쪽 Y축 추가 
        private ScottPlot.AxisPanels.LeftAxis? YLeft2 = null;    // 왼쪽 Y축 추가 

        public Form1()
        {
            InitializeComponent();
            graphInit();
        }
        private void graphInit()
        {
            YLeft2 = formsPlot1.Plot.Axes.AddLeftAxis();        // 폼 디자인에 올려진 그래프 컨트롤에 왼쪽축을 추가하고 맵핑시킨다. 
            YRight2 = formsPlot1.Plot.Axes.AddRightAxis();      // 폼 디자인에 올려진 그래프 컨트롤에 오른쪽축을 추가하고 맵핑시킨다. 

            formsPlot1.Plot.Axes.Left.Label.Text = "Power";   // 기존 왼쪽 축의 항목을 설정 
            formsPlot1.Plot.Axes.Left.Label.ForeColor = Colors.Red;
            formsPlot1.Plot.Axes.Right.Label.Text = "Voltage";    // 기존 오른쪽 축의 항목을 설정 
            formsPlot1.Plot.Axes.Right.Label.ForeColor = Colors.Green;

            YLeft2.Label.Text = "SE";                           // 추가된 왼쪽 축의 항목을 설정 
            YLeft2.Label.ForeColor = Colors.Blue;
            YRight2.Label.Text = "RD";                          // 추가된 오른쪽 축의 항목을 설정 
            YRight2.Label.ForeColor = Colors.Purple;

            formsPlot1.Plot.Axes.Bottom.Label.Text = "Current"; // Bottom 축의 항목을 설정 
        }

        private void button1_Click(object sender, EventArgs e)
        {
            double[] dataX = { 0, 1, 2, 3, 4, 5};
            double[] dataLeftY1 = { 0, 10, 100, 300, 500, 800};
            double[] dataLeftY2 = { 0.1, 0.24, 1, 2, 2.1, 2.1};
            double[] dataRightY1 = { 0, 2, 2.1, 2.2, 2.3, 2.4};
            double[] dataRightY2 = { 100, 10, 5, 5.1, 5.2, 5.1 };

            var sigLeft1 = formsPlot1.Plot.Add.SignalXY(dataX, dataLeftY1, Colors.Red);       // 시그널 생성
            var sigLeft2 = formsPlot1.Plot.Add.SignalXY(dataX, dataLeftY2, Colors.Blue);
            var sigRight1 = formsPlot1.Plot.Add.SignalXY(dataX, dataRightY1, Colors.Green);
            var sigRight2 = formsPlot1.Plot.Add.SignalXY(dataX, dataRightY2, Colors.Purple);

            sigLeft1.Axes.YAxis = formsPlot1.Plot.Axes.Left;            // 시그널과 축을 맵핑 
            sigLeft2.Axes.YAxis = YLeft2!;
            sigRight1.Axes.YAxis = formsPlot1.Plot.Axes.Right;
            sigRight2.Axes.YAxis = YRight2!;

            graphScale(6, 1000, 4, 5, 100);   // 그래프 스케일 조정 
        }

        private void graphScale(double X_Axis, double Y_Left1, double Y_Left2, double Y_Right1, double Y_Right2)
        {
            formsPlot1.Plot.Axes.Bottom.Min = 0;
            formsPlot1.Plot.Axes.Bottom.Max = X_Axis;

            formsPlot1.Plot.Axes.Left.Min = 0;
            formsPlot1.Plot.Axes.Left.Max = Y_Left1;

            formsPlot1.Plot.Axes.Right.Min = 0;
            formsPlot1.Plot.Axes.Right.Max = Y_Right1;

            YLeft2!.Min = 0;
            YLeft2!.Max = Y_Left2;

            YRight2!.Min = 0;
            YRight2!.Max = Y_Right2;

            formsPlot1.Refresh();       // 그래프 갱신
        }
    }

 

 

출력결과 표시

 

반응형

댓글()

ScottPlot (Growing Data - #2)

Software/C#|2023. 2. 16. 13:23
반응형

지난 포스팅에서 ScottPlot 의 Growing Data 사용 방법에 대해 기술했습니다.  그러나 기본적으로 제공하는 사용방법이 불편하기도 할 뿐더러 코드도 길고 메모리 Overflow 의 위험성도 있어서 사용 방법을 변경해서 기술해 보았습니다. 

 

* 데이타 버퍼로 배열을 사용하지 않고 List 를 사용하여 메모리 누수를 예방했습니다. 

* 지난번의 문제점으로 지적된 X 축의 증가 Step을 조정할 수 없는 단점을 제거하였습니다.

* 코드도 훨씬 간결해 졌습니다. 

 

1. 데이타저장을 위한 클래스를 하나 구현해 놓습니다. 지난번과는 다르게 데이타 갯수를 알려주는 Index 가 없어졌고 X축의 값을 저장하기 위한 버퍼가 추가 되었습니다. 

public class MeasuredData
{
    public readonly List<double> XAxis;    // X축 데이타 저장 버퍼
    public readonly List<double> YAxis;     // Y축 데이타 저장 버퍼

    public MeasuredData()
    {
        XAxis = new List<double>();
        YAxis = new List<double>();
    }

    public void Reset()
    {
        XAxis.Clear();
        YAxis.Clear();
    }
}

 

2. 타이머를 이용하여 랜덤으로 데이타를 생성하게 하였습니다. 

  * 기본 기능과 다른 점은 X축의 간격을 임의로 넣을 수 있다는 점 입니다. 

  * 코드량도 작아지고 사용도 편리해 졌습니다. 

public partial class Form1 : Form
{
    private readonly  MeasuredData measuredData = new MeasuredData();
    private Random random = new Random();
    private double XValue = 0;      // X 축의 값을 임의로 만들기 위해 임시로 만든 변수 입니다. (실제로는 측정 상황에서의 값이 되겠네요)

    public Form1()     //  지난번과 다르게 생성자에서 특별히 구현해 줄게 없습니다. 
    {
        InitializeComponent();     
    }
    //---------------------------------------------------------------------------------------------------
    private void timer1_Tick(object sender, EventArgs e)
    {
        formsPlot1.Plot.Clear();    // graph 출력을 클리어 합니다. 

        measuredData.XAxis.Add(XValue);                              // X 축 데이타 입력
        measuredData.YAxis.Add(random.NextDouble());       // Y 축 데이타 입력  

        formsPlot1.Plot.AddScatterLines(measuredData.XAxis.ToArray(), measuredData.YAxis.ToArray()); // List 를 Array 로 변경하여 그래프 출력

        formsPlot1.Render();    // 그래프 출력 화면 갱신

        XValue += 0.3;    
    }

    private void button1_Click(object sender, EventArgs e)
    {
        measuredData.Reset();      // 그래프 데이타 초기화

        timer1.Enabled = true;    // 타이머 시작 
    }
    //---------------------------------------------------------------------------------------------------
}

 

3. 출력 결과 화면 입니다. 

Growing Data 출력결과 (X축 추가)
Growing Data 출력결과 (X축 추가)

그런데 매번 그래프를 갱신하면서 그리는 것이라 속도면에서는 어떨지 잘모르겠네요.. 그런데 ScottPlot 가 그리는 속도가 꽤 빠르긴 합니다. 크게 속도를 요하는 것이 아니라면 이 방법도 괜찮아 보입니다. 

반응형

댓글()

ScottPlot (Growing Data - #1)

Software/C#|2023. 2. 16. 11:40
반응형

C# 으로 개발툴을 변경하면서 고민중에 하나가 자주 사용하는 차트 컴포넌트를 어떻게 할 것인가 였는데 ScottPlot 컴포넌트를 발견하고는 유레카를 외쳤습니다.. 무료인데다 기능도 아주 뛰어나고 사용성에 있어서도 만족사용 하게 되었습니다.. 무엇보다도 Multi Axis 을 많이 사용하는 제 입장에서는 너무 반가운 컴포넌트 였습니다. 

하지만 딱 한가지 아쉬운 부분이 시간의 경과에 따라 데이타가 늘어나는 그래프를 그릴 경우가 많이 있는데 특히 번인장비의 경우 시간에 따른 샘플의 특성 변화를 그래프로 실시간 보여주게 되는데 이게 ScottPlot 에서는 좀 간단하지가 않습니다. 

이번 장에서는 기본적으로 ScottPlot 에서 Growing Data를 그리는 방법을 코딩합니다. ScottPlot 예제에서 나오는 방법을 조금 수정해서 코딩하였습니다. 

 

 

1. 먼제 아래와 같이 측정데이타를 저장할 Class를 하나 만들어 줍니다.

public class MeasuredData
{
    public readonly double[] YAxis;    // 실제 측정데이타가 저장될 배열
    public int index { get; set; } = 0;    // 측정된 데이타의 갯수에 대한 index

    public MeasuredData()
    {
        YAxis = new double[10000];  // 배열의 갯수는 측정될 데이타의 최대 갯수보다 크게 설정 합니다. ( 이부분이 좀 문제)
    }
}

 

2. 타이머를 이용하여 주기적으로 데이타가 랜덤으로 생성되도록 코드를 작성하여 테스트 했습니다.

public partial class Form1 : Form
{
    private readonly  MeasuredData measuredData = new MeasuredData();
    private Random random = new Random();
    private readonly ScottPlot.Plottable.SignalPlot SignalPlot;  // 출력 데이타의 시그날 위치를 제어하게 됩니다. 
    //---------------------------------------------------------------------------------------------------
    public Form1()
    {
        InitializeComponent();
        SignalPlot = formsPlot1.Plot.AddSignal(measuredData.YAxis);  // 실제 화면에 표시될 시그날 정보와 생성된 시스날을 맵핑합니다. 
        SignalPlot.MaxRenderIndex = 0;                                                  // 출력될 시그날의 초기 위치를 설정(이걸 안하면 10000개의 데이타가 다 보입니다.)
        formsPlot1.Plot.SetAxisLimits(0, 10, -2, 2);     // 그래프 출력 Limit 설정  

        formsPlot1.Refresh();   // 이 부분을 안하면 ScottPlot 그래프 화면에 오류메시지가 보입니다. 
    }
   //---------------------------------------------------------------------------------------------------
    private void timer1_Tick(object sender, EventArgs e)      // 타이머 이벤트( 1초에 한번씩 데이타 생성하도록 했습니다.)
    {
        double currentRightEdge = formsPlot1.Plot.GetAxisLimits().XMax;   // 현재 그래프의 최대 X축의 값을 가져 옵니다. 
        if (measuredData.index > currentRightEdge)           // X축의 최대값이 데이타 갯수보다 작으면
        {
            formsPlot1.Plot.SetAxisLimits(xMax: currentRightEdge + 10);    // X 축의 최대값을 측정데이타 보다 10 step 많게 설정합니다. 
        }

        measuredData.YAxis[measuredData.index] = random.NextDouble();    // 랜덤으로 만들어진 값을 측정 버퍼에 넣습니다. 
        SignalPlot.MaxRenderIndex = measuredData.index++ ;                        // 그래프 화면에서 측정된 데이타 갯수 만큼만 그려 줍니다.  

        formsPlot1.Render();                                                                               // 그래프 화면 갱신 
    }
   //---------------------------------------------------------------------------------------------------
    private void button1_Click(object sender, EventArgs e)
    {
        timer1.Enabled = true;    // 타이머 시작 
    }
}

 

3. 실제 화면 에서는 아래와 같이 출력이 됩니다. 

Growing Data 출력 결과
Growing Data 출력 결과

Growing Data 출력 결과

4. 이 Growing Data 츨력은 아쉬운 부분이 좀 있습니다.

 

  (1) 이것저것 사용이 좀 번거롭다

  (2)  X 축의 증감부분을 선택할 수 없다는 것입니다. (이 부분이  가장 아쉽네요.)  무조건 1씩 증가 입니다.

  (3) 버퍼의 크기를 미리 설정해 놓아야 하는데 이게 잘못해서 실제 데이타 보다 적게 설정해 놓으면 overflow 문제가 발생할 수 있습니다.        ScottPlot 매뉴얼에도 이 부분이 명시되어 있는데 overflow 문제에 대한 오류처리를 잘 설계하거나 또는 버퍼를 충분히 설정해야 합니다.

 

 

반응형

댓글()