解析在Direct2D中画Bezier曲线的实现方法

yuxiyuxiba

yuxiyuxiba

2016-02-19 09:02

今天图老师小编要跟大家分享解析在Direct2D中画Bezier曲线的实现方法,精心挑选的过程简单易学,喜欢的朋友一起来学习吧!
Direct2D通过ID2D1RenderTarget接口支持基本图元(直线,矩形,圆角矩形,椭圆等)的绘制,然而,此接口并未提供对曲线绘制的直接支持。因此,想要使用Direct2D绘制一段通过指定点的曲线,比如Bezier曲线,必须借助于DrawGeometry()方法间接实现。需要通过一定的算法,将指定点转换为定义Path的控制点。幸运的是,codproject上已经有人做了这项工作,给出了相应的转换算法,并给出了C#版的实现:
Draw a Smooth Curve through a Set of 2D Points with Bezier Primitives
C#的代码可以很容易的转换成C++版本的,下面是我转换的一个用于Direct2D的绘制Bezier曲线的C++函数:
代码如下:

 /// summary
 /// Refer to : http://www.codeproject.com/KB/graphics/BezierSpline.aspx
 /// Solves a tridiagonal system for one of coordinates (x or y) of first Bezier control points.
 /// /summary
 /// param name="rhs"Right hand side vector./param
 /// param name="x"Solution vector./param
 void GetFirstControlPoints(
     __in const std::vectorFLOAT& rhs,
     __out std::vectorFLOAT& x )
 {
     ATLASSERT(rhs.size()==x.size());
     int n = rhs.size();
     std::vectorFLOAT tmp(n);    // Temp workspace.

     FLOAT b = 2.0f;
     x[0] = rhs[0] / b;
     for (int i = 1; i n; i++) // Decomposition and forward substitution.
     {
         tmp[i] = 1 / b;
         b = (i n-1 ? 4.0f : 3.5f) - tmp[i];
         x[i] = (rhs[i] - x[i-1]) / b;
     }
     for (int i = 1; i n; i++)
     {
         x[n-i-1] -= tmp[n-i] * x[n-i]; // Back substitution.
     }
 }

 /// summary
 /// Refer to : http://www.codeproject.com/KB/graphics/BezierSpline.aspx
 /// Get open-ended Bezier Spline Control Points.
 /// /summary
 /// param name="knots"Input Knot Bezier spline points./param
 /// param name="firstCtrlPt"Output First Control points array of knots.size()-1 length./param
 /// param name="secondCtrlPt"Output Second Control points array of knots.size()-1 length./param
 void GetCurveControlPoints(
     __in const std::vectorD2D1_POINT_2F& knots,
     __out std::vectorD2D1_POINT_2F& firstCtrlPt,
     __out std::vectorD2D1_POINT_2F& secondCtrlPt )
 {
     ATLASSERT( (firstCtrlPt.size()==secondCtrlPt.size())
         && (knots.size()==firstCtrlPt.size()+1) );

     int n = knots.size()-1;
     ATLASSERT(n=1);

     if (n == 1)
     {
         // Special case: Bezier curve should be a straight line.
         // 3P1 = 2P0 + P3
         firstCtrlPt[0].x = (2 * knots[0].x + knots[1].x) / 3.0f;
         firstCtrlPt[0].y = (2 * knots[0].y + knots[1].y) / 3.0f;

         // P2 = 2P1 – P0
         secondCtrlPt[0].x = 2 * firstCtrlPt[0].x - knots[0].x;
         secondCtrlPt[0].y = 2 * firstCtrlPt[0].y - knots[0].y;
         return;
     }

     // Calculate first Bezier control points
     // Right hand side vector
     std::vectorFLOAT rhs(n);

     // Set right hand side X values
     for (int i = 1; i (n-1); ++i)
     {
         rhs[i] = 4 * knots[i].x + 2 * knots[i+1].x;
     }
     rhs[0] = knots[0].x + 2 * knots[1].x;
     rhs[n-1] = (8 * knots[n-1].x + knots[n].x) / 2.0f;
     // Get first control points X-values
     std::vectorFLOAT x(n);
     GetFirstControlPoints(rhs,x);

     // Set right hand side Y values
     for (int i = 1; i (n-1); ++i)
     {
         rhs[i] = 4 * knots[i].y + 2 * knots[i+1].y;
     }
     rhs[0] = knots[0].y + 2 * knots[1].y;
     rhs[n-1] = (8 * knots[n-1].y + knots[n].y) / 2.0f;
     // Get first control points Y-values
     std::vectorFLOAT y(n);
     GetFirstControlPoints(rhs,y);

     // Fill output arrays.
     for (int i = 0; i n; ++i)
     {
         // First control point
         firstCtrlPt[i] = D2D1::Point2F(x[i],y[i]);
         // Second control point
         if (i (n-1))
         {
             secondCtrlPt[i] = D2D1::Point2F(2 * knots[i+1].x - x[i+1], 2*knots[i+1].y-y[i+1]);
         }
         else
         {
             secondCtrlPt[i] = D2D1::Point2F((knots[n].x + x[n-1])/2, (knots[n].y+y[n-1])/2);
         }
     }
 }

 HRESULT CreateBezierSpline(
     __in ID2D1Factory* pD2dFactory,
     __in const std::vectorD2D1_POINT_2F& points,
     __out ID2D1PathGeometry** ppPathGeometry )
 {
     CHECK_PTR(pD2dFactory);
     CHECK_OUTPUT_PTR(ppPathGeometry);
     ATLASSERT(points.size()1);

     int n = points.size();
     std::vectorD2D1_POINT_2F firstCtrlPt(n-1);
     std::vectorD2D1_POINT_2F secondCtrlPt(n-1);
     GetCurveControlPoints(points,firstCtrlPt,secondCtrlPt);

     HRESULT hr = pD2dFactory-CreatePathGeometry(ppPathGeometry);
     CHECKHR(hr);
     if (FAILED(hr))
         return hr;

     CComPtrID2D1GeometrySink spSink;
     hr = (*ppPathGeometry)-Open(&spSink);
     CHECKHR(hr);
     if (SUCCEEDED(hr))
     {
         spSink-SetFillMode(D2D1_FILL_MODE_WINDING);
         spSink-BeginFigure(points[0],D2D1_FIGURE_BEGIN_FILLED);
         for (int i=1;in;i++)
             spSink-AddBezier(D2D1::BezierSegment(firstCtrlPt[i-1],secondCtrlPt[i-1],points[i]));
         spSink-EndFigure(D2D1_FIGURE_END_OPEN);
         spSink-Close();
     }
     return hr;
 }

