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.
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.
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.
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.
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.
// 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.
// 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.
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);
Temas relacionados