Skip to content

Polygon

Hapaxia edited this page Mar 13, 2024 · 6 revisions

Introduction

A class to draw a filled polygon from points describing its border.

Polygon takes points that describe its border and displays a filled area inside those points.

The filled area is made of triangles only and is automatically triangulated. The resulting triangles are also available if required.

As of v1.2, Polygon can also take points that describe holes inside the polygon area and will cut them out from the polygon area.

As of v1.3, Polygon can also display a wireframe, showing the edges of all triangulated triangles.

As of v1.4, Polygon can also process vertices in the opposite order (if activated), and many new features such as colour and texture!

Each vertex (the points that describe the borders) is a full vertex so it holds a position, a colour and a texture co-ordinate, allowing colouring and texturing.

The points that describe the borders must be provided together - the holes' borders after all of the polygon border - as a single collection of vertices (although they can be set individually or all at once). The index of the first vertex of each hole must also be provided so that Polygon knows where each hole starts. The vertices for the polygon border must be provided in anti-clockwise order. The vertices for the holes' borders must be provided in clockwise order. The vertices' order can be reversed from v1.4 (i.e. the outer border can be clockwise and the holes' borders can be anti-clockwise)

Usage

Declaration

sw::Polygon polygon;
creates a Polygon.

sw::Polygon polygon(other);
creates a copy of the other Polygon.

sw::Polygon polygon(initializer list of points);
creates a Polygon with vertex positions matching those given in the initializer list. The points are of type sf::Vector2f.
e.g. sw::Polygon polygon{ { 0.f, 0.f }, { 0.f, 100.f }, { 100.f, 100.f }, { 100.f, 0.f } }; will create a 100x100 square.

Object

polygon = other;
copies the other Polygon onto the existing Polygon, polygon.

Drawing

This class inherits from sf::Drawable so it is drawn in the same way as all SFML drawables:
window.draw(polygon);
where window is an sf::RenderWindow.

Note: actually, window could be any sf::RenderTarget.

Transformations

This class inherits from sf::Transformable so it has all the usual SFML transformations available.

Manipulation

Overall

  • update()
    must be called after all modifications have taken place but before drawing or before getting any information from processing. This performs all triangulations and updates the actual drawing output.

This is potentially an expensive operation (depending on how complex the polygon may be) so calling this multiple times unnecessarily should be avoided.

  • operator[]
    allows access to the Polygon vertices directly using the index operator.

No index validity checking is performed so care is required for proper access.

  • setColor(sf::Color)
    sets the colour of the entire polygon area.

This colour is multiplied by each individual vertex's colour.

  • setTriangulationMethod(TriangulationMethod)
    sets the triangulation method from a selection given in the enum class. Currently, there are two possible values: BasicEarClip (a more simple version that cannot process holes and treat all vertices as a part of the anti-clockwise polygon border) and EarClip (a more thorough version that can also process holes)

Note that the current default is BasicEarClip so this must be set to be able to use holes.

  • setMeshRefinementMethod(MeshRefinementMethod)
    sets the mesh refinement method from a selection given in the enum class. Currently, there is only one choice: None.

This method is reserved in advance for future updates but currently serves no purpose. However, you can be explicit with it now in case a future addition becomes the default and you want to keep the same "none" results.

  • setTriangleLimit(std::size_t)
    sets the triangle limit. This is the maximum amount of triangles that Polygon will create before halting the update.

The triangles created before this limit is reached will still be present.

  • setReverseDirection(bool)
    sets whether or not to work with vertices in a reversed direction.

Normally, vertices (the main ones around the boundary) should be provided in an anti-clockwise direction around the polygon shape. Setting reverse direction to true means that they should be provided in a clockwise direction instead. Note that holes should always be provided in an opposite direction so if reverse direction is false, holes should be clockwise and if reverse direction is true, holes should be anti-clockwise (the opposite direction to the boundary vertices).

  • setTexture(texture)
    assigns the texture ([sf::Texture]) to Polygon, which then uses it when drawing, taking into account Polygon's vertices' tex-coords (see "Vertices" section below). If texture is omitted, texture is de-activated and the Polygon is drawn as a solid.

