Search code examples
c++directxdirect3ddirect2d

How do I draw a portion of a circle in C++ / Direct X?


Using direct x (direct 3d or 2d) I'd like to draw a part of a circle by specifying the theta, starting point and end point. For example I want to set the source x/y co-ordinates as 0/0, theta by 20 degrees and the end x/y as 200/200 to draw a part of the circle.

A flat 2D circle or rather its portion will do. Any ideas? Thanks.

I've added the code but this has gone through many revisions but it didn't work so I've not looked at it ever since.

D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, &pD2DFactory);


RECT rc = {dx,dy,dx+dw,dy+dh};
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
   D2D1_RENDER_TARGET_TYPE_DEFAULT,
   D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,D2D1_ALPHA_MODE_IGNORE),
   0,0, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT);

pD2DFactory->CreateDCRenderTarget(&props,&pD2DDCRenderTarget);
pD2DDCRenderTarget->BindDC(hDC,&rc);
pD2DDCRenderTarget->CreateBitmap (D2D1::SizeU(sw,sh),(void*)p_bmp,pitch,
        D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,D2D1_ALPHA_MODE_IGNORE)),
        &pD2DBitmap);

pD2DDCRenderTarget->BeginDraw();    
pD2DDCRenderTarget->DrawBitmap(pD2DBitmap,
        D2D1::RectF((FLOAT)dx,(FLOAT)dy,(FLOAT)dw,(FLOAT)dh),1.0,D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
        D2D1::RectF((FLOAT)sx,(FLOAT)sy,(FLOAT)sw,(FLOAT)sh));  
pD2DDCRenderTarget->EndDraw();      

SafeRelease(&pD2DBitmap);
SafeRelease(&pD2DDCRenderTarget);       
SafeRelease(&pD2DFactory);  

Solution

  • In Direct2D, you want to use geometries to accomplish this. Below is an example of how you might do this. The function below creates a path geometry using three points: p0 is the start point, p1 is an end point, and p2 is simply p1 rotated by theta degrees around p0. The function below requires thetaDegrees to be <= 180 degrees.

    In Direct3D, you would use a triangle fan/strip/list to approximate the circular arc; the more triangles, the smoother the shape will be. Direct2D takes care of all this for you.

    void DrawArc(
        ID2D1Factory* factory, 
        ID2D1DeviceContext* context,
        ID2D1Brush* brush,
        D2D1_POINT_2F p0, 
        D2D1_POINT_2F p1, 
        float thetaDegrees
        )
    {
        // rotate p1 around p0 by theta degrees
        D2D1_POINT_2F p2 = D2D1::Matrix3x2F::Rotation(thetaDegrees, p0).TransformPoint(p1);
    
        // distance between p0 and p1 is the radius
        float dx = p1.x - p0.x;
        float dy = p1.y - p0.y;
        float radius = std::sqrt(dx * dx + dy * dy);
    
        ComPtr<ID2D1PathGeometry> pathGeometry;
        factory->CreatePathGeometry(&pathGeometry);
    
        ComPtr<ID2D1GeometrySink> geometrySink;
        pathGeometry->Open(&geometrySink);
    
        // begin figure at p0
        geometrySink->BeginFigure(p0, D2D1_FIGURE_BEGIN_FILLED);
    
        // draw a line between p0 and p1
        geometrySink->AddLine(p1);
    
        // draw an arc segment between p1 and p2
        geometrySink->AddArc(D2D1::ArcSegment(
            p2,
            D2D1::SizeF(radius, radius),
            0.0f,
            D2D1_SWEEP_DIRECTION_CLOCKWISE,
            D2D1_ARC_SIZE_SMALL
            ));
    
        // end the figure in a closed state (automatically adds a line from p2 back to p0)
        geometrySink->EndFigure(D2D1_FIGURE_END_CLOSED);
        geometrySink->Close();
    
        // fill the interior of the geometry with the given brush
        context->FillGeometry(pathGeometry.Get(), brush);
    }