SFML community forums

Help => Graphics => Topic started by: Xrey274 on July 09, 2018, 04:12:32 pm

Title: Circle Shape not updating dynamicly
Post by: Xrey274 on July 09, 2018, 04:12:32 pm
I'm working on a small paint like program which for now only can make circles. I am working on a feature, which show what the circle will look like before it's placed. You can move the mouse to shrink or expand the circle preview if you haven't released the right mouse button. It seems to be working, but I have 2 problems:

1. The shape is not dynamicly updating when moving the mouse

2.I need to find a way to calculate the radius of the circle depending on how many pixels the mouse has moved. Is the size measured in pixels?
main.cpp
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <SFML/System.hpp>
#include "Shapes.h"
#include <iostream>
#include <string>
#include <vector>
#include <cstdlib>

constexpr float pi = 3.14;


sf::CircleShape sizeSelection(sf::Vector2f mousePosition, int shapeSize)
{
    sf::CircleShape circle;

    circle.setFillColor(sf::Color(136, 167, 216));
    circle.setPosition(mousePosition);
    circle.setRadius(shapeSize);

    return circle;
}

int main()
{
    std::string versionString = "1.2.2";
    std::string title = "Little Painter " + versionString;


    //window related stuff
    sf::VideoMode screenSize = sf::VideoMode::getDesktopMode();
    sf::RenderWindow window(sf::VideoMode(screenSize.width / 2, screenSize.height / 2), title);
    sf::View defaultView(sf::FloatRect(0.f, 0.f, screenSize.width, screenSize.height));

    window.setView(defaultView);

    window.setFramerateLimit(60);

    sf::Event event;

    //gui elements
    sf::RectangleShape brushTypeGUI(sf::Vector2f(200, 30)), dotColorGUI(sf::Vector2f(200, 30)), canvas(sf::Vector2f(1020, 800)), clearCanvasGUI(sf::Vector2f(200, 30)), changeBrushGUI(sf::Vector2f(200, 30));

    //gui setters
    brushTypeGUI.setPosition(0, 0);
    dotColorGUI.setPosition(202, 0);
    clearCanvasGUI.setPosition(404, 0);
    canvas.setPosition(120 ,112);

    //text elements
    sf::Text brushTypeText, dotColorText, clearCanvasText;
    sf::Font font;

    //text setters
    brushTypeText.setString("Change Brush");
    brushTypeText.setCharacterSize(24);
    brushTypeText.setFillColor(sf::Color::White);
    brushTypeText.setFont(font);
    brushTypeText.setPosition(25, 0);

    dotColorText.setString("Dot Color");
    dotColorText.setCharacterSize(24);
    dotColorText.setFillColor(sf::Color::White);
    dotColorText.setFont(font);
    dotColorText.setPosition(252, 1);

    clearCanvasText.setString("Clear Canvas");
    clearCanvasText.setCharacterSize(24);
    clearCanvasText.setFillColor(sf::Color::White);
    clearCanvasText.setFont(font);
    clearCanvasText.setPosition(433, 1);

    font.loadFromFile("expressway rg.ttf");

    //time
    sf::Time sleepTime = sf::milliseconds(10);

    //ranodom stuff
    std::vector<sf::CircleShape> circles;
    std::vector<sf::RectangleShape> rectangles;

    sf::VertexArray lines(sf::LinesStrip, 4);

    int defaultSize = 15, r = 0, g = 100, b = 0;
    bool selectingSize = false;

    sf::Vector2i oldPosition = sf::Mouse::getPosition(window);

    Shapes brush;

    brush.setBrushType("circle");

    while(window.isOpen() == true)
    {

        while(window.pollEvent(event))
        {
            //gui setters
            brushTypeGUI.setFillColor(sf::Color(33, 33, 33));
            dotColorGUI.setFillColor(sf::Color(33, 33, 33));
            clearCanvasGUI.setFillColor(sf::Color(33, 33, 33));

            //mouse position
            sf::Vector2i mousePosition = sf::Mouse::getPosition(window);
            sf::Vector2f mouseCoords = window.mapPixelToCoords(mousePosition);

            //setting bounds of gui elements
            sf::FloatRect dotColorGUIBounds = dotColorGUI.getGlobalBounds();
            sf::FloatRect clearCanvasGUIBounds = clearCanvasGUI.getGlobalBounds();
            sf::FloatRect canvasBounds = canvas.getGlobalBounds();

            int r;

            if(event.type == sf::Event::Closed)
            {
                window.close();
            }

            if(event.type == sf::Event::Resized)
            {
                window.setView(defaultView);
            }

            if(event.type == sf::Event::MouseButtonPressed)
            {
                if(canvasBounds.contains(mouseCoords))
                {
                    //if reset the position of lastPosition
                    if(oldPosition == sf::Vector2i(0, 0))
                    {
                        oldPosition = mousePosition;
                    }

                    //check if mouse has moved since the begging of the size selection
                    if(oldPosition != mousePosition)
                    {
                        //try to calculate the radius of a
                        r = ((oldPosition.x - mousePosition.x) - (oldPosition.y - mousePosition.y)) / 2;
                        std::cout<<r<<" "<<mousePosition.x<<" "<<mousePosition.y<<std::endl;

                        circles.pop_back();

                        //set the oldPosition to current mousePosition
                        oldPosition = mousePosition;

                        circles.push_back(sizeSelection(mouseCoords, r));
                    }
                    //if mouse hasn't moved set the size to the defaultSize
                    else
                    {
                        circles.push_back(sizeSelection(mouseCoords, defaultSize));
                    }

                    selectingSize = true;
                }
            }

            if(event.type == sf::Event::MouseButtonReleased && selectingSize == true)
            {
                if(event.mouseButton.button == sf::Mouse::Right)
                {
                    if(canvasBounds.contains(mouseCoords))
                    {
                        //remove the size Selection circle
                        circles.pop_back();

                        //set oldPosition to 0, 0 so it can be reset
                        oldPosition = (sf::Vector2i(0, 0));

                        //go out of selectingSize
                        selectingSize = false;

                        brush.setData(mouseCoords, defaultSize, r, g, b);

                        switch(brush.getBrushType())
                        {
                            case Shapes::Circle:
                                    circles.push_back(brush.drawCircle());
                                    break;
                            case Shapes::Rectangle:
                                    rectangles.push_back(brush.drawRectangle());
                                    break;
                        }
                    }

                }

            }

            if(dotColorGUIBounds.contains(mouseCoords))
            {
                dotColorGUI.setFillColor(sf::Color(40, 50, 95));

                if(event.type == sf::Event::MouseButtonPressed)
                {
                    if(event.mouseButton.button == sf::Mouse::Left)
                    {
                        r = rand() % 255;
                        g = rand() % 255;
                        b = rand() % 255;
                    }
                }
            }

            if(clearCanvasGUIBounds.contains(mouseCoords))
            {
                clearCanvasGUI.setFillColor(sf::Color(40, 50, 95));

                if(event.type == sf::Event::MouseButtonPressed && event.key.code == sf::Mouse::Left)
                {
                    circles.clear();
                }
            }

            if(event.type == sf::Event::MouseWheelMoved)
            {
                defaultSize += (event.mouseWheel.delta + 0.8);
            }
        }
        window.clear(sf::Color(61,61,61));

        window.draw(brushTypeGUI);
        window.draw(brushTypeText);

        window.draw(dotColorGUI);
        window.draw(dotColorText);

        window.draw(clearCanvasGUI);
        window.draw(clearCanvasText);

        window.draw(canvas);
        for(const auto it : circles)
        {
            window.draw(it);
        }

        window.draw(lines);
        window.display();
    }
}

 
Title: Re: Circle Shape not updating dynamicly
Post by: NGM88 on July 12, 2018, 04:35:47 pm
2.I need to find a way to calculate the radius of the circle depending on how many pixels the mouse has moved. Is the size measured in pixels?
main.cpp