Vertices

  • setNumberOfVertices(std::size_t)
    sets the total number of vertices. This include the polygon area and holes' areas (if present)

  • reserveVertices(std::size_t)
    reserves the total number of vertices in advance. This can help avoid unnecessary movement in memory. Akin to the reserve method of things like std::vector. You can usually ignore this for standard cases, especially if the number of vertices does not change at all or often.

  • setVertexPosition(vertexIndex, position)
    sets the position of the given vertex. vertexIndex is of type std::size_t (first vertex is 0) and position is of type sf::Vector2f.

  • setVertexColor(vertexIndex, color)
    sets the colour of the given vertex. vertexIndex is of type std::size_t (first vertex is 0) and color is of type sf::Color.

  • setVertexTexCoords(vertexIndex, texCoords)
    sets the tex-coords (texture co-ordinates) of the given vertex. vertexIndex is of type std::size_t (first vertex is 0) and texCoords is of type sf::Vector2f.

Note that the tex-coords are ignored if not texture has been assigned.

  • importVertexPositions(vertexPositions)
    sets the vertices of the polygon to match the positions given is vertexPositions, which is an std::vector of sf::Vector2fs.

Note that this sets the number of vertices to match the imported positions so may change the number of vertices.

  • reverseVertices()
    reverses the order of all of the given vertices. Can be useful when importing positions that are clockwise.

Holes

A hole start index is an index representing the starting vertex of a hole within the total given vertices. It must be in the range [0, number of vertices)

  • addHoleStartIndex(std::size_t)
    adds a hole start index. Multiple holes can be added this way - individually.

  • clearHoleStartIndices()
    removes all hole start indices. This allows a 'clean slate' to which to add hole indices.

  • setHoleStartIndices(indices)
    sets all hole start indices. Note that this also removes any previously given hole start indices. Indices is a std::vector of std::size_t

  • setNumberOfHoles(numberOfHoles)
    sets the number of holes to numberOfHoles. This may add or remove holes accordingly.

  • setHoleStartIndex(holeIndex, holeStartIndex)
    sets the start index to holeStartIndex for hole of index holeIndex. The hole should already exist; this just modifies the value.

Wireframe

The wireframe is an extra drawable. It is a collection of lines (sf::PrimitiveType::Lines) that are drawn around each triangle after triangulation.

  • setShowWireframe(bool)
    sets whether or not the wireframe should be shown.

Note that this setting not only determines whether or not it is drawn but also whether or not it is built (updated) in the first place.
This means that if this is set to "not shown", the wireframe will be cleared and not be created, meaning that if it is not wasting any time processing the wireframe if it is not required.
Also note that the default value is "false" (not to show).

  • setWireframeColor(sf::Color)
    sets the colour to use for the wireframe. This is only altered during a Polygon update.

Information

  • getColor()
    return the current overall colour of the Polygon. This is the value set by setColor and is not affected by individual vertices' colours. (sf::Color)

  • getTriangulationMethod()
    returns the current triangulation method (enum class: sw::Polygon::TriangulationMethod)

  • getMeshRefinementMethod()
    returns the current mesh refinement method (enum class: sw::Polygon::MeshRefinementMethod)

  • getTriangleLimite()
    returns the current triangle limit (std::size_t)

  • getReverseDirection()
    return whether or not the flag to indicate using vertices in a reversed direction is set (bool)

  • getNumberOfVertices()
    returns the current number of vertices (std::size_t)

  • getVertexPosition(vertexIndex)
    returns the current position of the given vertex index, which is a std::size_t (sf::Vector2f)

  • getVertexColor(vertexIndex)
    returns the current colour of the given vertex index, which is a std::size_t (sf::Color)

  • getVertexTexCoords(vertexIndex)
    returns the current tex-coords of the given vertex index, which is a std::size_t (sf::Vector2f)

  • getShowWireframe()
    returns the current state of whether or not to show the wireframe (bool)

  • getWireframeColor()
    returns the current colour to use for the update of wireframe (sf::Color)

  • getPerimeter()
    returns the length of the entire perimeter. This includes hole perimeters (float)

