Información general sobre las geometrías

En esta introducción se describe cómo crear y usar objetos ID2D1Geometry para definir y manipular figuras 2D. Contiene las siguientes secciones:

¿Qué es una geometría direct2D?

Una geometría direct2D es un objeto ID2D1Geometry . Este objeto puede ser una geometría simple (ID2D1RectangleGeometry, ID2D1RoundedRectangleGeometry o ID2D1EllipseGeometry), una geometría de ruta (ID2D1PathGeometry) o una geometría compuesta (ID2D1GeometryGroup e ID2D1TransformedGeometry).

Las geometrías de Direct2D permiten describir figuras bidimensionales y ofrecer muchos usos, como definir regiones de prueba de posicionamiento, regiones de clip e incluso rutas de animación.

Las geometrías de Direct2D son recursos inmutables e independientes del dispositivo creados por ID2D1Factory. Por lo general, debe crear geometrías una vez y mantenerlos durante la vida útil de la aplicación, o hasta que tengan que cambiarse. Para obtener más información sobre los recursos independientes del dispositivo y dependientes del dispositivo, consulte La introducción a los recursos.

En las secciones siguientes se describen los diferentes tipos de geometrías.

Geometrías simples

Las geometrías simples incluyen objetos ID2D1RectangleGeometry, ID2D1RoundedRectangleGeometry e ID2D1EllipseGeometry , y se pueden usar para crear figuras geométricas básicas, como rectángulos, rectángulos redondeados, círculos y elipses.

Para crear una geometría simple, use uno de los métodos de geometría ID2D1Factory::Create<geometryType>. Estos métodos crean un objeto del tipo especificado. Por ejemplo, para crear un rectángulo, llame a ID2D1Factory::CreateRectangleGeometry, que devuelve un objeto ID2D1RectangleGeometry ; para crear un rectángulo redondeado, llame a ID2D1Factory::CreateRoundedRectangleGeometry, que devuelve un objeto ID2D1RoundedRectangleGeometry , etc.

En el ejemplo de código siguiente se llama al método CreateEllipseGeometry , pasando una estructura de elipse con el centro establecido en (100, 100), radio x en 100 y radio y en 50. A continuación, llama a DrawGeometry, pasando la geometría de elipse devuelta, un puntero a un ID2D1SolidColorBrush negro y un ancho de trazo de 5. En la ilustración siguiente se muestra la salida del ejemplo de código.

ilustración de una elipse

ID2D1EllipseGeometry *m_pEllipseGeometry;
if (SUCCEEDED(hr))
{
    hr = m_pD2DFactory->CreateEllipseGeometry(
        D2D1::Ellipse(D2D1::Point2F(100.f, 60.f), 100.f, 50.f),
        &m_pEllipseGeometry
        );
}
m_pRenderTarget->DrawGeometry(m_pEllipseGeometry, m_pBlackBrush, 5);

Para dibujar el contorno de cualquier geometría, use el método DrawGeometry . Para pintar su interior, utilice el método FillGeometry .

Geometrías de ruta de acceso

Las geometrías de ruta de acceso se representan mediante la interfaz ID2D1PathGeometry . Estos objetos se pueden usar para describir figuras geométricas complejas compuestas de segmentos como arcos, curvas y líneas. En la ilustración siguiente se muestra un dibujo creado mediante la geometría de la ruta de acceso.

ilustración de un río, montañas y el sol

Para obtener más información y ejemplos, vea Información general sobre geometrías de ruta de acceso.

Geometrías compuestas

Una geometría compuesta es una geometría agrupada o combinada con otro objeto geometry, o con una transformación. Las geometrías compuestas incluyen objetos ID2D1TransformedGeometry e ID2D1GeometryGroup .

Grupos de geometría

Los grupos de geometría son una manera cómoda de agrupar varias geometrías al mismo tiempo, por lo que todas las figuras de varias geometrías distintas se concatenan en una. Para crear un objeto ID2D1GeometryGroup , llame al método CreateGeometryGroup en el objeto ID2D1Factory , pasando fillMode con los valores posibles de D2D1_FILL_MODE_ALTERNATE (alternativo) y D2D1_FILL_MODE_WINDING, una matriz de objetos geometry que se van a agregar al grupo de geometría y el número de elementos de esta matriz.

