winfrom 生命体征波形 心律、血氧、呼吸等曲线图 附源码

 利用PictureBox画动态曲线监护仪软件示例 功能说明:
            1. 利用PictureBox控件,双缓冲,绘图无闪屏;
            2. 动态绘制Sin(x)示例曲线,X范围固定为 0-4Pi;
            3. 曲线的刷新可通过按钮停止或继续;
            4. 实时绘制鼠标竖直标线,可通过右键固定或解除固定;
            5. 实时计算鼠标标线与曲线交点的数值坐标;
            6. 窗体大小可实时调整,曲线绘制不受影响;
            7. 可用于绘制心律、血氧、呼吸曲线等生命体征波形的绘制;


                                --2024.07.03 By 云草桑

C# winfrom GDI 把曲线波形升级为曲线面积-CSDN博客

图形处理

  /// <summary>
  /// 生成曲线点(像素表示)
  /// </summary>
  /// <param name="picBox">控件</param>
  /// <param name="datArray">数据数组</param>
  /// <returns>曲线点数组</returns>
  private PointF[] GenerateCurvePoints(PictureBox picBox, double[] datArray)
  {
      var curvePoints = new PointF[datArray.Length];

      Rectangle pnlRect = picBox.ClientRectangle;
      PointF tmpPoint;
      float x, y;
      float dltX = pnlRect.Width * 1f / (datArray.Length - 1);
      for (int i = 0; i < datArray.Length; i++)
      {
          tmpPoint = new PointF(i * dltX, (float)(pnlRect.Height * (1 - datArray[i] / 2) / 2));
          curvePoints[i] = tmpPoint;
      }

      return curvePoints;
  }

  /// <summary>
  /// 初始化曲线数据数组
  /// </summary>
  /// <param name="datArray">曲线数据数组</param>
  private void InitializeDatArray(double[] datArray)
  {
      if (datArray == null)
          return;
      //初始化为两个周期的正弦值(0-4Pi)
      int datLen = datArray.Length;
      double dltX = 4 * Math.PI / (datLen - 1);
      for (int i = 0; i < datLen; i++)
      {
          datArray[i] = Math.Sin(i * dltX);
      }
  }

  /// <summary>
  /// 数组左移一个元素,留出最后一个元素填充新数据
  /// </summary>
  /// <param name="datArray">曲线数据数组</param>
  private void LeftShiftOneElement(double[] datArray)
  {
      if (datArray != null && datArray.Length > 1)
      {
          //两条BlockCopy语句,实现数组左移一个元素
          //int datLen = datArray.Length;
          //var tmpArray = new double[datLen - 1];
          //Buffer.BlockCopy(datArray, sizeof(double) * 1, tmpArray, 0, Buffer.ByteLength(tmpArray));
          //Buffer.BlockCopy(tmpArray, 0, datArray, 0, Buffer.ByteLength(tmpArray));

          //BlockCopy itself, only one sentence.(其实只一条BlockCopy语句即可)
          Buffer.BlockCopy(src: datArray, srcOffset: sizeof(double) * 1,
                           dst: datArray, dstOffset: 0,
                           count: Buffer.ByteLength(datArray) - sizeof(double) * 1);
      }
  }

  /// <summary>
  /// 绘制位图,并赋值pictureBox.Image属性
  /// </summary>
  /// <param name="picBox">PictureBox</param>
  private void DrawBitmap(PictureBox picBox)
  {
      Action<PictureBox> thisDel = DrawBitmap;
      if (InvokeRequired)                         //跨线处理
      {
          Invoke(thisDel, picBox);
      }
      else
      {
          #region --- 绘图操作 ---
          var rect = picBox.ClientRectangle;
          Point pt1, pt2;
          var bmp = new Bitmap(rect.Width, rect.Height);
          var gBmp = Graphics.FromImage(bmp);

          //画黑背景
          gBmp.FillRectangle(Brushes.Black, rect);

          //画网格
          for (int i = 0; i < 3; i++)             //三条横线及坐标
          {
              pt1 = new Point(0, (i + 1) * rect.Height / 4);
              pt2 = new Point(rect.Width, (i + 1) * rect.Height / 4);
              gBmp.DrawLine(Pens.LightGreen, pt1, pt2);
              var yScal = 1 - i;
              gBmp.DrawString(yScal.ToString("F1"), Font, Brushes.LightGreen, pt1 + new Size(5, -15));

          }
          for (int i = 0; i < 3; i++)             //三条纵线,及坐标
          {
              pt1 = new Point((i + 1) * rect.Width / 4, 0);
              pt2 = new Point((i + 1) * rect.Width / 4, rect.Height);
              gBmp.DrawLine(Pens.LightGreen, pt1, pt2);
              var xScal = i + 1;
              gBmp.DrawString(xScal.ToString("F1") + "PI", Font, Brushes.LightGreen, pt2 - new Size(-5, 15));
          }

          //画曲线
          if (curvePoints != null)
              gBmp.DrawCurve(Pens.Yellow, curvePoints);

          //画鼠标竖线
          int msLocX;                     //鼠标的横坐标
          if (fixedMarkLine)
              msLocX = (int)(rect.Width * markLineRelPos);
          else
              msLocX = (int)(msRelLoc.X * rect.Width);
          pt1 = new Point(msLocX, 0);
          pt2 = new Point(msLocX, rect.Height);
          gBmp.DrawLine(Pens.Red, pt1, pt2);

          //刷新PictureBox的Image属性
          picBox.Image = bmp;

          //显示坐标
          int xIdx = (int)((sinDat.Length - 1) * msLocX * 1d / rect.Width);
          if (xIdx >= 0 && xIdx < sinDat.Length)
          {
              double xVal = 4 * Math.PI * xIdx / (sinDat.Length - 1);
              double yVal = sinDat[xIdx];

              lblCoord.Text = "交点坐标:X = " + xVal.ToString("F3") + " , Y = " + yVal.ToString("F3");
          }
          #endregion
      }
  }

C# winfrom GDI 把曲线波形升级为曲线面积-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云草桑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值