Save mouse position on mouse button press event AND on mouse button release event. Calculate distance. Update circle radius with this distance length.
Title: Re: Circle Shape not updating dynamicly
Post by: Hapax on July 14, 2018, 03:06:48 am
It looks like they want the size to update during the drag so the only advice really is the first part of NGM88's answer: to record the position of the mouse when the button is pressed.

The only calculation left it to calculate the distance between the dragging start point and the current position of the mouse.

Oh, and yes, the measurement is in pixels.
It's technically not; it's in the view's co-ordinate system. However, you have the view's size to match the window's size so a view unit matches a pixel.


Note that you're not actually checking which mouse button is pressed.
Also, you can get the position of the mouse at time of mouse event from within the mouse event itself. This can be trivial a lot of the time but important when operating system is laggy, for example.
Title: Re: Circle Shape not updating dynamicly
Post by: NGM88 on July 14, 2018, 07:28:16 am
Ah, I didn't realize that OP wanted the circle updated in real time. In that case just calculate position at every frame with the mouse moved event. Here's an example:

#include <SFML/Graphics.hpp>

int main()
{
        sf::RenderWindow WINDOW(sf::VideoMode(800, 450), "SFML works!");
        WINDOW.setFramerateLimit(60);

        bool Dragging = false;

        sf::Vector2f MousePosition;
        sf::Vector2f OldPosition;

        float Radius = 50;

        sf::CircleShape Circle{ Radius };

        while (WINDOW.isOpen())
        {
                MousePosition = static_cast<sf::Vector2f>(sf::Mouse::getPosition(WINDOW));

                // -- EVENTS

                sf::Event EVENT;

                while (WINDOW.pollEvent(EVENT))
                {
                        switch (EVENT.type)
                        {
                        case sf::Event::Closed: WINDOW.close(); break;

                        case sf::Event::MouseButtonPressed:

                                if (EVENT.mouseButton.button == sf::Mouse::Left)
                                {
                                        Dragging = true;
                                        OldPosition = MousePosition;
                                }

                        case sf::Event::MouseMoved:

                                if (Dragging)
                                {
                                        sf::Vector2f Delta = OldPosition - MousePosition;
                                        Radius = Radius + Delta.x;
                                        OldPosition = MousePosition;
                                }

                                break;

                        case sf::Event::MouseButtonReleased:

                                if (EVENT.mouseButton.button == sf::Mouse::Button::Left) { Dragging = false; }

                                break;
                        }
                }

                Circle.setRadius(Radius);

                WINDOW.clear();        
                WINDOW.draw(Circle);           
                WINDOW.display();
        }

        return 0;
}
 
Title: Re: Circle Shape not updating dynamicly
Post by: Hapax on July 14, 2018, 02:33:35 pm
That should work, mostly. It restricts movements to horizontal only, which can be fine if that's what you want.
However, it won't take into account any movement outside of the window, which can cause this to stop the circle matching the mouse position.

Also, don't forget that if you change the view, the positions will need mapping before calculating the new radius.