En el ejemplo de código siguiente se declara primero una matriz de objetos geometry. Estos objetos son cuatro círculos concéntricos que tienen los siguientes radios: 25, 50, 75 y 100. A continuación, llame a CreateGeometryGroup en el objeto ID2D1Factory , pasando D2D1_FILL_MODE_ALTERNATE, una matriz de objetos geometry para agregar al grupo de geometría y el número de elementos de esta matriz.

ID2D1Geometry *ppGeometries[] =
{
    m_pEllipseGeometry1,
    m_pEllipseGeometry2,
    m_pEllipseGeometry3,
    m_pEllipseGeometry4
};

hr = m_pD2DFactory->CreateGeometryGroup(
    D2D1_FILL_MODE_ALTERNATE,
    ppGeometries,
    ARRAYSIZE(ppGeometries),
    &m_pGeoGroup_AlternateFill
    );

if (SUCCEEDED(hr))
{
    hr = m_pD2DFactory->CreateGeometryGroup(
        D2D1_FILL_MODE_WINDING,
        ppGeometries,
        ARRAYSIZE(ppGeometries),
        &m_pGeoGroup_WindingFill
        );
}

En la ilustración siguiente se muestran los resultados de la representación de las dos geometrías de grupo del ejemplo.

ilustración de dos conjuntos de cuatro círculos concéntricos, uno con anillos alternados rellenos y uno con todos los anillos rellenos

Geometrías transformadas

Hay varias maneras de transformar una geometría. Puede usar el método SetTransform de un destino de representación para transformar todo lo que dibuja el destino de representación, o puede asociar una transformación directamente a una geometría mediante el método CreateTransformedGeometry para crear un ID2D1TransformedGeometry.

El método que debe usar depende del efecto que desee. Cuando se usa el destino de representación para transformar y, a continuación, representar una geometría, la transformación afecta a todo lo relacionado con la geometría, incluido el ancho de cualquier trazo que haya aplicado. Por otro lado, cuando se usa un ID2D1TransformedGeometry, la transformación afecta solo a las coordenadas que describen la forma. La transformación no afectará al grosor del trazo cuando se dibuja la geometría.

Nota

A partir de Windows 8 la transformación mundial no afectará al grosor del trazo de los trazos con D2D1_STROKE_TRANSFORM_TYPE_FIXEDo D2D1_STROKE_TRANSFORM_TYPE_HAIRLINE. Debe usar estos tipos de transformación para lograr trazos independientes de transformación.

 

En el ejemplo siguiente se crea un ID2D1RectangleGeometry y, a continuación, se dibuja sin transformarlo. Genera la salida que se muestra en la ilustración siguiente.

ilustración de un rectángulo

hr = m_pD2DFactory->CreateRectangleGeometry(
    D2D1::RectF(150.f, 150.f, 200.f, 200.f),
    &m_pRectangleGeometry
    );
// Draw the untransformed rectangle geometry.
m_pRenderTarget->DrawGeometry(m_pRectangleGeometry, m_pBlackBrush, 1);

En el ejemplo siguiente se usa el destino de representación para escalar la geometría por un factor de 3 y, a continuación, se dibuja. En la ilustración siguiente se muestra el resultado de dibujar el rectángulo sin la transformación y con la transformación. Observe que el trazo es más grueso después de la transformación, aunque el grosor del trazo sea 1.

ilustración de un rectángulo más pequeño dentro de un rectángulo más grande con un trazo más grueso

// Transform the render target, then draw the rectangle geometry again.
m_pRenderTarget->SetTransform(
    D2D1::Matrix3x2F::Scale(
        D2D1::SizeF(3.f, 3.f),
        D2D1::Point2F(175.f, 175.f))
    );

m_pRenderTarget->DrawGeometry(m_pRectangleGeometry, m_pBlackBrush, 1);

En el ejemplo siguiente se usa el método CreateTransformedGeometry para escalar la geometría por un factor de 3 y, a continuación, se dibuja. Genera la salida que se muestra en la ilustración siguiente. Observe que, aunque el rectángulo es mayor, su trazo no ha aumentado.

