一、前言
專案中之前涉及到胎兒心率圖曲線的繪制,最近專案中還需要添加心電曲線和血樣曲線的繪制功能,今天就來分享一下心電曲線的繪制方式;
二、正文
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、最終實作效果


轉載請註明出處,本文鏈接:https://www.uj5u.com/net/495600.html
標籤:.NET技术