下面是一个使用此函数绘制正弦函数的Sample,曲线的红点是曲线的控制点:
代码如下:

 #pragma once
 #include "stdafx.h"
 #include Direct2DHelper.h
 using D2D1::Point2F;
 using D2D1::SizeU;
 using D2D1::ColorF;
 using D2D1::Matrix3x2F;
 using D2D1::BezierSegment;
 using D2D1::RectF;

 #include vector
 using std::vector;
 #include algorithm
 #include boost/math/distributions/normal.hpp

 class CMainWindow :
     public CWindowImplCMainWindow,CWindow,CSimpleWinTraits
 {
 public:
     BEGIN_MSG_MAP(CMainWindow)
         MSG_WM_PAINT(OnPaint)
         MSG_WM_ERASEBKGND(OnEraseBkgnd)
         MSG_WM_SIZE(OnSize)
         MSG_WM_CREATE(OnCreate)
         MSG_WM_DESTROY(OnDestroy)
     END_MSG_MAP()

     int OnCreate(LPCREATESTRUCT /*lpCreateStruct*/)
     {
         CreateDeviceIndependentResource();
         CreateDeviceResource();
         CreateCurve();
         return 0;
     }

     void OnDestroy()
     {
         PostQuitMessage(0);
     }

     void OnPaint(CDCHandle)
     {
         CPaintDC dc(m_hWnd);
         Render();
     }

     BOOL OnEraseBkgnd(CDCHandle dc)
     {
         return TRUE;    // we have erased the background
     }

     void OnSize(UINT /*nType*/, CSize size)
     {
         if (m_spHwndRT)
         {
             m_spHwndRT-Resize(SizeU(size.cx,size.cy));
             CreateCurve();
         }
     }

 private:
     void Render()
     {
         if (!m_spHwndRT)
             CreateDeviceResource();

         m_spHwndRT-BeginDraw();
         m_spHwndRT-Clear(ColorF(ColorF::CornflowerBlue));

         m_spHwndRT-SetTransform(Matrix3x2F::Identity());

         D2D1_SIZE_F size = m_spHwndRT-GetSize();
         FLOAT width = size.width-50, height = size.height-50;
         D2D1_MATRIX_3X2_F reflectY = Direct2DHelper::ReflectYMatrix();
         D2D1_MATRIX_3X2_F translate = Matrix3x2F::Translation(size.width/2.0f,size.height/2.0f);
         m_spHwndRT-SetTransform(reflectY*translate);

         // draw coordinate axis
         m_spSolidBrush-SetColor(ColorF(ColorF::Red));
         m_spHwndRT-DrawLine(Point2F(-width*0.5f,0),Point2F(width*0.5f,0),m_spSolidBrush,2.0f);
         m_spSolidBrush-SetColor(ColorF(ColorF::DarkGreen));
         m_spHwndRT-DrawLine(Point2F(0,-height*0.5f),Point2F(0,height*0.5f),m_spSolidBrush,2.0f);

         // draw curve
         m_spSolidBrush-SetColor(ColorF(ColorF::Blue));
         m_spHwndRT-DrawGeometry(m_spPathGeometry,m_spSolidBrush,1.0f);

         // draw point marks
         m_spSolidBrush-SetColor(ColorF(ColorF::Red));
         for (auto p=m_Points.cbegin();p!=m_Points.cend();p++)
         {
             Direct2DHelper::DrawRectPoint(m_spHwndRT,m_spSolidBrush,(*p),5.0f);
         }

         HRESULT hr = m_spHwndRT-EndDraw();
         if (hr == D2DERR_RECREATE_TARGET)
             DiscardDeviceResource();
     }

     void CreateDeviceIndependentResource()
     {
         Direct2DHelper::CreateD2D1Factory(&m_spD2dFactory);
     }

     void CreateDeviceResource()
     {
         CRect rc;
         GetClientRect(&rc);

         CHECK_PTR(m_spD2dFactory);
         IFR(m_spD2dFactory-CreateHwndRenderTarget(
             D2D1::RenderTargetProperties(),
             D2D1::HwndRenderTargetProperties(m_hWnd,SizeU(rc.Width(),rc.Height())),
             &m_spHwndRT));
         IFR(m_spHwndRT-CreateSolidColorBrush(ColorF(ColorF::Red),&m_spSolidBrush));
     }

     void DiscardDeviceResource()
     {
         m_spSolidBrush.Release();
         m_spHwndRT.Release();
     }

     void CreateCurve()
     {
         if (!m_spHwndRT)
             return;
         if (m_spPathGeometry)
         {
             m_spPathGeometry.Release();
             m_Points.clear();
         }

         const int ptCount = 100;
         D2D1_SIZE_F size = m_spHwndRT-GetSize();
         FLOAT width = size.width-50.0f, height = size.height*0.4f;

 #define SIN_CURVE   
 #ifdef SIN_CURVE    // create sin curve
         FLOAT factor = static_castFLOAT(4.0f*M_PI/width);
         FLOAT x = -width*0.5f, y = 0, dx = width/ptCount;
         for (int i=0;iptCount+1;i++)
         {
             y = height*sin(factor*x);
             m_Points.push_back(Point2F(x,y));
             x += dx;
         }
 #else                // create normal distribute curve
         FLOAT factor = 10.0f/width;
         FLOAT x = -width*0.5f, y = 0, dx = width/ptCount;
         boost::math::normal nd;
         for (int i=0;iptCount+1;i++)
         {
             y = height*static_castFLOAT(boost::math::pdf(nd,factor*x));
             m_Points.push_back(Point2F(x,y));
             x += dx;
         }
 #endif // SIN_CURVE

         // create Bezier spline
         Direct2DHelper::CreateBezierSpline(m_spD2dFactory,m_Points,&m_spPathGeometry);
         CHECK_PTR(m_spPathGeometry);
     }

 private:
     CComPtrID2D1Factory m_spD2dFactory;
     CComPtrID2D1HwndRenderTarget m_spHwndRT;
     CComPtrID2D1SolidColorBrush m_spSolidBrush;
     CComPtrID2D1PathGeometry m_spPathGeometry;

     vectorD2D1_POINT_2F m_Points;
 };


展开更多 50%)
分享

猜你喜欢

解析在Direct2D中画Bezier曲线的实现方法

编程语言 网络编程
解析在Direct2D中画Bezier曲线的实现方法

Window画图程序中画曲线的简单实现

编程语言 网络编程
Window画图程序中画曲线的简单实现

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

在Struts 2中实现文件上传

编程语言 网络编程
在Struts 2中实现文件上传

在VC中实现画任意方向椭圆

编程语言 网络编程
在VC中实现画任意方向椭圆

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

在ORACLE中实现SELECTTOPN的方法

电脑网络
在ORACLE中实现SELECTTOPN的方法

在iOS开发的Quartz2D使用中实现图片剪切和截屏功能

编程语言 网络编程
在iOS开发的Quartz2D使用中实现图片剪切和截屏功能

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

Android 避免APP启动闪黑屏的解决办法(Theme和Style)

Android 避免APP启动闪黑屏的解决办法(Theme和Style)

IOS简单实现瀑布流UICollectionView

IOS简单实现瀑布流UICollectionView
下拉加载更多内容 ↓