ilustración de un rectángulo más pequeño dentro de un rectángulo mayor con el mismo grosor de trazo

 // Create a geometry that is a scaled version
 // of m_pRectangleGeometry.
 // The new geometry is scaled by a factory of 3
 // from the center of the geometry, (35, 35).

 hr = m_pD2DFactory->CreateTransformedGeometry(
     m_pRectangleGeometry,
     D2D1::Matrix3x2F::Scale(
         D2D1::SizeF(3.f, 3.f),
         D2D1::Point2F(175.f, 175.f)),
     &m_pTransformedGeometry
     );
// Replace the previous render target transform.
m_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());

// Draw the transformed geometry.
m_pRenderTarget->DrawGeometry(m_pTransformedGeometry, m_pBlackBrush, 1);

Geometrías como máscaras

Puede usar un objeto ID2D1Geometry como máscara geométrica al llamar al método PushLayer . La máscara geométrica especifica el área de la capa compuesta en el destino de representación. Para obtener más información, vea la sección Máscaras geométricas de la información general sobre capas.

Operaciones geométricas

La interfaz ID2D1Geometry proporciona varias operaciones geométricas que puede usar para manipular y medir figuras geométricas. Por ejemplo, puede usarlos para calcular y devolver sus límites, comparar para ver cómo una geometría está relacionada espacialmente con otra (útil para las pruebas de posicionamiento), calcular las áreas y longitudes, etc. En la tabla siguiente se describen las operaciones geométricas comunes.

Operación Método
Combine CombineWithGeometry
Bounds/Widened Bounds/Retrieve Bounds, Dirty Region update Widen, GetBounds, GetWidenedBounds
Pruebas de posicionamiento FillContainsPoint, StrokeContainsPoint
Carrera StrokeContainsPoint
De comparación CompareWithGeometry
Simplificación (quita arcos y curvas bezier cuadráticas) Simplificación
Teselación Teselado
Esquema (quitar intersección) Esquema
Cálculo del área o longitud de una geometría ComputeArea, ComputeLength, ComputePointAtLength

 

Nota

A partir de Windows 8, puede usar el método ComputePointAndSegmentAtLength en id2D1PathGeometry1 para calcular el área o la longitud de una geometría.

 

Combinación de geometrías

Para combinar una geometría con otra, llame al método ID2D1Geometry::CombineWithGeometry . Al combinar las geometrías, se especifica una de las cuatro formas de realizar la operación de combinación: D2D1_COMBINE_MODE_UNION (unión), D2D1_COMBINE_MODE_INTERSECT (intersección), D2D1_COMBINE_MODE_XOR (xor) y D2D1_COMBINE_MODE_EXCLUDE (excluir). En el ejemplo de código siguiente se muestran dos círculos que se combinan mediante el modo de combinación de unión, donde el primer círculo tiene el punto central de (75, 75) y el radio de 50, y el segundo círculo tiene el punto central de (125, 75) y el radio de 50.

HRESULT hr = S_OK;
ID2D1GeometrySink *pGeometrySink = NULL;

// Create the first ellipse geometry to merge.
const D2D1_ELLIPSE circle1 = D2D1::Ellipse(
    D2D1::Point2F(75.0f, 75.0f),
    50.0f,
    50.0f
    );

hr = m_pD2DFactory->CreateEllipseGeometry(
    circle1,
    &m_pCircleGeometry1
    );

if (SUCCEEDED(hr))
{
    // Create the second ellipse geometry to merge.
    const D2D1_ELLIPSE circle2 = D2D1::Ellipse(
        D2D1::Point2F(125.0f, 75.0f),
        50.0f,
        50.0f
        );

    hr = m_pD2DFactory->CreateEllipseGeometry(circle2, &m_pCircleGeometry2);
}


