GDI+绘制雷达图

欢迎拍砖

 


#include <atlimage.h>
using namespace Gdiplus;

class CRadarDrawClass
{
public:
  //绘制的单元
  typedef struct _tagRadarItem_t
  {
    WCHAR szName[64]; //名称
    float fValue; //数值
    WCHAR szDescp[128]; //描述
  }RADARITEM, *LPRADARITEM;

//GDI+
protected:
  ULONG_PTR gdiplusToken;
  Bitmap *m_pBakImage; //背景图片
  Color m_BackColor; //背景色
  Color m_LayerLineColor; //坐标层颜色
  Color m_EvLayerFColor; //偶数层填充色
  Color m_OdLayerFColor; //奇数层填充色
  Color m_NameStrColor; //字体颜色
  Color m_DataLineColor; //数据线颜色
  Color m_DataFillColor; //数据填充颜色

public:
  CRadarDrawClass()
  {
    //初始化GDI+
    GdiplusStartupInput gdiplusStartupInput;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    m_pBakImage = NULL; //背景图片

    //Color(a,r,g,b)
    //背景色
    SetColor(m_BackColor, 255, 100, 100, 100); 
    //坐标层颜色
    SetColor(m_LayerLineColor, 100, 200, 200, 233); 
    //偶数层填充色
    SetColor(m_EvLayerFColor, 120, 100, 200, 100); 
    //奇数层填充色
    SetColor(m_OdLayerFColor, 120, 100, 100, 200);
    //字体颜色
    SetColor(m_NameStrColor, 200, 255, 255, 0); 
    //数据线颜色
    SetColor(m_DataLineColor, 200, 255, 100, 200);
    //数据填充颜色
    SetColor(m_DataFillColor, 100, 200, 100, 100); 
  }

  virtual ~CRadarDrawClass()
  {
    if(m_pBakImage)
    {
      delete m_pBakImage;
      m_pBakImage = NULL;
    }

    //关闭GDI+
    GdiplusShutdown(gdiplusToken);
  }

public:
  virtual void SetColor(Color &cr, BYTE a, BYTE r, BYTE g, BYTE b)
  {
    cr.SetValue(cr.MakeARGB(a, r, g, b));
  }
  BOOL SetBackImage(LPCWSTR szFile)
  {
    if(m_pBakImage != NULL)
    {
      delete m_pBakImage;
      m_pBakImage = NULL;
    }

    m_pBakImage = Bitmap::FromFile(szFile, TRUE);
    return m_pBakImage != NULL;
  }

public:
  