does not require triangulation/update

  • getArea()
    returns the area of the entire Polygon. Holes are not included (they are 'cut out') (float)

requires triangulation; the area is based on the results of the triangulation

  • isPointInside(point)
    returns if the given point (sf::Vector2f) is inside the Polygon. Holes are not included (they are 'cut out') (bool)

requires triangulation; the area is based on the results of the triangulation

  • getLocalBounds()
    returns an axis-aligned rectangle representing the local bounding box (sf::FloatRect)

  • getGlobalBounds()
    returns an axis-aligned rectangle representing the global bounding box. This takes into account any transformations (sf::FloatRect)

  • getCentroid()
    returns the centroid (sf::Vector2f)

Note that this is an average of all of the vertices around the outer boundary of the Polygon (holes vertices are excluded).

  • getCenterOfMass()
    returns the actual centre of mass (sf::Vector2f)

Note that this is affected by holes and also takes into account sizes of triangles.
requires triangulation; the area is based on the results of the triangulation

  • getNumberOfHoles()
    returns the number of holes (std::size_t)

  • getHoleStartIndex(holeIndex)
    returns the start index for the hole of index holeIndex

This returns the start index for any hole if holeIndex is valid/hole exists or the number of vertices if holeIndex is invalid or no holes exist

  • exportVertexPositions()
    returns the positions of all of Polygon's vertices (std::vector<sf::Vector2f>)

  • exportVertexPositionsOuterOnly()
    returns the positions of all of Polygon's vertices that represent the outer boundary; it does not include any hole vertices (std::vector<sf::Vector2f>)

  • exportVertexPositionsHoleOnly(holeIndex)
    returns the positions of all vertices for the hole of index holeIndex; it does not include any of the outer boundary vertices (std::vector<sf::Vector2f>)

  • exportTriangulatedPositions()
    returns the triangulation positions after the triangulations have been processed. This call does not cause the triangulation to be performed; it uses the most recent triangulation results. Returned is a vector of positions in a multiple of 3, each 3 positions representing a single triangle. (std::vector<sf::Vector2f>)

  • exportWireframePositions()
    returns the positions used by the wireframe. These are in pairs (of sf::Vector2f) that represent each line of the wireframe.

The wireframe is based on the triangulation so requires triangulation to have been performed.

Simple Example

#include <SFML/Graphics.hpp>
#include <SelbaWard/Polygon.hpp>
int main()
{
    sf::RenderWindow window(sf::VideoMode(960u, 540u), "Selba Ward Polygon simple example");
    sw::Polygon polygon;
    polygon.setScale({ 0.5f, 0.5f }); // vertices provided fit within a range of 1920x1080 and the both of the window's dimensions are half of that range.
    polygon.importVertexPositions({ { 274.f, 618.f }, { 638.f, 693.f }, { 748.f, 344.f }, { 801.f, 726.f }, { 1070.f, 780.f }, { 1275.f, 512.f },
                                    { 1670.f, 455.f }, { 1353.f, 323.f }, { 1214.f, 76.f }, { 832.f, 158.f }, { 934.f, 481.f }, { 964.f, 207.f },
                                    { 1210.f, 224.f }, { 1242.f, 461.f }, { 869.f, 563.f }, { 706.f, 154.f }, { 881.f, 37.f }, { 563.f, 116.f } });
    polygon.update();
    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
            if (event.type == sf::Event::Closed) window.close();
        window.clear();
        window.draw(polygon);
        window.display();
    }
}

The code above displays something similar to:
Simple Example

Simple Example 2