if (SUCCEEDED(hr))
{
    //
    // Use D2D1_COMBINE_MODE_UNION to combine the geometries.
    //
    hr = m_pD2DFactory->CreatePathGeometry(&m_pPathGeometryUnion);

    if (SUCCEEDED(hr))
    {
        hr = m_pPathGeometryUnion->Open(&pGeometrySink);

        if (SUCCEEDED(hr))
        {
            hr = m_pCircleGeometry1->CombineWithGeometry(
                m_pCircleGeometry2,
                D2D1_COMBINE_MODE_UNION,
                NULL,
                NULL,
                pGeometrySink
                );
        }

        if (SUCCEEDED(hr))
        {
            hr = pGeometrySink->Close();
        }

        SafeRelease(&pGeometrySink);
    }
}

En la ilustración siguiente se muestran dos círculos combinados con un modo de combinación de unión.

ilustración de dos círculos superpuestos combinados en una unión

Para obtener ilustraciones de todos los modos de combinación, vea la enumeración D2D1_COMBINE_MODE.

Ampliar

El método Widen genera una nueva geometría cuyo relleno es equivalente a estrocar la geometría existente y, a continuación, escribe el resultado en el objeto ID2D1SimplifiedGeometrySink especificado. En el ejemplo de código siguiente se llama a Open en el objeto ID2D1PathGeometry . Si Open se realiza correctamente, llama a Widen en el objeto geometry.

