[c++ / SFML] [µPaint][2] A simple drawing program
German version: hier
Demonstration video: here
Due to the positive reactions on the first part this followup is going to show a way on how to implement a rectangle drawing feature.
Again this programm can be further developed. Features like (sorted by effort) drawing circles, zooming or deleting the last element via ctrl + z could be implemented.
I'm looking forward to answer questions, suggestions in the comments.
Setup
First of all c++ and the SimpleFastMultimediaLibrary (SFML) are used to create this minimalistic program. Install guide of SFML for Visual Studio: hier
Since steemit doesn't support syntax highlighting (yet), I suggest copying the below code to a program of your choice, that supports it. Like Notepad++.
Code
Like in the first part VertexArrays are used to construct the rectangles.
But why?
Simply less memory usage. An alternative to VertexArrays would be RectangleShapes. But when looking at the member variables in Shape.hpp (RectangleShape inherits that class) it should be clear, that RectangleShape uses more memory.
To be more precise: (See code)
Note that here the memory usage of the RectangleShape was at its minimum and the memory usage of the VertexArray (as it is used in the code: 16 vertices) was at its maximum.
Functional description (user)
Several methods could be considered. If you just wanted to draw fully filled rectangles, 4 vertices would be enough (see sf::Quads). This method would also only require two opposing points to construct the rectangle.
In order to realize being able to adjust the border thickness 3 points are required:
Release a mouse button -> position of point 1
Press a mouse button -> position of point 2
Release a mouse button -> position of point 3 (final)
Point 2 will only not be the same as point 3, when the mouse button will be held down after pressing it and moving the mouse to the final position before releasing it.
Concrete (user):
- Click once to set the first point
- Move the mouse as far as you want the border thickness to be
- Click and hold
- Move mouse to the final position and release the button
Note: In order to switch between the line and rectangle drawing modes you need to press "r" and "l" respectively.
Functional description (behind the scenes)
The variable "R_first_MB_released" gives evidence, whether or not the 1st point is defined.
"R_locked" will allow the preview rectangle to be calculated and drawn, since it is assumed that the 3rd point is located at the current mouse position.
Calculation of the 16 vertices
Basically 4 points will form a trapezoid (sf::Quads), wich are linked as shown in the diagram above via their positions.
Points 1 and 15 are defined through the 1st point the user specified.
Points 2 and 14 through the 2nd one.
Points 8 and 7 are set to the current mouse position, wich allows a preview of the rectangle.
Note the mouse_position-coordinate mapping as shown in the first part.
All the other points can be calculated, when separating the x and y portion.
Fore example point 6 (7.x - (1.x - 0.x ) | 7.y - (1.y - 0.y))
A nice side effect of this method: Depending on the arrangement of the 3 points you are able to even draw hexagons and 45° rotated filled rectangles:
c++
#include "SFML\Graphics.hpp"
#include <time.h> // srand()
#include <iostream>
#include <vector>
int main()
{
srand(time(NULL)); // initialize rand() by using the current system time
std::vector<sf::VertexArray> vertices; // vector in wich all vertexArrays (lines, rectangles) will be stored
enum class states { Line, Rectangle, Circle }; // current drawing mode
states current_state = states::Line;
// Lines
vertices.push_back(sf::VertexArray()); // 1st line
vertices[0].setPrimitiveType(sf::LinesStrip); // PrimitiveType of the 1st line: see https://www.sfml-dev.org/tutorials/2.4/graphics-vertex-array.php
bool L_locked = false; // (only when in line mode)
// Rectangles
bool R_first_MB_released = false; // (only when in rectangle mode)
sf::Vector2f R_first; // position of the 1st mouse button released
sf::Vector2f R_second; // position of the 2nd mouse button released
bool R_locked = false;
sf::VertexArray construction(sf::Quads); // construction rectangle (preview)
for (int i = 0; i < 16; i++)
construction.append(sf::Vertex());
//sf::RectangleShape s;
//std::cout << "RectangleShape: " << sizeof(s) << ", VertexArray: " << sizeof(construction) << std::endl; std::cin.get(); std::cin.get(); // RectangleShape vs VertexArray
sf::Color curr_col = sf::Color::Black; // color
sf::Vector2i last_Mouse_pos(0, 0);
sf::RenderWindow window(sf::VideoMode(1280, 720), "µPaint", sf::Style::Close, sf::ContextSettings(0, 0, 0)); // where everythings drawn to
window.setFramerateLimit(60);
sf::Vector2i Border_Offset(-5, -25); // See (peculiarities in the 1st part: https://steemit.com/steemstem/@numbo/c-sfml-paint-a-simple-drawing-program )
while (window.isOpen())
{
// Events
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::KeyPressed) // closing window
if (event.key.code == sf::Keyboard::Key::Escape)
window.close();
if (event.type == sf::Event::Closed) // closing window
window.close();
if (event.type == sf::Event::KeyPressed) // Switch between drawing modes
{
if (event.key.code == sf::Keyboard::Key::L)
current_state = states::Line;
else if (event.key.code == sf::Keyboard::Key::R)
current_state = states::Rectangle;
else if (event.key.code == sf::Keyboard::Key::C)
current_state = states::Circle;
}
if (event.type == sf::Event::MouseButtonPressed)
{
if (current_state == states::Line)
L_locked = true;
else if (current_state == states::Rectangle)
{
if (R_first_MB_released) // Only when the 1st button is already defined, define the second one
{
R_second = sf::Vector2f(sf::Mouse::getPosition() - window.getPosition() + Border_Offset);
R_locked = true;
}
}
else if (current_state == states::Circle)
{
// DIY
}
}
if (event.type == sf::Event::MouseButtonReleased)
{
if (current_state == states::Line)
{
// Add a new line
vertices.push_back(sf::VertexArray());
vertices[vertices.size() - 1].setPrimitiveType(sf::LinesStrip);
L_locked = false; // Reset line
}
else if (current_state == states::Rectangle)
{
if (!R_first_MB_released) // Define the 1st position
{
R_first_MB_released = true;
R_first = sf::Vector2f(sf::Mouse::getPosition() - window.getPosition() + Border_Offset);
}
if (R_first_MB_released && R_locked) // Set the final position -> save and reset
{
vertices.push_back(construction);
R_first_MB_released = false;
R_locked = false;
curr_col = sf::Color::Color(rand() % 255, rand() % 255, rand() % 255);
for (int i = 0; i < construction.getVertexCount(); i++)
construction[i].position = sf::Vector2f(0, 0);
}
}
else if (current_state == states::Circle)
{
// DIY
}
}
} // End Events
// Construction of a line
if (L_locked)
{
if (last_Mouse_pos != sf::Mouse::getPosition()) // Only add, when the mouse has moved (save memory)
{
//.append(Position, Farbe) : .append(MousePos - WindowPos + MouseOffset, curr_col)
vertices[vertices.size() - 1].append(sf::Vertex(sf::Vector2f(sf::Mouse::getPosition().x - window.getPosition().x + Border_Offset.x, sf::Mouse::getPosition().y - window.getPosition().y + Border_Offset.y), curr_col));
last_Mouse_pos = sf::Mouse::getPosition();
}
}
// Construction of a rectangle
if (R_locked)
{
if (last_Mouse_pos != sf::Mouse::getPosition())
{
//calculate (see Functional description (behind the scenes))
// = QuadsStrip DIY
sf::Vector2f render_mouse_pos(sf::Mouse::getPosition() - window.getPosition() + Border_Offset); // see mouse coordinate mapping in part 1
construction[0] = sf::Vertex(R_first, curr_col);
construction[1] = sf::Vertex(R_second, curr_col);
construction[2] = sf::Vertex(sf::Vector2f(render_mouse_pos.x - (R_second.x - R_first.x), R_second.y), curr_col);
construction[3] = sf::Vertex(sf::Vector2f(render_mouse_pos.x, R_first.y), curr_col);
construction[4] = sf::Vertex(construction[3].position, curr_col);
construction[5] = sf::Vertex(construction[2].position, curr_col);
construction[6] = sf::Vertex(sf::Vector2f(render_mouse_pos.x - (R_second.x - R_first.x), render_mouse_pos.y - (R_second.y - R_first.y)), curr_col);
construction[7] = sf::Vertex(sf::Vector2f(render_mouse_pos), curr_col);
construction[8] = sf::Vertex(construction[7].position, curr_col);
construction[9] = sf::Vertex(construction[6].position, curr_col);
construction[10] = sf::Vertex(sf::Vector2f(construction[1].position.x, construction[6].position.y), curr_col);
construction[11] = sf::Vertex(sf::Vector2f(construction[0].position.x, construction[7].position.y), curr_col);
construction[12] = sf::Vertex(construction[11].position, curr_col);
construction[13] = sf::Vertex(construction[10].position, curr_col);
construction[14] = sf::Vertex(construction[1].position, curr_col);
construction[15] = sf::Vertex(construction[0].position, curr_col);
last_Mouse_pos = sf::Mouse::getPosition(); // reset mouse position
}
}
//curr_col = sf::Color::Color(rand() % 255, rand() % 255, rand() % 255);
//std::cout << "vertices in line " << lines_number << ": " << vertices[lines_number].getVertexCount() << std::endl;
std::cout << "V1: " << R_first.x << " | " << R_first.y << " V2: " << R_second.x << " | " << "vertices-vec: " << vertices.size() << std::endl;
window.clear(sf::Color::White); // delete the current scene with a specific color
// draw everything
for (int i = 0; i < vertices.size(); i++)
{
window.draw(vertices[i]);
}
window.draw(construction); // construction rectangle -> preview
window.display();
}
return 0;
}
Congratulations @numbo! You have completed some achievement on Steemit and have been rewarded with new badge(s) :
Award for the number of upvotes
Click on any badge to view your own Board of Honor on SteemitBoard.
To support your work, I also upvoted your post!
For more information about SteemitBoard, click here
If you no longer want to receive notifications, reply to this comment with the word
STOP
Congratulations @numbo! You have completed some achievement on Steemit and have been rewarded with new badge(s) :
Award for the number of upvotes
Click on any badge to view your own Board of Honor on SteemitBoard.
For more information about SteemitBoard, click here
If you no longer want to receive notifications, reply to this comment with the word
STOP
Do not miss the last announcement from @steemitboard!
Congratulations @numbo! You have completed some achievement on Steemit and have been rewarded with new badge(s) :
Award for the number of upvotes
Click on any badge to view your own Board of Honor on SteemitBoard.
For more information about SteemitBoard, click here
If you no longer want to receive notifications, reply to this comment with the word
STOP
Congratulations @numbo! You have completed some achievement on Steemit and have been rewarded with new badge(s) :
Award for the number of upvotes
Click on the badge to view your Board of Honor.
If you no longer want to receive notifications, reply to this comment with the word
STOP
Do not miss the last post from @steemitboard!
Participate in the SteemitBoard World Cup Contest!
Collect World Cup badges and win free SBD
Support the Gold Sponsors of the contest: @good-karma and @lukestokes
Congratulations @numbo! You have completed the following achievement on Steemit and have been rewarded with new badge(s) :
Award for the number of upvotes
Click on the badge to view your Board of Honor.
If you no longer want to receive notifications, reply to this comment with the word
STOP
Do not miss the last post from @steemitboard:
SteemitBoard World Cup Contest - Final results coming soon
Congratulations @numbo! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :
Click here to view your Board of Honor
If you no longer want to receive notifications, reply to this comment with the word
STOP
Do not miss the last post from @steemitboard:
Congratulations @numbo! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :
Click here to view your Board
If you no longer want to receive notifications, reply to this comment with the word
STOP
Congratulations @numbo! You received a personal award!
Happy Birthday! - You are on the Steem blockchain for 1 year!
Click here to view your Board
Congratulations @numbo! You received a personal award!
You can view your badges on your Steem Board and compare to others on the Steem Ranking
Vote for @Steemitboard as a witness to get one more award and increased upvotes!