#include <SFML/Graphics.hpp>
#include <SelbaWard/Polygon.hpp>
#include <vector>
const std::vector<sf::Vector2f> polygonVertices
{ {
    // polygon
    { 274.f, 618.f }, { 638.f, 693.f }, { 748.f, 344.f }, { 801.f, 726.f }, { 1070.f, 780.f }, { 1275.f, 512.f },
    { 1670.f, 455.f }, { 1353.f, 323.f }, { 1214.f, 76.f }, { 832.f, 158.f }, { 934.f, 481.f }, { 964.f, 207.f },
    { 1210.f, 224.f }, { 1242.f, 461.f }, { 869.f, 563.f }, { 706.f, 154.f }, { 881.f, 37.f }, { 563.f, 116.f },
    // hole 1
    { 435.f, 517.f }, { 630.f, 292.f }, { 574.f, 630.f },
    // hole 2
    { 1012.f, 586.f }, { 1089.f, 641.f }, { 1021.f, 695.f },
    // hole 3
    { 1362.f, 387.f }, { 1442.f, 410.f }, { 1370.f, 472.f }, { 1310.f, 402.f },
} };
int main()
{
    sf::RenderWindow window(sf::VideoMode(960u, 540u), "Selba Ward Polygon simple example 2");
    sw::Polygon polygon;
    polygon.setTriangulationMethod(sw::Polygon::TriangulationMethod::EarClip);
    polygon.setColor(sf::Color::Red);
    polygon.setScale({ 0.5f, 0.5f }); // vertices provided fit within a range of 1920x1080 and the both of the window's dimensions are half of that range.
    polygon.importVertexPositions(polygonVertices);
    polygon.setHoleStartIndices({ 18u, 21u, 24u });
    polygon.update();
    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
            if (event.type == sf::Event::Closed) window.close();
        window.clear();
        window.draw(polygon);
        window.display();
    }
}

The code above displays something similar to:
Simple Example 2

Simple Example 3

#include <SFML/Graphics.hpp>
#include <SelbaWard/Polygon.hpp>
#include <vector>
const std::vector<sf::Vector2f> polygonVertices
{ {
	// polygon
	{ 274.f, 618.f }, { 638.f, 693.f }, { 748.f, 344.f }, { 801.f, 726.f }, { 1070.f, 780.f }, { 1275.f, 512.f },
	{ 1670.f, 455.f }, { 1353.f, 323.f }, { 1214.f, 76.f }, { 832.f, 158.f }, { 934.f, 481.f }, { 964.f, 207.f },
	{ 1210.f, 224.f }, { 1242.f, 461.f }, { 869.f, 563.f }, { 706.f, 154.f }, { 881.f, 37.f }, { 563.f, 116.f },
	// hole 1
	{ 435.f, 517.f }, { 630.f, 292.f }, { 574.f, 630.f },
	// hole 2
	{ 1012.f, 586.f }, { 1089.f, 641.f }, { 1021.f, 695.f },
	// hole 3
	{ 1362.f, 387.f }, { 1442.f, 410.f }, { 1370.f, 472.f }, { 1310.f, 402.f },
} };
int main()
{
	sf::RenderWindow window(sf::VideoMode(960u, 540u), "Selba Ward Polygon simple example 3");
	sw::Polygon polygon;
	polygon.setTriangulationMethod(sw::Polygon::TriangulationMethod::EarClip);
	polygon.setColor(sf::Color::Red);
	polygon.setScale({ 0.5f, 0.5f }); // vertices provided fit within a range of 1920x1080 and the both of the window's dimensions are half of that range.
	polygon.importVertexPositions(polygonVertices);
	polygon.setHoleStartIndices({ 18u, 21u, 24u });
	polygon.setShowWireframe(true); // shows the wireframe (default is off)
	polygon.setWireframeColor(sf::Color::Cyan); // set the wireframe colour (default is white)
	polygon.update(); // wireframe is build at this point
	while (window.isOpen())
	{
		sf::Event event;
		while (window.pollEvent(event))
			if (event.type == sf::Event::Closed) window.close();
		window.clear();
		window.draw(polygon);
		window.display();
	}
}

The code above displays something similar to:
Simple Example 3

(Polygon v1.4)