ID2D1GeometrySink *pGeometrySink = NULL;
hr = pPathGeometry->Open(&pGeometrySink);
if (SUCCEEDED(hr))
{
    hr = pGeometry->Widen(
            strokeWidth,
            pIStrokeStyle,
            pWorldTransform,
            pGeometrySink
            );

Teselado

El método Teselate crea un conjunto de triángulos de herida en el sentido de las agujas del reloj que cubren la geometría después de transformarse mediante la matriz especificada y acoplada mediante la tolerancia especificada. En el ejemplo de código siguiente se usa Teselate para crear una lista de triángulos que representan pPathGeometry. Los triángulos se almacenan en un ID2D1Mesh, pMesh y, a continuación, se transfieren a un miembro de clase, m_pStrokeMesh, para su uso posterior al representar.

ID2D1Mesh *pMesh = NULL;
hr = m_pRT->CreateMesh(&pMesh);
if (SUCCEEDED(hr))
{
    ID2D1TessellationSink *pSink = NULL;
    hr = pMesh->Open(&pSink);
    if (SUCCEEDED(hr))
    {
        hr = pPathGeometry->Tessellate(
                NULL, // world transform (already handled in Widen)
                pSink
                );
        if (SUCCEEDED(hr))
        {
            hr = pSink->Close();
            if (SUCCEEDED(hr))
            {
                SafeReplace(&m_pStrokeMesh, pMesh);
            }
        }
        pSink->Release();
    }
    pMesh->Release();
}

FillContainsPoint y StrokeContainsPoint

El método FillContainsPoint indica si el área rellenada por la geometría contiene el punto especificado. Puede usar este método para realizar pruebas de posicionamiento. En el ejemplo de código siguiente se llama a FillContainsPoint en un objeto ID2D1EllipseGeometry , pasando un punto en (0,0) y una matriz identity .

BOOL containsPoint1;
hr = m_pCircleGeometry1->FillContainsPoint(
    D2D1::Point2F(0,0),
    D2D1::Matrix3x2F::Identity(),
    &containsPoint1
    );

if (SUCCEEDED(hr))
{
    // Process containsPoint.
}

El método StrokeContainsPoint determina si el trazo de la geometría contiene el punto especificado. Puede usar este método para realizar pruebas de posicionamiento. En el ejemplo de código siguiente se usa StrokeContainsPoint.

BOOL containsPoint;

hr = m_pCircleGeometry1->StrokeContainsPoint(
    D2D1::Point2F(0,0),
    10,     // stroke width
    NULL,   // stroke style
    NULL,   // world transform
    &containsPoint
    );

if (SUCCEEDED(hr))
{
    // Process containsPoint.
}

Simplificación

El método Simplify quita arcos y curvas bezier cuadráticas de una geometría especificada. Por lo tanto, la geometría resultante solo contiene líneas y, opcionalmente, curvas Bezier cúbicas. En el ejemplo de código siguiente se usa Simplificar para transformar una geometría con curvas Bezier en una geometría que contiene solo segmentos de línea.

HRESULT D2DFlatten(
    ID2D1Geometry *pGeometry,
    float flatteningTolerance,
    ID2D1Geometry **ppGeometry
    )
{
    HRESULT hr;
    ID2D1Factory *pFactory = NULL;
    pGeometry->GetFactory(&pFactory);

    ID2D1PathGeometry *pPathGeometry = NULL;
    hr = pFactory->CreatePathGeometry(&pPathGeometry);

    if (SUCCEEDED(hr))
    {
        ID2D1GeometrySink *pSink = NULL;
        hr = pPathGeometry->Open(&pSink);

        if (SUCCEEDED(hr))
        {
            hr = pGeometry->Simplify(
                    D2D1_GEOMETRY_SIMPLIFICATION_OPTION_LINES,
                    NULL, // world transform
                    flatteningTolerance,
                    pSink
                    );

            if (SUCCEEDED(hr))
            {
                hr = pSink->Close();

                if (SUCCEEDED(hr))
                {
                    *ppGeometry = pPathGeometry;
                    (*ppGeometry)->AddRef();
                }
            }
            pSink->Release();
        }
        pPathGeometry->Release();
    }

    pFactory->Release();

    return hr;
}

ComputeLength y ComputeArea

El método ComputeLength calcula la longitud de la geometría especificada si cada segmento se desenrolló en una línea. Esto incluye el segmento de cierre implícito si la geometría está cerrada. En el ejemplo de código siguiente se usa ComputeLength para calcular la longitud de un círculo especificado (m_pCircleGeometry1).

float length;

// Compute the area of circle1
hr = m_pCircleGeometry1->ComputeLength(
    D2D1::IdentityMatrix(),
    &length
    );

if (SUCCEEDED(hr))
{
    // Process the length of the geometry.
}

El método ComputeArea calcula el área de la geometría especificada. En el ejemplo de código siguiente se usa ComputeArea para calcular el área de un círculo especificado (m_pCircleGeometry1).

float area;

// Compute the area of circle1
hr = m_pCircleGeometry1->ComputeArea(
    D2D1::IdentityMatrix(),
    &area
    );

CompareWithGeometry

El método CompareWithGeometry describe la intersección entre la geometría que llama a este método y la geometría especificada. Los valores posibles para la intersección incluyen D2D1_GEOMETRY_RELATION_DISJOINT (separado), D2D1_GEOMETRY_RELATION_IS_CONTAINED (está contenido), D2D1_GEOMETRY_RELATION_CONTAINS (contiene) y D2D1_GEOMETRY_RELATION_OVERLAP (superposición). "disjoint" significa que dos rellenos de geometría no se intersecan en absoluto. "está contenido" significa que la geometría está completamente contenida por la geometría especificada. "contains" significa que la geometría contiene completamente la geometría especificada y "superponer" significa que las dos geometrías se superponen, pero ninguna contiene completamente la otra.

En el ejemplo de código siguiente se muestra cómo comparar dos círculos que tienen el mismo radio de 50, pero que se desplazan por 50.

HRESULT hr = S_OK;
ID2D1GeometrySink *pGeometrySink = NULL;

// Create the first ellipse geometry to merge.
const D2D1_ELLIPSE circle1 = D2D1::Ellipse(
    D2D1::Point2F(75.0f, 75.0f),
    50.0f,
    50.0f
    );

hr = m_pD2DFactory->CreateEllipseGeometry(
    circle1,
    &m_pCircleGeometry1
    );

if (SUCCEEDED(hr))
{
    // Create the second ellipse geometry to merge.
    const D2D1_ELLIPSE circle2 = D2D1::Ellipse(
        D2D1::Point2F(125.0f, 75.0f),
        50.0f,
        50.0f
        );

    hr = m_pD2DFactory->CreateEllipseGeometry(circle2, &m_pCircleGeometry2);
}

D2D1_GEOMETRY_RELATION result = D2D1_GEOMETRY_RELATION_UNKNOWN;

// Compare circle1 with circle2
hr = m_pCircleGeometry1->CompareWithGeometry(
    m_pCircleGeometry2,
    D2D1::IdentityMatrix(),
    0.1f,
    &result
    );

if (SUCCEEDED(hr))
{
    static const WCHAR szGeometryRelation[] = L"Two circles overlap.";
    m_pRenderTarget->SetTransform(D2D1::IdentityMatrix());
    if (result == D2D1_GEOMETRY_RELATION_OVERLAP)
    {
        m_pRenderTarget->DrawText(
            szGeometryRelation,
            ARRAYSIZE(szGeometryRelation) - 1,
            m_pTextFormat,
            D2D1::RectF(25.0f, 160.0f, 200.0f, 300.0f),
            m_pTextBrush
            );
    }
}

Esquema

El método Outline calcula el contorno de la geometría (una versión de la geometría en la que ninguna figura cruza por sí misma ni ninguna otra figura) y escribe el resultado en un ID2D1SimplifiedGeometrySink. En el ejemplo de código siguiente se usa Outline para construir una geometría equivalente sin ninguna intersección automática. Usa la tolerancia de acoplamiento predeterminada.

HRESULT D2DOutline(
    ID2D1Geometry *pGeometry,
    ID2D1Geometry **ppGeometry
    )
{
    HRESULT hr;
    ID2D1Factory *pFactory = NULL;
    pGeometry->GetFactory(&pFactory);

    ID2D1PathGeometry *pPathGeometry = NULL;
    hr = pFactory->CreatePathGeometry(&pPathGeometry);

    if (SUCCEEDED(hr))
    {
        ID2D1GeometrySink *pSink = NULL;
        hr = pPathGeometry->Open(&pSink);

        if (SUCCEEDED(hr))
        {
            hr = pGeometry->Outline(NULL, pSink);

            if (SUCCEEDED(hr))
            {
                hr = pSink->Close();

                if (SUCCEEDED(hr))
                {
                    *ppGeometry = pPathGeometry;
                    (*ppGeometry)->AddRef();
                }
            }
            pSink->Release();
        }
        pPathGeometry->Release();
    }

    pFactory->Release();

    return hr;
}

GetBounds y GetWidenedBounds

El método GetBounds recupera los límites de la geometría. En el ejemplo de código siguiente se usa GetBounds para recuperar los límites de un círculo especificado (m_pCircleGeometry1).

D2D1_RECT_F bounds;

hr = m_pCircleGeometry1->GetBounds(
      D2D1::IdentityMatrix(),
      &bounds
     );

if (SUCCEEDED(hr))
{
    // Retrieve the bounds.
}

El método GetWidenedBounds recupera los límites de la geometría después de ampliarlos por el estilo y el ancho de trazo especificados y transformados por la matriz especificada. En el ejemplo de código siguiente se usa GetWidenedBounds para recuperar los límites de un círculo especificado (m_pCircleGeometry1) después de que el ancho del trazo especificado lo amplíe.

float dashes[] = {1.f, 1.f, 2.f, 3.f, 5.f};

m_pD2DFactory->CreateStrokeStyle(
    D2D1::StrokeStyleProperties(
        D2D1_CAP_STYLE_FLAT,
        D2D1_CAP_STYLE_FLAT,
        D2D1_CAP_STYLE_ROUND,
        D2D1_LINE_JOIN_ROUND,   // lineJoin
        10.f,   //miterLimit
        D2D1_DASH_STYLE_CUSTOM,
        0.f     //dashOffset
        ),
     dashes,
     ARRAYSIZE(dashes)-1,
     &m_pStrokeStyle
     );
D2D1_RECT_F bounds1;
hr = m_pCircleGeometry1->GetWidenedBounds(
      5.0,
      m_pStrokeStyle,
      D2D1::IdentityMatrix(),
      &bounds1
     );
if (SUCCEEDED(hr))
{
    // Retrieve the widened bounds.
}

ComputePointAtLength

El método ComputePointAtLength calcula el vector de punto y tangente a la distancia especificada a lo largo de la geometría. En el ejemplo de código siguiente se usa ComputePointAtLength.

D2D1_POINT_2F point;
D2D1_POINT_2F tangent;

hr = m_pCircleGeometry1->ComputePointAtLength(
    10, 
    NULL, 
    &point, 
    &tangent); 

Información general sobre geometrías de ruta de acceso

Referencia de Direct2D