SFML/GRAPHICS КРУГОВЫЕ ДИАГРАММЫ
Упражняемся с круговыми диаграммами. Я тут потихонечку подхожу к реализации своей давней мечты сделать программу наподобие Baobab или scanner. Написать целиком такую программу на сегодняшний день не может ни одна нейросетка. Поэтому приходится продвигаться потихоньку, поэтапно... Думаю, сначала разобрать несколько примеров базового функционала, разбив глобальную задачу на несколько этапов и статей, а затем соберу все вместе и реализую таким образом давно задуманный проект. Здесь первое поле ввода предназначено для ввода цифры от 0 до 100, которая суть процент площади красного сектора от круга. А второе поле ввода суть процент желтого сектора от красного сектора, то есть, желтый сектор не может быть больше красного.
#include <SFML/Graphics.hpp> #include <SFML/Window.hpp> #include <cmath> #include <string> int main() { sf::ContextSettings settings; settings.antialiasingLevel = 8; sf::RenderWindow window(sf::VideoMode(800, 600), "SFML Program", sf::Style::Default, settings); window.setFramerateLimit(60); sf::Font font; if (!font.loadFromFile("arial.ttf")) { return -1; } // Меню sf::RectangleShape menuBar(sf::Vector2f(800, 30)); menuBar.setPosition(0, 0); menuBar.setFillColor(sf::Color(200, 200, 200)); sf::Text menuFile("File", font, 20); menuFile.setPosition(10, 5); sf::Text menuEdit("Edit", font, 20); menuEdit.setPosition(60, 5); sf::Text menuHelp("Help", font, 20); menuHelp.setPosition(110, 5); bool showFileMenu = false, showEditMenu = false, showHelpMenu = false; sf::RectangleShape fileMenu(sf::Vector2f(100, 90)); fileMenu.setFillColor(sf::Color(220, 220, 220)); fileMenu.setPosition(10, 30); fileMenu.setOutlineThickness(1); fileMenu.setOutlineColor(sf::Color::Black); sf::Text fileNew("New", font, 20); fileNew.setPosition(15, 35); sf::Text fileOpen("Open", font, 20); fileOpen.setPosition(15, 60); sf::Text fileExit("Exit", font, 20); fileExit.setPosition(15, 85); sf::RectangleShape editMenu(sf::Vector2f(100, 60)); editMenu.setFillColor(sf::Color(220, 220, 220)); editMenu.setPosition(60, 30); editMenu.setOutlineThickness(1); editMenu.setOutlineColor(sf::Color::Black); sf::Text editCopy("Copy", font, 20); editCopy.setPosition(65, 35); sf::Text editPaste("Paste", font, 20); editPaste.setPosition(65, 60); sf::RectangleShape helpMenu(sf::Vector2f(100, 60)); helpMenu.setFillColor(sf::Color(220, 220, 220)); helpMenu.setPosition(110, 30); helpMenu.setOutlineThickness(1); helpMenu.setOutlineColor(sf::Color::Black); sf::Text helpAbout("About", font, 20); helpAbout.setPosition(115, 35); sf::Text helpDocs("Docs", font, 20); helpDocs.setPosition(115, 60); // Виджеты для красного сектора (возвращаем исходное положение) sf::RectangleShape redInputBox(sf::Vector2f(150, 30)); redInputBox.setPosition(10, 100); redInputBox.setFillColor(sf::Color::White); redInputBox.setOutlineThickness(2); redInputBox.setOutlineColor(sf::Color::Black); sf::Text redInputText("", font, 20); redInputText.setPosition(15, 105); redInputText.setFillColor(sf::Color::Black); sf::RectangleShape redOkButton(sf::Vector2f(50, 30)); redOkButton.setPosition(170, 100); redOkButton.setFillColor(sf::Color::Green); sf::Text redOkText("OK", font, 20); redOkText.setPosition(180, 105); redOkText.setFillColor(sf::Color::White); // Виджеты для желтого сектора sf::RectangleShape yellowInputBox(sf::Vector2f(150, 30)); yellowInputBox.setPosition(10, 150); yellowInputBox.setFillColor(sf::Color::White); yellowInputBox.setOutlineThickness(2); yellowInputBox.setOutlineColor(sf::Color::Black); sf::Text yellowInputText("", font, 20); yellowInputText.setPosition(15, 155); yellowInputText.setFillColor(sf::Color::Black); sf::RectangleShape yellowOkButton(sf::Vector2f(50, 30)); yellowOkButton.setPosition(170, 150); yellowOkButton.setFillColor(sf::Color::Green); sf::Text yellowOkText("OK", font, 20); yellowOkText.setPosition(180, 155); yellowOkText.setFillColor(sf::Color::White); // Логика курсора bool redInputFocused = false; bool yellowInputFocused = false; sf::RectangleShape cursor(sf::Vector2f(2, 20)); cursor.setFillColor(sf::Color::Black); bool cursorVisible = true; sf::Clock cursorClock; float cursorBlinkTime = 0.5f; // Круговая диаграмма float redPercentage = 50.0f; // Процент от полного круга float yellowPercentage = 50.0f; // Процент от угла красного сектора const float PI = 3.14159265358979323846f; const int centerX = 400; const int centerY = 300; const int outerRadius = 115; const int innerRadius = 100; sf::RenderTexture diagramTexture; if (!diagramTexture.create(400, 400)) { return -1; } diagramTexture.setSmooth(true); std::string redInputString; std::string yellowInputString; while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) { window.close(); } if (event.type == sf::Event::MouseButtonPressed) { sf::Vector2f mousePos = window.mapPixelToCoords(sf::Vector2i(event.mouseButton.x, event.mouseButton.y)); redInputFocused = redInputBox.getGlobalBounds().contains(mousePos); yellowInputFocused = yellowInputBox.getGlobalBounds().contains(mousePos); if (menuFile.getGlobalBounds().contains(mousePos)) { showFileMenu = !showFileMenu; showEditMenu = false; showHelpMenu = false; } else if (menuEdit.getGlobalBounds().contains(mousePos)) { showEditMenu = !showEditMenu; showFileMenu = false; showHelpMenu = false; } else if (menuHelp.getGlobalBounds().contains(mousePos)) { showHelpMenu = !showHelpMenu; showFileMenu = false; showEditMenu = false; } else if (!fileMenu.getGlobalBounds().contains(mousePos) && !editMenu.getGlobalBounds().contains(mousePos) && !helpMenu.getGlobalBounds().contains(mousePos)) { showFileMenu = showEditMenu = showHelpMenu = false; } if (showFileMenu && fileExit.getGlobalBounds().contains(mousePos)) { window.close(); } if (redOkButton.getGlobalBounds().contains(mousePos)) { if (!redInputString.empty()) { float newPercentage = std::stof(redInputString); redPercentage = std::max(0.0f, std::min(100.0f, newPercentage)); // Желтый процент остается тем же относительно нового красного угла redInputString.clear(); redInputText.setString(""); } } if (yellowOkButton.getGlobalBounds().contains(mousePos)) { if (!yellowInputString.empty()) { float newPercentage = std::stof(yellowInputString); yellowPercentage = std::max(0.0f, std::min(100.0f, newPercentage)); yellowInputString.clear(); yellowInputText.setString(""); } } } if (event.type == sf::Event::TextEntered) { if (redInputFocused) { if (event.text.unicode >= '0' && event.text.unicode <= '9') { redInputString += static_cast<char>(event.text.unicode); redInputText.setString(redInputString); } else if (event.text.unicode == 8 && !redInputString.empty()) { redInputString.pop_back(); redInputText.setString(redInputString); } } else if (yellowInputFocused) { if (event.text.unicode >= '0' && event.text.unicode <= '9') { yellowInputString += static_cast<char>(event.text.unicode); yellowInputText.setString(yellowInputString); } else if (event.text.unicode == 8 && !yellowInputString.empty()) { yellowInputString.pop_back(); yellowInputText.setString(yellowInputString); } } } } if (cursorClock.getElapsedTime().asSeconds() >= cursorBlinkTime) { cursorVisible = !cursorVisible; cursorClock.restart(); } window.clear(sf::Color::White); // Отрисовка виджетов (сначала, чтобы меню перекрывало их) window.draw(redInputBox); window.draw(redInputText); window.draw(redOkButton); window.draw(redOkText); window.draw(yellowInputBox); window.draw(yellowInputText); window.draw(yellowOkButton); window.draw(yellowOkText); if (redInputFocused && cursorVisible) { float cursorX = 15 + redInputText.getLocalBounds().width; cursor.setPosition(cursorX, 105); window.draw(cursor); } if (yellowInputFocused && cursorVisible) { float cursorX = 15 + yellowInputText.getLocalBounds().width; cursor.setPosition(cursorX, 155); window.draw(cursor); } // Отрисовка круговой диаграммы diagramTexture.clear(sf::Color::Transparent); float redAngle = (redPercentage / 100.0f) * 2 * PI; float yellowAngle = (yellowPercentage / 100.0f) * redAngle; // Желтый угол как процент от красного const int segments = 64; sf::ConvexShape outerSector; outerSector.setPointCount(segments + 2); outerSector.setPoint(0, sf::Vector2f(200, 200)); for (int i = 0; i <= segments; ++i) { float currentAngle = (yellowAngle * i) / segments; outerSector.setPoint(i + 1, sf::Vector2f( 200 + outerRadius * cos(currentAngle), 200 + outerRadius * sin(currentAngle) )); } outerSector.setFillColor(sf::Color::Yellow); sf::ConvexShape innerSector; innerSector.setPointCount(segments + 2); innerSector.setPoint(0, sf::Vector2f(200, 200)); for (int i = 0; i <= segments; ++i) { float currentAngle = (redAngle * i) / segments; innerSector.setPoint(i + 1, sf::Vector2f( 200 + innerRadius * cos(currentAngle), 200 + innerRadius * sin(currentAngle) )); } innerSector.setFillColor(sf::Color::Red); sf::CircleShape outerCircle(outerRadius); outerCircle.setPosition(200 - outerRadius, 200 - outerRadius); outerCircle.setFillColor(sf::Color::White); outerCircle.setOutlineThickness(1); outerCircle.setOutlineColor(sf::Color::Black); sf::CircleShape innerCircle(innerRadius); innerCircle.setPosition(200 - innerRadius, 200 - innerRadius); innerCircle.setFillColor(sf::Color(0, 0, 255, 200)); diagramTexture.draw(outerCircle); diagramTexture.draw(outerSector); diagramTexture.draw(innerCircle); diagramTexture.draw(innerSector); diagramTexture.display(); sf::Sprite diagramSprite(diagramTexture.getTexture()); diagramSprite.setPosition(centerX - 200, centerY - 200); window.draw(diagramSprite); // Отрисовка меню поверх всего window.draw(menuBar); window.draw(menuFile); window.draw(menuEdit); window.draw(menuHelp); if (showFileMenu) { window.draw(fileMenu); window.draw(fileNew); window.draw(fileOpen); window.draw(fileExit); } if (showEditMenu) { window.draw(editMenu); window.draw(editCopy); window.draw(editPaste); } if (showHelpMenu) { window.draw(helpMenu); window.draw(helpAbout); window.draw(helpDocs); } window.display(); } return 0; }
g++ main.cpp -o madPie.exe -mwindows -DSFML_STATIC \ -I/ucrt64/include -L/ucrt64/lib \ -lsfml-graphics-s -lsfml-window-s -lsfml-system-s \ -lopengl32 -lgdi32 -lfreetype -lwinmm \ -Wl,-Bstatic -lstdc++ -lpthread -static -static-libgcc -static-libstdc++