Skip to content

Commit

Permalink
Bevel shape outline corners beyond threshold
Browse files Browse the repository at this point in the history
  • Loading branch information
kimci86 committed Oct 26, 2023
1 parent fa4dfb6 commit e6d0d66
Showing 1 changed file with 51 additions and 20 deletions.
71 changes: 51 additions & 20 deletions src/SFML/Graphics/Shape.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,21 @@

#include <algorithm>

#include <cmath>

namespace
{
// Compute the normal of a segment
sf::Vector2f computeNormal(const sf::Vector2f& p1, const sf::Vector2f& p2, bool flipped)
// Compute the direction and normal unit vectors of a segment
std::pair<sf::Vector2f, sf::Vector2f> computeDirectionAndNormal(const sf::Vector2f& p1, const sf::Vector2f& p2, bool flipped)
{
sf::Vector2f normal = (p2 - p1).perpendicular();
const float length = normal.length();
sf::Vector2f direction = (p2 - p1);
const float length = direction.length();
if (length != 0.f)
normal /= length;
direction /= length;
sf::Vector2f normal = direction.perpendicular();
if (flipped)
normal = -normal;
return normal;
return {direction, normal};
}
} // namespace

Expand Down Expand Up @@ -296,7 +299,13 @@ void Shape::updateOutline()
}

const std::size_t count = m_vertices.getVertexCount() - 2;
m_outlineVertices.resize((count + 1) * 2);
m_outlineVertices.resize((count + 1) * 2); // We need at least that many vertices.
// We will add two more vertices each time we need a bevel.

// Parameters to compute bevels
const float miterLimit = 4.f;
const float minExtension = 1.f;
const float maxExtension = std::sqrt(miterLimit * miterLimit - 1.f);

// Determine if points are defined clockwise or counterclockwise. This will impact normals computation.
const bool flipNormals = [this, count]()
Expand All @@ -307,6 +316,7 @@ void Shape::updateOutline()
return twiceArea >= 0.f;
}();

std::size_t outlineIndex = 0;
for (std::size_t i = 0; i < count; ++i)
{
const std::size_t index = i + 1;
Expand All @@ -316,22 +326,43 @@ void Shape::updateOutline()
const Vector2f p1 = m_vertices[index].position;
const Vector2f p2 = m_vertices[index + 1].position;

// Compute their normal pointing towards the outside of the shape
const Vector2f n1 = computeNormal(p0, p1, flipNormals);
const Vector2f n2 = computeNormal(p1, p2, flipNormals);

// Combine them to get the extrusion direction
const float factor = 1.f + (n1.x * n2.x + n1.y * n2.y);
const Vector2f normal = (n1 + n2) / factor;

// Update the outline points
m_outlineVertices[i * 2 + 0].position = p1;
m_outlineVertices[i * 2 + 1].position = p1 + normal * m_outlineThickness;
// Compute their direction and normal pointing towards the outside of the shape
const auto [d1, n1] = computeDirectionAndNormal(p0, p1, flipNormals);
const auto [d2, n2] = computeDirectionAndNormal(p1, p2, flipNormals);

// Decide whether to add a bevel or not
const float factor = 1.f + n1.dot(n2);
const float squaredLengthRatio = miterLimit * miterLimit * factor / 2.f;
const bool needsBevel = squaredLengthRatio < 1.f; // Note this is true if factor is 0

if (needsBevel)
{
// Make room for two more vertices
m_outlineVertices.resize(m_outlineVertices.getVertexCount() + 2);

// Interpolate how much we extend outline to have a smooth transition between mitter and bevel
const float extension = minExtension + (maxExtension - minExtension) * squaredLengthRatio;

// Update the outline points
m_outlineVertices[outlineIndex++].position = p1;
m_outlineVertices[outlineIndex++].position = p1 + (n1 + extension * d1) * m_outlineThickness;
m_outlineVertices[outlineIndex++].position = p1;
m_outlineVertices[outlineIndex++].position = p1 + (n2 - extension * d2) * m_outlineThickness;
}
else
{
// Combine normals to get the extrusion direction
const Vector2f normal = (n1 + n2) / factor;

// Update the outline points
m_outlineVertices[outlineIndex++].position = p1;
m_outlineVertices[outlineIndex++].position = p1 + normal * m_outlineThickness;
}
}

// Duplicate the first point at the end, to close the outline
m_outlineVertices[count * 2 + 0].position = m_outlineVertices[0].position;
m_outlineVertices[count * 2 + 1].position = m_outlineVertices[1].position;
m_outlineVertices[outlineIndex++].position = m_outlineVertices[0].position;
m_outlineVertices[outlineIndex++].position = m_outlineVertices[1].position;

// Update outline colors
updateOutlineColors();
Expand Down

0 comments on commit e6d0d66

Please sign in to comment.