Direct2D has a bag full of functions for manipulation of geometries. You can intersect polygons or unite them, test for the relation between two geometries and much more.
Sometimes you may need to get the resulting coordinates from such an operation. Or you want to use D2D as a geometric object manipulation library. This is not as easy as it should be.
From the D2D related blogs I got the hint: ‚You have to implement the sink interface and grab the data from there.‘. As there is not much documentation yet and only a handful of samples I didn’t find code so I tried to implement it myself.
But some background first.
The base interface for geometries in Direct2D is ID2D1Geometry. It has methods to operate on geometries like CombineWithGeometry, ComputeArea or Widen. These methods write the result of the operation to an ID2D1SimplifiedGeometrySink you have to provide. The geometry operation calls the sink’s methods BeginFigure, AddLines, EndFigure, Close and so on.
Normally the sink is used to create contents for a new ID2D1PathGeometry, a geometry derivate that can consist of figures and segments, just like the path objects in GDI, GDI+ or WPF. The path geometry’s Open method creates a new sink and returns it’s interface pointer so the sink can be used to fill the path. Now you can call the sink’s methods or pass the sink through to the geometry operations as mentioned above.
If you imitate an ID2D1SimplifiedGeometrySink you can give its pointer to the geometry operations and AddLines for example may be called by the geometry. As AddLines is your code, you can do what you like with the coordinates. So, let’s implement a sink. I needed a ID2D1TessellationSink because I wanted to grab the triangles created by the ID2D1Geometry Tesselate method. First comes the definition of the class derived from the ID2D1TessellationSink interface in MySink.h:
#pragma once
class MySink : public ID2D1TessellationSink
{
public:
MySink(ID2D1TessellationSink *pSink);
~MySink(void);
STDMETHOD_(void, AddTriangles)(
__in_ecount(trianglesCount) CONST D2D1_TRIANGLE *triangles, UINT trianglesCount
);
STDMETHOD(Close)();
public:
unsigned long STDMETHODCALLTYPE AddRef();
unsigned long STDMETHODCALLTYPE Release();
HRESULT STDMETHODCALLTYPE QueryInterface(IID const& riid, void** ppvObject);
private:
LONG volatile m_cRefCount;
ID2D1TessellationSinkPtr m_spSink;
};
And now the implementation in MySink.cpp:
#include "StdAfx.h"
#include "MySink.h"
MySink::MySink(ID2D1TessellationSink *pSink) : m_cRefCount(0)
{
m_spSink = pSink;
m_spSink->AddRef();
}
MySink::~MySink(void)
{
m_spSink->Release();
}
STDMETHODIMP_(void) MySink::AddTriangles(__in_ecount(trianglesCount) CONST D2D1_TRIANGLE *triangles, UINT trianglesCount)
{
// here you can copy the triangles data...
m_spSink->AddTriangles(triangles, trianglesCount);
}
STDMETHODIMP MySink::Close()
{
return m_spSink->Close();
}
STDMETHODIMP_(unsigned long) MySink::AddRef()
{
return InterlockedIncrement(&m_cRefCount);
}
STDMETHODIMP_(unsigned long) MySink::Release()
{
if (InterlockedDecrement(&m_cRefCount) == 0)
{
delete this;
return 0;
}
return m_cRefCount;
}
STDMETHODIMP MySink::QueryInterface(IID const& riid, void** ppvObject)
{
if (__uuidof(ID2D1TessellationSink) == riid)
*ppvObject = dynamic_cast(this);
else if (__uuidof(IUnknown) == riid)
*ppvObject = dynamic_cast(this);
else
{
*ppvObject = NULL;
return E_FAIL;
}
return S_OK;
}
My class takes an ID2D1TesselationSink pointer in the ctor that is used to pipe through the data to create a mesh (see the ID2D1Mesh interface). You can port the idea to a simplified or a normal geometry sink.