7

WPF开发随笔收录-心电图曲线绘制 - 流浪g

 2 years ago
source link: https://www.cnblogs.com/cong2312/p/16411637.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

WPF开发随笔收录-心电图曲线绘制

项目中之前涉及到胎儿心率图曲线的绘制,最近项目中还需要添加心电曲线和血样曲线的绘制功能。今天就来分享一下心电曲线的绘制方式;

1、胎儿心率曲线的绘制是通过DrawingVisual来实现的,这里的心电曲线我也是采用差不多相同的方式来实现的,只是两者曲线的数据有所区别。心电图的数据服务器端每秒发送至客户端一个数据包,一个数据包钟心电的数据大概一百个左右,看过心电图的应该知道,心电图的效果是匀速绘制出来的,而不是一次性将一百个点绘制出来;项目中是通过将数据存到数据缓冲区,然后通过线程定时推送数据到绘图端,线程里会根据缓冲区现有数据量来动态控制数据的快慢;这里的例子我就直接通过定时推数据来直接演示如何实现;

2、新建个项目,添加一个类继承FrameworkElement,然后加上对应的数据接收和绘制功能,这里直接贴出所有代码,具体细节之前写绘制高性能曲线时写过了,不清楚的可以参考之前的;(实际上绘图部分用Canvas实现也可以,用DrawingVisual其实每次推送了一个数据,整个视图都重新绘制了,我之所以用这个是因为我要支持自动缩放功能)

public class EcgDrawingVisual : FrameworkElement
{
    private readonly List<Visual> visuals = new List<Visual>();
    private DrawingVisual Layer;

    private Pen ecg_pen = new Pen(Brushes.Orange, 1.5);

    private int?[] ecg_points = new int?[2000];

    private int currentStart = 0;

    private double y_offset = 0;

    private int ecg_max = 60;
    private int ecg_min = -25;

    public EcgDrawingVisual()
    {
        ecg_pen.Freeze();

        Layer = new DrawingVisual();
        visuals.Add(Layer);
    }

    public void SetupData(int ecg)
    {
        ecg_points[currentStart] = ecg;
        for (int i = 1; i <= 20; i++)
        {
            ecg_points[currentStart + i] = null;
        }

        currentStart++;
        if (currentStart >= RenderSize.Width / 2)
        {
            currentStart = 0;
        }

        DrawEcgLine();
        InvalidateVisual();
    }

    private void DrawEcgLine()
    {
        var scale = RenderSize.Height / (ecg_max - ecg_min);
        y_offset = ecg_min * -scale;

        DrawingContext dc = Layer.RenderOpen();
        Matrix mat = new Matrix();
        mat.ScaleAt(1, -1, 0, RenderSize.Height / 2);
        dc.PushTransform(new MatrixTransform(mat));

        for (int i = 0, left = 0; left < RenderSize.Width; i++, left += 2)
        {
            if (ecg_points[i] == null || ecg_points[i + 1] == null) continue;
            dc.DrawLine(ecg_pen, new Point(left, ecg_points[i].Value * scale + y_offset), new Point(left + 2, ecg_points[i + 1].Value * scale + y_offset));
        }

        dc.Pop();
        dc.Close();
    }

    protected override int VisualChildrenCount => visuals.Count;
    protected override Visual GetVisualChild(int index)
    {
        return visuals[index];
    }

    protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
    {
        base.OnRenderSizeChanged(sizeInfo);
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        drawingContext.DrawRectangle(Brushes.White, null, new Rect(0, 0, RenderSize.Width, RenderSize.Height));
        base.OnRender(drawingContext);
    }
}

3、主界面添加这个控件,然后后台添加对应的推送数据的线程,这里我是定时每隔十毫秒推送一个数据给到绘图端。

public partial class MainWindow : Window
{
    private List<int> points = new List<int>() { 4, 4, 3, -1, -2, -2, -2, -2, -2, -2, -2, -2, -4, -3, 25, 37, 8, -7, -5, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -1, -1, 3, 5, 8, 9, 9, 10, 9, 7, 5, 1, -1, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -1, 1, 3 };
    private bool flag = true;
    private int currentIndex = 0;

    public MainWindow()
    {
        InitializeComponent();

        new Thread(() =>
        {
            while (flag)
            {
                Thread.Sleep(10);
                this.Dispatcher.BeginInvoke(new Action(() =>
                {
                    if (currentIndex == points.Count) currentIndex = 0;
                    ecgDrawingVisual.SetupData(points[currentIndex]);
                    currentIndex++;
                }));
            }
        }).Start();
    }

    protected override void OnClosed(EventArgs e)
    {
        base.OnClosed(e);
        flag = false;
    }
}

4、最终实现效果

2252415-20220625150929609-1392120546.gif

 

2252415-20220625150948222-337280932.gif

Recommend

  • 18

    基尼系数和洛伦兹曲线,在表示数据的不平均方面特别是财富的不平均上被广泛应用。但是目前在python里面并没有找到很好的可以直接绘制洛伦兹曲线的函数,由于目前项目用到,也就在实际应用中使用到,就把如何使用numpy,pandas,matplotlib等包来计算基尼系数和...

  • 8
    • www.chenwenguan.com 4 years ago
    • Cache

    AntV Chart绘制内存和CPU曲线

    AntV Chart绘制内存和CPU曲线 2021年3月14日 | 最近更新于 下午7:04AntV G2是蚂蚁金服数据可视化团队开发的一套基于可视化编码的图形语法,以数据驱动,具有高度的易用性和扩展性,用户无需关注各种繁琐的实现细节,一条语句即可构建出各...

  • 1

    ⌈随笔⌋ 《流浪地球》的体验成功要素 本文不含剧透,非影评。今天看到《流浪地球》票房已超30亿。春节前有幸买到大年初一的票,观后的感慨与欣喜也确实是此刻疲惫一天窝在被子里敲字时...

  • 8
    • yuguo.us 3 years ago
    • Cache

    iOS开发随笔-2

    关于segue segue就是在storyboard中描述两个场景(scene)的过渡关系的一个对象。 比如在tableview和collectionview中常见的一种模式就是点击列表页中的一项之后“跳转”到详情页,这个“跳转”的逻辑和动画就是一个segue,实现方法也很简单,再ta...

  • 12

    Grand Central Dispatch 简称(GCD,这个缩写有点大不敬……)是苹果公司开发的一种多线程技术,它提供了一个比较简单的接口来让开发者操作多线程,而不用关心太多底层实现。 Blocks 首先要知晓的一个概念就是Blocks。...

  • 4
    • yuguo.us 3 years ago
    • Cache

    iOS开发随笔

    随意记录一下一些经验,对新手可能有帮助。 数组NSArray 操作数组的时候计算数组包含的对象个数是: [dataArray count] 获取索引处的对象 [dataArray objectAtIndex:2] 删除指...

  • 13

    关于绘制统计曲线算法的一些思考 浏览:1436次  出处信息 这篇文章还是关于

  • 3

    有开发过博客系统的吗,为了更好的收录设计方案是什么 ...

  • 5
    • blog.51cto.com 2 years ago
    • Cache

    VB.NET绘制直线和贝塞尔曲线

    VB.NET绘制直线和贝塞尔曲线 精选 原创 于锋VB课程 2022-09-29 16:28:31...

  • 6
    • www.fatbobman.com 2 years ago
    • Cache

    健康笔记 2.0 开发随笔(六)

    健康笔记 2.0 开发随笔(六)SwiftUI

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK