Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Circular sectors with texture  (Read 1308 times)

0 Members and 1 Guest are viewing this topic.

Nightzus

  • Newbie
  • *
  • Posts: 6
    • View Profile
Circular sectors with texture
« on: January 28, 2014, 02:52:26 pm »
Hello all.

I'm working on a basic HUD that would display the life of the player. I'm planning on using a circular life bar like this:



Obviously, as long as the player's life decreases, the circle should hide a corresponding part of itself and should become a circular sector like this:



I've been currently working without any textures and the results seem encouraging. I've derived sf::Shape and reimplementing these methods:

unsigned int ArcCircle::getPointCount() const
{
        return m_pointCount + 1;
}

sf::Vector2f ArcCircle::getPoint(unsigned int index) const
{
        static const float pi = 3.141592654f;

        if (index == 0)
                return sf::Vector2f(m_radius, m_radius);

        float angle = m_portion * (index-1) * 2 * pi / (m_pointCount - 1);
        angle -= pi / 2;
        float x = std::cos(angle) * m_radius;
        float y = std::sin(angle) * m_radius;

        return sf::Vector2f(m_radius + x, m_radius + y);
}

where
m_portion
denotes the portion in percentage of the circle to draw.

This works quite fine... until I've tried using textures. It appears that the texture is fully drawn inside the portion of the circle displayed, which gives weird results. Here with/without texture:




Does anyone have an idea how to solve this issue?

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Circular sectors with texture
« Reply #1 on: January 28, 2014, 02:56:25 pm »
You might be applying the texture wrong, but without the complete and minimal code, we can't say anything. ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Nightzus

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: Circular sectors with texture
« Reply #2 on: January 28, 2014, 03:03:32 pm »
I'm pretty sure I'm not doing anything fancy to apply the texture but... here is the complete code:

#include <SFML/Graphics/RenderWindow.hpp>
#include "arc_circle.hpp"
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Window/Event.hpp>

using namespace sf;

int main()
{
        RenderWindow window(VideoMode(72, 72), "Test");

        VideoMode desktop = VideoMode::getDesktopMode();
        Vector2i pos(desktop.width / 2, desktop.height / 2);
        pos.x -= window.getSize().x / 2;
        pos.y -= window.getSize().y / 2;

        window.setPosition(pos);

        ArcCircle life(36);
        Texture lifeText;
        lifeText.loadFromFile("life.png");
        //life.setTexture(&lifeText);

        life.setPortion(0.3);

    while (window.isOpen())
        {
                // check all the window's events that were triggered since the last iteration of the loop
                sf::Event event;
                while (window.pollEvent(event))
                {
                        // "close requested" event: we close the window
                        if (event.type == sf::Event::Closed)
                                window.close();
                }

                window.clear(Color::Black);

                window.draw(life);

                window.display();
        }

        return 0;
}

#include <SFML/Graphics/Shape.hpp>

class ArcCircle : public sf::Shape
{
        private:
                int m_radius;
                int m_pointCount;
                float m_portion;

        public:
                explicit ArcCircle(int radius = 0, int pointCount = 50) : m_radius(radius), m_pointCount(pointCount), m_portion(1.0)
                {
                        update();
                }

                void setRadius(int radius) {m_radius = radius; update();};
                int getRadius() const {return m_radius;};

                void setPortion(float portion)
                {
                        m_portion = portion > 1.0 ? 1.0 : portion;
                        m_portion = portion < 0 ? 0 : portion;
                        update();
                };
                float getPortion() const {return m_portion;};

                virtual unsigned int getPointCount() const;
                virtual sf::Vector2f getPoint(unsigned int index) const;
};

#include "arc_circle.hpp"

unsigned int ArcCircle::getPointCount() const
{
        return m_pointCount + 1;
}

sf::Vector2f ArcCircle::getPoint(unsigned int index) const
{
        static const float pi = 3.141592654f;

        if (index == 0)
                return sf::Vector2f(m_radius, m_radius);

        float angle = m_portion * (index-1) * 2 * pi / (m_pointCount - 1);
        angle -= pi / 2;
        float x = std::cos(angle) * m_radius;
        float y = std::sin(angle) * m_radius;

        return sf::Vector2f(m_radius + x, m_radius + y);
}

I think it might be that when I call update(), it internally calls updateTextureCoords() which tries to match "every point" of the texture to those of the shape. But since update is not virtual, I cannot rewrite it nor updateTextureCoords()
« Last Edit: January 28, 2014, 03:05:57 pm by Nightzus »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Circular sectors with texture
« Reply #3 on: January 28, 2014, 03:11:10 pm »
Use the setTextureRect function.
Laurent Gomila - SFML developer

Nightzus

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: Circular sectors with texture
« Reply #4 on: January 28, 2014, 04:13:19 pm »
Indeed. It seemed weird at first glance but it is indeed a rather good solution.

I'm getting some little artefacts but I'll see if I can get rid of them later.

Thank you very much.


For those who are interested, here is the modified code:

EDIT: this solution only works if the texture has the same size than the circle.
EDIT2: I've updated the following code. It should work with any texture smaller or larger than the circle and produces no visual glitches.

void ArcCircle::setPortion(float portion)
{
        m_portion = portion > 1.0 ? 1.0 : portion;
        m_portion = portion < 0 ? 0 : portion;

        if (getTexture())
        {
                Vector2i offset(0,0);
                Vector2u size = getTexture()->getSize();
                Vector2u halfSize(size.x / 2, size.y / 2);
                Vector2f factor((float) halfSize.x / m_radius, (float) halfSize.y / m_radius);

                switch ((int) (m_portion * 100) / 25)
                {
                        case 0:
                                offset.x = round(getPoint(getPointCount() - 1).x * factor.x);
                                setTextureRect(IntRect(halfSize.x, 0, offset.x - halfSize.x, halfSize.y));
                                break;
                        case 1:
                                offset.y = round(getPoint(getPointCount() - 1).y * factor.y);
                                setTextureRect(IntRect(halfSize.x, 0, halfSize.x, offset.y));
                                break;
                        case 2:
                                offset.x = round(getPoint(getPointCount() - 1).x * factor.x);
                                setTextureRect(IntRect(offset.x, 0, size.x - offset.x, size.y));
                                break;
                        case 3:
                                setTextureRect(IntRect(0, 0, size.x, size.y));
                                break;
                        default:
                                setTextureRect(IntRect(0, 0, size.x, size.y));
                                break;
                }
        }

        update();
}
 
« Last Edit: January 29, 2014, 12:09:27 am by Nightzus »