  void DrawRadarGraphics(HDC hDstDC, RECT rcRect, 
    RADARITEM *pRadItem, int iMaxRadItem, float fMaxValue)
  {
    INT cx = rcRect.right - rcRect.left;
    INT cy = rcRect.bottom - rcRect.top;

    //双缓冲绘制
    HDC hdc = CreateCompatibleDC(hDstDC);
    int nSaveDC = SaveDC(hdc);
    HBITMAP hMemBmp = CreateCompatibleBitmap(hDstDC, cx, cy);
    SelectObject(hdc, hMemBmp);

    //绘制
    DrawRadarGraphicsEx(hdc, RectF(0, 0, (REAL)cx, (REAL)cy),
      pRadItem, iMaxRadItem, fMaxValue);

    //贴图
    BitBlt(hDstDC, rcRect.left, rcRect.top, cx, cy,
      hdc, 0, 0, SRCCOPY);

    //结束清理
    RestoreDC(hdc, nSaveDC);
    DeleteObject(hMemBmp);
    DeleteDC(hdc);
  }

protected:
  void DrawRadarGraphicsEx(HDC hdc, RectF rect, 
    RADARITEM *pRadItem, int iMaxRadItem, float fMaxValue)
  {
    Graphics gs(hdc);
    gs.SetSmoothingMode(SmoothingModeDefault);

    if(m_pBakImage != NULL) //背景图片
    {
      gs.DrawImage(m_pBakImage, rect);
    }
    else //填充背景色
    {
      Brush *br = new SolidBrush(m_BackColor);
      gs.FillRectangle(br, rect);
      delete br;
    }

    //字体
    LOGFONTW lfont; 
    memset( &lfont, 0, sizeof(lfont) ); 
    lfont.lfHeight = -MulDiv(14, GetDeviceCaps(hdc, LOGPIXELSY), 72); 
    lfont.lfWeight      = FW_NORMAL;
    lfont.lfCharSet     = DEFAULT_CHARSET;
    wcscpy_s( lfont.lfFaceName, (L"宋体") );
    Gdiplus::Font gfont(hdc, &lfont);
    FontFamily fontfamily;
    gfont.GetFamily(&fontfamily);

    const int iMaxCorners = max(1, iMaxRadItem); //顶点数
    const int iMaxLayers = (int)max(1, ceil(fMaxValue)); //分层数
    double f2PI = 2.0 * (4.0 * atan(1.0)); //2π
    double fSAng = f2PI / 4.0; //起始90度
    double fDAng = f2PI / iMaxCorners; //角度间隔

    //中心点
    REAL cntX = rect.X + rect.Width/2;
    REAL cntY = rect.Y + rect.Height/2;

    //最大圆半径
    double fMaxRad = min(rect.Width, rect.Height)/2.0 * 0.8;
    //层步距
    double fLayDis = fMaxRad / iMaxLayers ;

    //绘制坐标
    {
      //线画笔
      Pen gPen(m_LayerLineColor);

      double fRad = fMaxRad;
      for(int iLay=0; iLay<iMaxLayers; iLay++)        
      {
        //填充模式      
        {
          double fAng = fSAng;
          int iPrvX=0, iPrvY=0;   
          GraphicsPath *gPath = new GraphicsPath();
          for(int iCon=0; iCon<=iMaxCorners; iCon++)
          {
            int x = (int)(cntX + fRad * cos(fAng));
            int y = (int)(cntY - fRad * sin(fAng));
            if(iCon > 0)
            {
              gPath->AddLine(iPrvX, iPrvY, x, y);
            }
            iPrvX = x;
            iPrvY = y;
            fAng += fDAng;
          }
          gPath->CloseFigure();
          Color crFill((iLay%2)? m_OdLayerFColor:m_EvLayerFColor);
          Brush *br = new SolidBrush(crFill);
          gs.FillPath(br, gPath);
          delete br;
          delete gPath;
        }
        
        //画线模式
        {
          double fAng = fSAng;
          int iPrvX, iPrvY;
          for(int iCon=0; iCon<=iMaxCorners; iCon++)
          {
            int x = (int)(cntX + fRad * cos(fAng));
            int y = (int)(cntY - fRad * sin(fAng));

            RectF rc((REAL)(x-1), (REAL)(y-1), (REAL)2, (REAL)2);
            gs.DrawRectangle(&gPen, rc);
            if(iCon > 0)
            {
              gs.DrawLine(&gPen, (REAL)iPrvX, (REAL)iPrvY, (REAL)x, (REAL)y);
            }

            iPrvX = x;
            iPrvY = y;

            fAng += fDAng;
          }
        }

        fRad -= fLayDis;
      }

      //中心点
      {
        Brush *br = new SolidBrush(m_LayerLineColor);
        RectF rc((REAL)(cntX-2), (REAL)(cntY-2), (REAL)4, (REAL)4);
        gs.FillEllipse(br, rc);
        delete br;
      }
    }

    //绘制名称
    if(pRadItem != NULL)
    {
      Brush *brs = new SolidBrush(m_NameStrColor);
      double fRad = fMaxRad + 20;
      double fAng = fSAng;
      for(int iCon=0; iCon<iMaxCorners; iCon++)
      {
        //要绘制的字体
        WCHAR wString[128];
        swprintf_s(wString, L"%s\r\n(%.1f)",
          pRadItem[iCon].szName, pRadItem[iCon].fValue);

        //字体中心
        int x = (int)(cntX + fRad * cos(fAng));
        int y = (int)(cntY - fRad * sin(fAng));

        //字体区域大小
        RectF rcTxt(0,0,0,0);
        gs.MeasureString(wString, wcslen(wString), &gfont, PointF(0,0), &rcTxt);

        //字体起始位置
        PointF pt(x - rcTxt.Width/2, y - rcTxt.Height/2);

        //绘制字体
        gs.DrawString(wString, wcslen(wString), &gfont, pt, brs);

        fAng += fDAng;
      }

      delete brs;
    }

    //绘制数据
    if(pRadItem != NULL && iMaxCorners > 0)
    {
      //填充模式
      {
        GraphicsPath *gPath = new GraphicsPath();
        int iPrvX = 0, iPrvY = 0;
        double fAng = fSAng;
        for(int iCon=0; iCon<=iMaxCorners; iCon++)
        {
          double fRad = pRadItem[iCon%iMaxCorners].fValue * fMaxRad / iMaxLayers;

          //数据坐标中心
          int x = (int)(cntX + fRad * cos(fAng));
          int y = (int)(cntY - fRad * sin(fAng));

          if(iCon > 0)
          {
            gPath->AddLine(iPrvX, iPrvY, x, y);
          }

          iPrvX = x;
          iPrvY = y;

          fAng += fDAng;
        }
        gPath->CloseFigure();

        Brush *brs = new SolidBrush(m_DataFillColor);
        gs.FillPath(brs, gPath);
        delete brs;
        delete gPath;
      }

      //画线模式
      {
        Pen gPen(m_DataLineColor, 2.0f);
        int iPrvX = 0, iPrvY = 0;
        double fAng = fSAng;
        for(int iCon=0; iCon<=iMaxCorners; iCon++)
        {
          double fRad = pRadItem[iCon%iMaxCorners].fValue * fMaxRad / iMaxLayers;

          //数据坐标中心
          int x = (int)(cntX + fRad * cos(fAng));
          int y = (int)(cntY - fRad * sin(fAng));

          if(iCon > 0)
          {
            gs.DrawLine(&gPen, iPrvX, iPrvY, x, y);
          }

          iPrvX = x;
          iPrvY = y;

          fAng += fDAng;
        }
      }
    }
  }
};
//调用示例

void CSDI1View::OnDraw(CDC* pDC)
{
  CSDI1Doc* pDoc = GetDocument();
  ASSERT_VALID(pDoc);

  CRect rcRect;
  GetClientRect(&rcRect);
  HDC hdc = pDC->m_hDC;

  CRadarDrawClass::RADARITEM Item[]=
  {
    { L"Con1", 2 },
    { L"Con2", 3 },
    { L"Con3", 6 },
    { L"Con4", 0.5 },
    { L"Con5", 4 },
  };

  CRadarDrawClass m_radDraw;
  m_radDraw.SetBackImage(L"C:\\temp\\8.jpg");
  m_radDraw.DrawRadarGraphics(hdc, rcRect, Item, _countof(Item), 10);

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值