Question
Edit the C++ program so that it meets the following requirements: Any time a pixel is modified, it must be as a command. Each command
Edit the C++ program so that it meets the following requirements:
- Any time a pixel is modified, it must be as a command.
- Each command must be processed through some intermediate data structure (e.g. a queue, stack, list, vector, tree, etc
- A history of the commands executed should be kept for at least the last 10 commands, such that a user can 'undo' with by pressing the 'z' key and redo by pressing the 'y' key up to 10 times.
- Executing the same command twice in a row should not be recorded. Think of this as a performance optimization where you are not changing the same pixel to the same color twice in a row.
- Modifying a pixel should only be done when pressing the 'left mouse key'
- compiles with: g++ -std=c++17 *.cpp -o App -lsfml-graphics -lsfml-window -lsfml-system
App.cpp
#include #include #include #include // Include standard library C++ libraries. #include // Project header files #include "App.hpp" // TODO: Decide, modify, add or remove any data structures you need. std::queue App::m_commands; std::stack App::m_undo; void (*App::m_initFunc)(void) = nullptr; void (*App::m_updateFunc)(void) = nullptr; void (*App::m_drawFunc)(void) = nullptr; unsigned int App::pmouseX=0; unsigned int App::pmouseY=0; unsigned int App::mouseX=0; unsigned int App::mouseY=0; sf::RenderWindow* App::m_window = nullptr; sf::Image* App::m_image = new sf::Image; sf::Sprite* App::m_sprite = new sf::Sprite; sf::Texture* App::m_texture = new sf::Texture;
void App::AddCommand(Command* c){ // TODO: You may refactor as needed }
/*! \brief We should execute commands in a data structure * Perhaps we will have to modify the logic in our * loop! * */ void App::ExecuteCommand(){ // TODO: You may refactor as needed }
/*! \brief Return a reference to our m_image, so that * we do not have to publicly expose it. * */ sf::Image& App::GetImage(){ return *m_image; }
/*! \brief Return a reference to our m_Texture so that * we do not have to publicly expose it. * */ sf::Texture& App::GetTexture(){ return *m_texture; }
/*! \brief Return a reference to our m_window so that we * do not have to publicly expose it. * */ sf::RenderWindow& App::GetWindow(){ return *m_window; }
/*! \brief Destroy we manually call at end of our program. * */ void App::Destroy(){ delete m_image; delete m_sprite; delete m_texture; }
/*! \brief Initializes the App and sets up the main * rendering window(i.e. our canvas.) */ void App::Init(void (*initFunction)(void)){ // Create our window m_window = new sf::RenderWindow(sf::VideoMode(600,400),"Mini-Paint alpha 0.0.2",sf::Style::Titlebar); m_window->setVerticalSyncEnabled(true); // Create an image which stores the pixels we will update m_image->create(600,400,sf::Color::White); assert(m_image != nullptr && "m_image != nullptr"); // Create a texture which lives in the GPU and will render our image m_texture->loadFromImage(*m_image); assert(m_texture != nullptr && "m_texture != nullptr"); // Create a sprite which is the entity that can be textured m_sprite->setTexture(*m_texture); assert(m_sprite != nullptr && "m_sprite != nullptr"); // Set our initialization function to perform any user // initialization m_initFunc = initFunction; }
/*! \brief Set a callback function which will be called each iteration of the main loop before drawing. * */ void App::UpdateCallback(void (*updateFunction)(void)){ m_updateFunc = updateFunction; }
/*! \brief Set a callback function which will be called each iteration of the main loop after update. * */ void App::DrawCallback(void (*drawFunction)(void)){ m_drawFunc = drawFunction; }
/*! \brief The main loop function which handles initialization and will be executed until the main window is closed. Within the loop function the update and draw callback functions will be called. * */ void App::Loop(){ // Call the init function m_initFunc();
// Start the main rendering loop while(m_window->isOpen()){ // Clear the window m_window->clear(); // Updates specified by the user m_updateFunc(); // Additional drawing specified by user m_drawFunc(); m_window->draw(*m_sprite); // Display the canvas m_window->display(); } }
App.hpp
#ifndef APP_HPP #define APP_HPP #include #include #include #include #include #include #include "Command.hpp" // Singleton for our Application called 'App'. class App{ private: // Member variables // Queue stores the next command to do. static std::queue m_commands; // Stack that stores the last action to occur. static std::stack m_undo; // Main image static sf::Image* m_image; // Create a sprite that we overaly // on top of the texture. static sf::Sprite* m_sprite; // Texture sent to the GPU for rendering static sf::Texture* m_texture;
// Member functions // Default constructor which is hidden in the Singleton App(); static void (*m_initFunc)(void); static void (*m_updateFunc)(void); static void (*m_drawFunc)(void);
public: static unsigned int pmouseX, pmouseY, mouseX, mouseY; static sf::RenderWindow* m_window; void AddCommand(Command* c); void ExecuteCommand(); static sf::Image& GetImage(); static sf::Texture& GetTexture(); static sf::RenderWindow& GetWindow();
static void Destroy(); static void Init(void (*initFunction)(void)); static void UpdateCallback(void (*updateFunction)(void)); static void DrawCallback(void (*drawFunction)(void)); static void Loop();
};
#endif
Command.cpp
#include #include "Command.hpp" Command::~Command(){ }
Command.hpp
#ifndef COMMAND_HPP #define COMMAND_HPP #include class Command{ private: std::string m_commandDescription; public: Command(std::string commandDescription) : m_commandDescription(commandDescription) {} // Destructor for a command virtual ~Command(); virtual bool execute() = 0; virtual bool undo() = 0; };
#endif
Draw.cpp #include #include "App.hpp" #include "Draw.hpp" bool Draw::execute(std::string commandDescription){ return true; }
bool Draw::undo(std::string commandDescription){ return true; }
Draw.hpp
#ifndef DRAW_H #define DRAW_H #include // Project header files #include "Command.hpp" class Draw : Command{ bool execute(std::string commandDescription); bool undo(std::string commandDescription); };
#endif
main.cpp
#include #include #include #include // Include standard library C++ libraries. #include #include #include "App.hpp" #include "Command.hpp" #include "Draw.hpp"
void initialization(void){ std::cout << "Starting the App" << std::endl; }
void update(void){ // Update our canvas sf::Event event; while(App::m_window->pollEvent(event)){ if(event.type == sf::Event::MouseMoved){ // Modify the pixel App::mouseX = event.mouseMove.x; App::mouseY = event.mouseMove.y; App::GetImage().setPixel(App::mouseX,App::mouseY,sf::Color::Blue); } }
if(sf::Mouse::isButtonPressed(sf::Mouse::Left)){ sf::Vector2i coordinate = sf::Mouse::getPosition(App::GetWindow()); std::cout << "Hmm, lots of repeats here: " << coordinate.x << "," << coordinate.y << std::endl; App::GetImage().setPixel(coordinate.x,coordinate.y,sf::Color::Red); } if(sf::Keyboard::isKeyPressed(sf::Keyboard::Escape)){ exit(EXIT_SUCCESS); } App::pmouseX = App::mouseX; App::pmouseY = App::mouseY; }
void draw(void){ // Static variable static int refreshRate = 0; ++refreshRate; if(refreshRate>10){ App::GetTexture().loadFromImage(App::GetImage()); refreshRate =0; } } int main(){ App::Init(&initialization); App::UpdateCallback(&update); App::DrawCallback(&draw); App::Loop(); App::Destroy();
return 0; }
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started