zl程序教程

您现在的位置是:首页 >  后端

当前栏目

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

方法 实现 解析 曲线
2023-06-13 09:14:54 时间
Direct2D通过ID2D1RenderTarget接口支持基本图元(直线,矩形,圆角矩形,椭圆等)的绘制,然而,此接口并未提供对曲线绘制的直接支持。因此,想要使用Direct2D绘制一段通过指定点的曲线,比如Bezier曲线,必须借助于DrawGeometry()方法间接实现。需要通过一定的算法,将指定点转换为定义Path的控制点。幸运的是,codproject上已经有人做了这项工作,给出了相应的转换算法,并给出了C#版的实现:
DrawaSmoothCurvethroughaSetof2DPointswithBezierPrimitives
C#的代码可以很容易的转换成C++版本的,下面是我转换的一个用于Direct2D的绘制Bezier曲线的C++函数:
复制代码代码如下:

 ///<summary>
 ///Referto:http://www.codeproject.com/KB/graphics/BezierSpline.aspx
 ///Solvesatridiagonalsystemforoneofcoordinates(xory)offirstBeziercontrolpoints.
 ///</summary>
 ///<paramname="rhs">Righthandsidevector.</param>
 ///<paramname="x">Solutionvector.</param>
 voidGetFirstControlPoints(
    __inconststd::vector<FLOAT>&rhs,
    __outstd::vector<FLOAT>&x)
 {
    ATLASSERT(rhs.size()==x.size());
    intn=rhs.size();
    std::vector<FLOAT>tmp(n);   //Tempworkspace.

    FLOATb=2.0f;
    x[0]=rhs[0]/b;
    for(inti=1;i<n;i++)//Decompositionandforwardsubstitution.
    {
        tmp[i]=1/b;
        b=(i<n-1?4.0f:3.5f)-tmp[i];
        x[i]=(rhs[i]-x[i-1])/b;
    }
    for(inti=1;i<n;i++)
    {
        x[n-i-1]-=tmp[n-i]*x[n-i];//Backsubstitution.
    }
 }

 ///<summary>
 ///Referto:http://www.codeproject.com/KB/graphics/BezierSpline.aspx
 ///Getopen-endedBezierSplineControlPoints.
 ///</summary>
 ///<paramname="knots">InputKnotBeziersplinepoints.</param>
 ///<paramname="firstCtrlPt">OutputFirstControlpointsarrayofknots.size()-1length.</param>
 ///<paramname="secondCtrlPt">OutputSecondControlpointsarrayofknots.size()-1length.</param>
 voidGetCurveControlPoints(
    __inconststd::vector<D2D1_POINT_2F>&knots,
    __outstd::vector<D2D1_POINT_2F>&firstCtrlPt,
    __outstd::vector<D2D1_POINT_2F>&secondCtrlPt)
 {
    ATLASSERT((firstCtrlPt.size()==secondCtrlPt.size())
        &&(knots.size()==firstCtrlPt.size()+1));

    intn=knots.size()-1;
    ATLASSERT(n>=1);

    if(n==1)
    {
        //Specialcase:Beziercurveshouldbeastraightline.
        //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;
    }

    //CalculatefirstBeziercontrolpoints
    //Righthandsidevector
    std::vector<FLOAT>rhs(n);

    //SetrighthandsideXvalues
    for(inti=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;
    //GetfirstcontrolpointsX-values
    std::vector<FLOAT>x(n);
    GetFirstControlPoints(rhs,x);

    //SetrighthandsideYvalues
    for(inti=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;
    //GetfirstcontrolpointsY-values
    std::vector<FLOAT>y(n);
    GetFirstControlPoints(rhs,y);

    //Filloutputarrays.
    for(inti=0;i<n;++i)
    {
        //Firstcontrolpoint
        firstCtrlPt[i]=D2D1::Point2F(x[i],y[i]);
        //Secondcontrolpoint
        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);
        }
    }
 }

 HRESULTCreateBezierSpline(
    __inID2D1Factory*pD2dFactory,
    __inconststd::vector<D2D1_POINT_2F>&points,
    __outID2D1PathGeometry**ppPathGeometry)
 {
    CHECK_PTR(pD2dFactory);
    CHECK_OUTPUT_PTR(ppPathGeometry);
    ATLASSERT(points.size()>1);

    intn=points.size();
    std::vector<D2D1_POINT_2F>firstCtrlPt(n-1);
    std::vector<D2D1_POINT_2F>secondCtrlPt(n-1);
    GetCurveControlPoints(points,firstCtrlPt,secondCtrlPt);

    HRESULThr=pD2dFactory->CreatePathGeometry(ppPathGeometry);
    CHECKHR(hr);
    if(FAILED(hr))
        returnhr;

    CComPtr<ID2D1GeometrySink>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(inti=1;i<n;i++)
            spSink->AddBezier(D2D1::BezierSegment(firstCtrlPt[i-1],secondCtrlPt[i-1],points[i]));
        spSink->EndFigure(D2D1_FIGURE_END_OPEN);
        spSink->Close();
    }
    returnhr;
 }

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

 #pragmaonce
 #include"stdafx.h"
 #include<Direct2DHelper.h>
 usingD2D1::Point2F;
 usingD2D1::SizeU;
 usingD2D1::ColorF;
 usingD2D1::Matrix3x2F;
 usingD2D1::BezierSegment;
 usingD2D1::RectF;

 #include<vector>
 usingstd::vector;
 #include<algorithm>
 #include<boost/math/distributions/normal.hpp>

 classCMainWindow:
    publicCWindowImpl<CMainWindow,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()

    intOnCreate(LPCREATESTRUCT/*lpCreateStruct*/)
    {
        CreateDeviceIndependentResource();
        CreateDeviceResource();
        CreateCurve();
        return0;
    }

    voidOnDestroy()
    {
        PostQuitMessage(0);
    }

    voidOnPaint(CDCHandle)
    {
        CPaintDCdc(m_hWnd);
        Render();
    }

    BOOLOnEraseBkgnd(CDCHandledc)
    {
        returnTRUE;   //wehaveerasedthebackground
    }

    voidOnSize(UINT/*nType*/,CSizesize)
    {
        if(m_spHwndRT)
        {
            m_spHwndRT->Resize(SizeU(size.cx,size.cy));
            CreateCurve();
        }
    }

 private:
    voidRender()
    {
        if(!m_spHwndRT)
            CreateDeviceResource();

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

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

        D2D1_SIZE_Fsize=m_spHwndRT->GetSize();
        FLOATwidth=size.width-50,height=size.height-50;
        D2D1_MATRIX_3X2_FreflectY=Direct2DHelper::ReflectYMatrix();
        D2D1_MATRIX_3X2_Ftranslate=Matrix3x2F::Translation(size.width/2.0f,size.height/2.0f);
        m_spHwndRT->SetTransform(reflectY*translate);

        //drawcoordinateaxis
        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);

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

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

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

    voidCreateDeviceIndependentResource()
    {
        Direct2DHelper::CreateD2D1Factory(&m_spD2dFactory);
    }

    voidCreateDeviceResource()
    {
        CRectrc;
        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));
    }

    voidDiscardDeviceResource()
    {
        m_spSolidBrush.Release();
        m_spHwndRT.Release();
    }

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

        constintptCount=100;
        D2D1_SIZE_Fsize=m_spHwndRT->GetSize();
        FLOATwidth=size.width-50.0f,height=size.height*0.4f;

 #defineSIN_CURVE   
 #ifdefSIN_CURVE   //createsincurve
        FLOATfactor=static_cast<FLOAT>(4.0f*M_PI/width);
        FLOATx=-width*0.5f,y=0,dx=width/ptCount;
        for(inti=0;i<ptCount+1;i++)
        {
            y=height*sin(factor*x);
            m_Points.push_back(Point2F(x,y));
            x+=dx;
        }
 #else               //createnormaldistributecurve
        FLOATfactor=10.0f/width;
        FLOATx=-width*0.5f,y=0,dx=width/ptCount;
        boost::math::normalnd;
        for(inti=0;i<ptCount+1;i++)
        {
            y=height*static_cast<FLOAT>(boost::math::pdf(nd,factor*x));
            m_Points.push_back(Point2F(x,y));
            x+=dx;
        }
 #endif//SIN_CURVE

        //createBezierspline
        Direct2DHelper::CreateBezierSpline(m_spD2dFactory,m_Points,&m_spPathGeometry);
        CHECK_PTR(m_spPathGeometry);
    }

 private:
    CComPtr<ID2D1Factory>m_spD2dFactory;
    CComPtr<ID2D1HwndRenderTarget>m_spHwndRT;
    CComPtr<ID2D1SolidColorBrush>m_spSolidBrush;
    CComPtr<ID2D1PathGeometry>m_spPathGeometry;

    vector<D2D1_POINT_2F>m_Points;
 };