Compartir a través de


Información general sobre 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 de acceso (ID2D1PathGeometry) o una geometría compuesta (ID2D1GeometryGroup e ID2D1TransformedGeometry).

Las geometrías de Direct2D permiten describir figuras bidimensionales y se pueden utilizar para muchos propósitos, como definir regiones de prueba de impacto, regiones de recorte 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 distintos tipos de geometrías.

Geometrías simples

Las geometrías simples incluyen ID2D1RectangleGeometry, ID2D1RoundedRectangleGeometry y 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 ID2D1Factory::Create<geometryType>Geometry . 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 a 100 y radio y a 50. A continuación, llama a DrawGeometry, pasando la geometría de elipse devuelta, un puntero a un negro ID2D1SolidColorBrushy 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 trayectorias

Las geometrías de trayectoria 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 geometría de trayectoria.

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

Para obtener más información y ejemplos, consulte La 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 de geometría 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 objetoID2D1GeometryGroup, llame al método CreateGeometryGroup en el objeto ID2D1Factory, pasando el fillMode con valores posibles de D2D1_FILL_MODE_ALTERNATE (alternativo) y D2D1_FILL_MODE_WINDING, una matriz de objetos geometría que se van a agregar al grupo de geometría, y el número de elementos de esta matriz.

En el siguiente ejemplo de código, primero se declara una matriz de objetos de geometría. 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 de geometría que se van a agregar al grupo de geometría, y el número de elementos en 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 llenos

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 sobre 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 del mundo no afectará al grosor de trazos con D2D1_STROKE_TRANSFORM_TYPE_FIXEDo D2D1_STROKE_TRANSFORM_TYPE_HAIRLINE. Debería usar estos tipos de transformación para lograr trazos que sean independientes de la transformación.

 

En el siguiente ejemplo, se crea un ID2D1RectangleGeometry y luego 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 mayor 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 que se integra en el objetivo de renderización. Para obtener más información, consulte la sección de Máscaras Geométricas en la Información General sobre Capas.

Operaciones geométricas

La interfaz ID2D1Geometry proporciona varias operaciones geométricas que se pueden 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
Combinar CombineWithGeometry
Límites/ Límites ampliados/Recuperar límites, actualización de la región sucia Widen, GetBounds, GetWidenedBounds
Pruebas de posicionamiento RellenoContienePunto, TrazoContienePunto
Accidente cerebrovascular StrokeContainsPoint
Comparación CompareWithGeometry
Simplificación (quita arcos y curvas bezier cuadráticas) Simplificar
Teselación Teselate
Esquema (quitar intersección) Esquema
Calcular el área o la longitud de una geometría ComputeArea, ComputeLength, ComputePointAtLength

 

Nota:

A partir de Windows 8, puedes 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, especifique una de las cuatro maneras 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 ver ilustraciones de todos los modos de combinación, consulte la enumeración D2D1_COMBINE_MODE.

Widen

El método Widen genera una nueva geometría cuyo relleno es equivalente a trazar la geometría existente y luego escribe el resultado en el objeto ID2D1SimplifiedGeometrySink especificado. En el siguiente ejemplo de código, se llama a Open en el objeto ID2D1PathGeometry. Si Open tiene éxito, llama a Widen en el objeto de geometría.

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

Teselado

El método Tessellate crea un conjunto de triángulos orientados en el sentido de las agujas del reloj que cubren la geometría después de que la geometría haya sido transformada utilizando la matriz especificada y nivelada utilizando la tolerancia especificada. En el ejemplo de código siguiente se usa Tessellate 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 siguiente ejemplo de código se llama a FillContainsPoint en un objeto ID2D1EllipseGeometry, pasando un punto en (0,0) y una matriz Identityde.

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.
}

Simplifique

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 ha anulado en una línea. Esto incluye el segmento de cierre implícito si se cierra la geometría. 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 (disjunto), D2D1_GEOMETRY_RELATION_IS_CONTAINED (está contenido), D2D1_GEOMETRY_RELATION_CONTAINS (contiene) y D2D1_GEOMETRY_RELATION_OVERLAP (superposición). "Disjuntos" 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 "superposición" significa que las dos geometrías se superponen, pero no contienen 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 esquema de la geometría (una versión de la geometría en la que ninguna figura se 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 autointersección. Usa la tolerancia de aplanamiento 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 ampliarlo por el ancho de trazo especificado.

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

Visión general de las geometrías de ruta

Referencia de Direct2D