diff --git a/.gitmodules b/.gitmodules index c79a8df..c474ebc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ -[submodule "third_party/imgui"] - path = third_party/imgui - url = https://github.com/ocornut/imgui.git -[submodule "third_party/imgui-sfml"] - path = third_party/imgui-sfml - url = https://github.com/eliasdaler/imgui-sfml.git +[submodule "third_party/raygui"] + path = third_party/raygui + url = https://github.com/raysan5/raygui.git +[submodule "third_party/glm"] + path = third_party/glm + url = https://github.com/g-truc/glm.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 430026f..dfcf4a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,14 +1,14 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.8.2) project(Triangles LANGUAGES CXX VERSION 1.0) -if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/third_party/imgui/imgui.h) - message(FATAL_ERROR "third_party/imgui/imgui.h is missing!\n \ -Please update the GameDevTools submodule by running 'git submodule init' and \ +if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/third_party/glm/glm/glm.hpp) + message(FATAL_ERROR "third_party/glm/glm/glm.hpp is missing!\n \ +Please update the glm submodule by running 'git submodule init' and \ 'git submodule update'!") endif() -if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/third_party/imgui-sfml/imgui-SFML.h) - message(FATAL_ERROR "third_party/imgui-sfml/imgui-SFML.h is missing!\n \ -Please update the GameDevTools submodule by running 'git submodule init' and \ +if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/third_party/raygui/src/raygui.h) + message(FATAL_ERROR "third_party/raygui/src/raygui.h is missing!\n \ +Please update the raygui submodule by running 'git submodule init' and \ 'git submodule update'!") endif() @@ -16,22 +16,16 @@ if(NOT DEFINED CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Debug") endif() -if(CMAKE_BUILD_TYPE MATCHES "Debug") - set(ImGuiDemo "third_party/imgui/imgui_demo.cpp") -else() - set(ImGuiDemo "") -endif() - set(Triangles_MAIN_SOURCES src/main.cpp ) set(Triangles_LIB_SOURCES src/state.cpp - third_party/imgui/imgui.cpp - third_party/imgui/imgui_draw.cpp - third_party/imgui/imgui_widgets.cpp - third_party/imgui-sfml/imgui-SFML.cpp + src/shape.cpp + src/triangle.cpp + src/circle.cpp + src/raygui.cpp ) if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") @@ -57,44 +51,28 @@ target_link_libraries(Triangles PUBLIC TrianglesLib) target_compile_features(TrianglesLib PUBLIC cxx_std_17) if(BUILD_SHARED_LIBS OR (UNIX AND NOT CYGWIN) OR (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")) - find_package(SFML 2 REQUIRED - COMPONENTS audio network graphics window system) + find_package(raylib REQUIRED) else() - find_package(SFML 2 REQUIRED - COMPONENTS audio-s network-s graphics-s window-s system-s) - add_definitions(-DSFML_STATIC) + find_package(raylib REQUIRED) endif() if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") target_link_libraries(TrianglesLib PUBLIC - sfml-graphics sfml-window sfml-system + raylib opengl32 ) else() target_link_libraries(TrianglesLib PUBLIC - sfml-graphics sfml-window sfml-system + raylib GL ) endif() -if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") - target_include_directories(TrianglesLib PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/src - ${SFML_INCLUDE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/third_party/imgui # imgui related headers - ${CMAKE_CURRENT_SOURCE_DIR}/third_party/imgui-sfml # imgui-sfml related headers - ) -else() - target_include_directories(TrianglesLib PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/src - ${SFML_INCLUDE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/build_include # imgui related headers - ) -endif() - -# Use macro to override imgui config header -target_compile_definitions(TrianglesLib PUBLIC - "IMGUI_USER_CONFIG=\"${CMAKE_CURRENT_SOURCE_DIR}/third_party/imgui-sfml/imconfig-SFML.h\"") +target_include_directories(TrianglesLib PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/third_party/raygui/src + ${CMAKE_CURRENT_SOURCE_DIR}/third_party/glm +) if(CMAKE_BUILD_TYPE MATCHES "Debug") set(Triangles_UNIT_TEST_SOURCES diff --git a/build_include/imconfig.h b/build_include/imconfig.h deleted file mode 120000 index b087668..0000000 --- a/build_include/imconfig.h +++ /dev/null @@ -1 +0,0 @@ -../third_party/imgui-sfml/imconfig-SFML.h \ No newline at end of file diff --git a/build_include/imgui-SFML.h b/build_include/imgui-SFML.h deleted file mode 120000 index be86680..0000000 --- a/build_include/imgui-SFML.h +++ /dev/null @@ -1 +0,0 @@ -../third_party/imgui-sfml/imgui-SFML.h \ No newline at end of file diff --git a/build_include/imgui-SFML_export.h b/build_include/imgui-SFML_export.h deleted file mode 120000 index 6597ef8..0000000 --- a/build_include/imgui-SFML_export.h +++ /dev/null @@ -1 +0,0 @@ -../third_party/imgui-sfml/imgui-SFML_export.h \ No newline at end of file diff --git a/build_include/imgui.h b/build_include/imgui.h deleted file mode 120000 index a826818..0000000 --- a/build_include/imgui.h +++ /dev/null @@ -1 +0,0 @@ -../third_party/imgui/imgui.h \ No newline at end of file diff --git a/build_include/imgui_internal.h b/build_include/imgui_internal.h deleted file mode 120000 index af37369..0000000 --- a/build_include/imgui_internal.h +++ /dev/null @@ -1 +0,0 @@ -../third_party/imgui/imgui_internal.h \ No newline at end of file diff --git a/build_include/imstb_rectpack.h b/build_include/imstb_rectpack.h deleted file mode 120000 index 850bd4e..0000000 --- a/build_include/imstb_rectpack.h +++ /dev/null @@ -1 +0,0 @@ -../third_party/imgui/imstb_rectpack.h \ No newline at end of file diff --git a/build_include/imstb_textedit.h b/build_include/imstb_textedit.h deleted file mode 120000 index 266b3ee..0000000 --- a/build_include/imstb_textedit.h +++ /dev/null @@ -1 +0,0 @@ -../third_party/imgui/imstb_textedit.h \ No newline at end of file diff --git a/build_include/imstb_truetype.h b/build_include/imstb_truetype.h deleted file mode 120000 index 4a8033e..0000000 --- a/build_include/imstb_truetype.h +++ /dev/null @@ -1 +0,0 @@ -../third_party/imgui/imstb_truetype.h \ No newline at end of file diff --git a/src/circle.cpp b/src/circle.cpp new file mode 100644 index 0000000..90c391e --- /dev/null +++ b/src/circle.cpp @@ -0,0 +1,60 @@ +#include "circle.hpp" + +#include + +Tri::Circle::Circle() : +Shape(), +position{0.0f, 0.0f}, +radius(4.0f) +{} + +Tri::Circle::Circle(glm::vec2 pos) : +Shape(), +position(pos), +radius(4.0f) +{} + +Tri::Circle::Circle(glm::vec2 pos, float radius) : +Shape(), +position(pos), +radius(radius) +{} + +Tri::Circle::Circle(glm::vec2 pos, float radius, Color fillColor) : +Shape(fillColor), +position(pos), +radius(radius) +{} + +Tri::Circle::Circle(glm::vec2 pos, float radius, Color fillColor, Color outlineColor) : +Shape(fillColor, outlineColor), +position(pos), +radius(radius) +{} + +Tri::Shape& Tri::Circle::draw() { + glm::vec2 transformed = transform * glm::vec3(position.x, position.y, 1.0f); + transformed.x = std::roundf(transformed.x); + transformed.y = std::roundf(transformed.y); + DrawCircle(transformed.x, transformed.y, radius, fillColor); + DrawCircleLines(transformed.x, transformed.y, radius, outlineColor); + return *this; +} + +void Tri::Circle::getVertices(std::vector &verticesOut) const { + verticesOut.clear(); + verticesOut.push_back(position); +} + +void Tri::Circle::getTransformedVertices(std::vector &verticesOut) const { + verticesOut.clear(); + verticesOut.push_back(transform * glm::vec3{position.x, position.y, 1.0f}); +} + +float Tri::Circle::getRadius() const { + return radius; +} + +void Tri::Circle::setRadius(float radius) { + this->radius = radius; +} diff --git a/src/circle.hpp b/src/circle.hpp new file mode 100644 index 0000000..f328397 --- /dev/null +++ b/src/circle.hpp @@ -0,0 +1,29 @@ +#ifndef TRIANGLES_CIRCLE_HPP +#define TRIANGLES_CIRCLE_HPP + +#include "shape.hpp" + +namespace Tri { + +struct Circle : public Shape { + Circle(); + Circle(glm::vec2 pos); + Circle(glm::vec2 pos, float radius); + Circle(glm::vec2 pos, float radius, Color fillColor); + Circle(glm::vec2 pos, float radius, Color fillColor, Color outlineColor); + + glm::vec2 position; + float radius; + + virtual Shape& draw() override; + + virtual void getVertices(std::vector &verticesOut) const override; + virtual void getTransformedVertices(std::vector &verticesOut) const override; + + virtual float getRadius() const override; + virtual void setRadius(float radius) override; +}; // struct Circle + +} // namespace Tri + +#endif diff --git a/src/helpers.hpp b/src/helpers.hpp index 391a474..b6efb59 100644 --- a/src/helpers.hpp +++ b/src/helpers.hpp @@ -3,9 +3,12 @@ #include #include +#include -#include +#include +#include +#include "shape.hpp" #include "state.hpp" #define SHOW_HELP_WIDTH (state->get_width() / 2.0f) @@ -20,163 +23,265 @@ namespace Tri { // so this should be called during update, not draw inline void draw_help(Tri::State *state) { if(state->get_flags().test(Tri::State::F_DISPLAY_HELP)) { - ImGui::SetNextWindowPos(sf::Vector2f(10.0f, 10.0f)); - ImGui::SetNextWindowSize(sf::Vector2f( - state->get_width() - 20.0f, - state->get_height() - 20.0f)); - ImGui::SetNextWindowBgAlpha(0.7f); - ImGui::Begin("Help Window", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings); - ImGui::Text("This is the help window - Press \"H\" to toggle this window"); - ImGui::Text("Click anywhere to create triangles, one point at a time"); - ImGui::Text("You cannot draw when a window is open"); - ImGui::Text("Press \"U\" to undo. Clicking will remove all future undo history"); - ImGui::Text("Press \"R\" to redo."); - ImGui::Text("Press \"C\" to change colors"); - ImGui::Text("Press \"B\" to change background color"); - ImGui::Text("Press \"P\" to set current color to a color on screen"); - ImGui::Text("Press \"S\" to save what was drawn as a png image"); - if(ImGui::Button("Close")) { + GuiFade(1.0f); + if(!GuiWindowBox({10.0f, + 10.0f, + 800.0f - 20.0f, + 600.0f - 20.0f}, + "Help")) { + GuiLabel( + {4.0f, 4.0f, 800.0f - 28.0f, 16.0f}, + "This is the help window - Press \"H\" to toggle this window"); + GuiLabel( + {4.0f, 22.0f, 800.0f - 28.0f, 16.0f}, + "Click anywhere to create triangles, one point at a time"); + GuiLabel( + {4.0f, 40.0f, 800.0f - 28.0f, 16.0f}, + "You cannot draw when a window is open"); + GuiLabel( + {4.0f, 58.0f, 800.0f - 28.0f, 16.0f}, + "Press \"U\" to undo. Clicking will remove all future undo history"); + GuiLabel( + {4.0f, 76.0f, 800.0f - 28.0f, 16.0f}, + "Press \"R\" to redo."); + GuiLabel( + {4.0f, 94.0f, 800.0f - 28.0f, 16.0f}, + "Press \"C\" to change colors"); + GuiLabel( + {4.0f, 112.0f, 800.0f - 28.0f, 16.0f}, + "Press \"B\" to change background color"); + GuiLabel( + {4.0f, 130.0f, 800.0f - 28.0f, 16.0f}, + "Press \"P\" to set current color to a color on screen"); + GuiLabel( + {4.0f, 148.0f, 800.0f - 28.0f, 16.0f}, + "Press \"S\" to save what was drawn as a png image"); + if(GuiButton({4.0f, 168.0f, 100.0f, 16.0f}, "Close")) { + state->close_help(); + } + } else { state->close_help(); } - ImGui::End(); } } inline void draw_notification(Tri::State *state) { float alpha = state->get_notification_alpha(); if(alpha > 0.0f) { - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, alpha); - - ImGui::SetNextWindowPos(sf::Vector2f( - (state->get_width() - SHOW_HELP_WIDTH) / 2.0f, - (state->get_height() - SHOW_HELP_HEIGHT) / 2.0f)); - ImGui::SetNextWindowSize(sf::Vector2f( - SHOW_HELP_WIDTH, - SHOW_HELP_HEIGHT)); - ImGui::Begin( - "Notification Window", - nullptr, - ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings); - ImGui::SetWindowFontScale(3.0f); - ImGui::Text("%s", state->get_notification_text()); - ImGui::End(); - - ImGui::PopStyleVar(); + GuiFade(alpha); + GuiPanel({(800 - SHOW_HELP_WIDTH) / 2.0f, + (600 - SHOW_HELP_HEIGHT) / 2.0f, + SHOW_HELP_WIDTH, + SHOW_HELP_HEIGHT}); + GuiLabel({4.0f, 4.0f, SHOW_HELP_WIDTH - 8.0f, SHOW_HELP_HEIGHT - 8.0f}, + state->get_notification_text()); } } inline void draw_color_picker(Tri::State *state) { if(state->get_flags().test(Tri::State::F_DISPLAY_COLOR_P)) { - ImGui::Begin("Tri Color Picker"); - ImGui::ColorPicker4("Tri Color", state->get_color()); - if(ImGui::Button("Close")) { + GuiFade(1.0f); + if(!GuiWindowBox({4.0f, 4.0f, 242.0f, 292.0f}, "Tri Color Picker")) { + auto &colorArray = state->get_color(); + Color color = GuiColorPicker( + {4.0f, 4.0f, 234.0f, 264.0f}, + {(unsigned char)(colorArray[0] * 255.0f), + (unsigned char)(colorArray[1] * 255.0f), + (unsigned char)(colorArray[2] * 255.0f), + (unsigned char)(colorArray[3] * 255.0f)}); + colorArray = { + color.r / 255.0f, + color.g / 255.0f, + color.b / 255.0f, + color.a / 255.0f + }; + if(GuiButton({4.0f, 268.0f, 234.0f, 16.0f}, "Close")) { + state->close_color_picker(); + } + } else { state->close_color_picker(); } - ImGui::End(); } } inline void draw_bg_color_picker(Tri::State *state) { if(state->get_flags().test(Tri::State::F_DISPLAY_BG_COLOR_P)) { - ImGui::Begin("BG Color Picker"); - ImGui::ColorPicker3("BG Color", state->get_bg_color()); - if(ImGui::Button("Close")) { + GuiFade(1.0f); + if(!GuiWindowBox({250.0f, 4.0f, 242.0f, 292.0f}, "BG Color Picker")) { + auto &colorArray = state->get_bg_color(); + Color color = GuiColorPicker( + {4.0f, 4.0f, 234.0f, 264.0f}, + {(unsigned char)(colorArray[0] * 255.0f), + (unsigned char)(colorArray[1] * 255.0f), + (unsigned char)(colorArray[2] * 255.0f), + 255}); + colorArray = { + color.r / 255.0f, + color.g / 255.0f, + color.b / 255.0f + }; + if(GuiButton({4.0f, 268.0f, 234.0f, 16.0f}, "Close")) { + state->close_bg_color_picker(); + } + } else { state->close_bg_color_picker(); } - ImGui::End(); } } inline void draw_save(Tri::State *state) { if(state->get_flags().test(Tri::State::F_DISPLAY_SAVE)) { auto *filenameBuffer = state->get_save_filename_buffer(); - ImGui::Begin("Save"); - ImGui::InputText("Filename", filenameBuffer->data(), filenameBuffer->size() - 1); - if(ImGui::Button("Save")) { - if(state->do_save()) { - state->close_save(); + GuiFade(1.0f); + if(!GuiWindowBox({4.0f, 300.0f, 292.0f, 292.0f}, "Save")) { + GuiTextBox( + {4.0f, 4.0f, 284.0f, 150.0f}, + filenameBuffer->data(), + filenameBuffer->size() - 1, + true); + if(GuiButton({4.0f, 158.0f, 50.0f, 16.0f}, "Save")) { + if(state->do_save()) { + state->close_save(); + } } - } else if(ImGui::Button("Cancel")) { + const std::string &string = state->failed_message(); + if(!string.empty()) { + GuiLabel({4.0f, 178.0f, 284.0f, 16.0f}, string.c_str()); + } + } else { state->close_save(); } - auto string_view = state->failed_message(); - if(!string_view.empty()) { - ImGui::TextUnformatted(string_view.data(), string_view.data() + string_view.size()); - } - ImGui::End(); } } inline void draw_change_size(Tri::State *state) { if(state->get_flags().test(Tri::State::F_DISPLAY_CHANGE_SIZE)) { - ImGui::Begin("ChangeSize"); - ImGui::InputInt2("Width and Height", state->get_input_width_height()); - auto string_view = state->failed_message(); - if(!string_view.empty()) { - ImGui::TextUnformatted(string_view.data(), string_view.data() + string_view.size()); - } - if(ImGui::Button("Cancel")) { - state->close_input_width_height_window(); - } - if(ImGui::Button("Set")) { - if(state->change_width_height()) { + GuiFade(1.0f); + if(!GuiWindowBox({300.0f, 300.0f, 292.0f, 292.0f}, "Change Size")) { + GuiValueBox( + {4.0f, 4.0f, 80.0f, 16.0f}, + "Width", + state->get_input_width(), + 800, + 1920, + true); + GuiValueBox( + {4.0f, 24.0f, 80.0f, 16.0f}, + "Height", + state->get_input_height(), + 600, + 1080, + true); + const std::string &failMessage = state->failed_message(); + if(!failMessage.empty()) { + GuiLabel({4.0f, 44.0f, 284.0f, 16.0f}, failMessage.c_str()); + } + if(GuiButton({4.0f, 70.0f, 70.0f, 16.0f}, "Cancel")) { state->close_input_width_height_window(); } + if(GuiButton({58.0f, 70.0f, 50.0f, 16.0f}, "Set")) { + if(state->change_width_height()) { + state->close_input_width_height_window(); + } + } + } else { + state->close_input_width_height_window(); } - ImGui::End(); } } inline bool is_within_shape( - const sf::ConvexShape &shape, - sf::Vector2f xy) { - std::optional is_right; - sf::Transform t = shape.getTransform(); - for(unsigned int i = 0; i < shape.getPointCount(); ++i) { - sf::Vector2f t_a = t.transformPoint(shape.getPoint(i)); - sf::Vector2f t_b = t.transformPoint(shape.getPoint((i + 1) % shape.getPointCount())); + const Tri::Shape &shape, + glm::vec2 xy) { + float radius = shape.getRadius(); + std::vector vertices; + shape.getTransformedVertices(vertices); + if(radius > 0.0f) { + assert(vertices.size() == 1); - t_b = t_b - t_a; - t_a = xy - t_a; + xy = xy - vertices[0]; + return std::sqrt(xy.x * xy.x + xy.y * xy.y) <= radius; + } else { + assert(vertices.size() > 2); - // TODO - // cross product, where z coordinate is 0 - // Use sign of z value to determine if line is to right or left - // - // a x b = c - // a_1 b_1 0 - // a_2 b_2 0 - // 0 0 a_1 * b_2 - a_2 * b_1 - // - // in this case "a" is "t_b" - float z = t_b.x * t_a.y - t_b.y * t_a.x; - if(is_right.has_value()) { - if(is_right.value()) { - if(z >= 0.0f) { + std::optional is_right; + for(unsigned int i = 0; i < vertices.size(); ++i) { + glm::vec2 t_a, t_b; + t_b = vertices[(i + 1) % vertices.size()] - vertices[i]; + t_a = xy - vertices[i]; + + // cross product, where z coordinate is 0 + // Use sign of z value to determine if line is to right or left + // + // a x b = c + // a_1 b_1 0 + // a_2 b_2 0 + // 0 0 a_1 * b_2 - a_2 * b_1 + // + // in this case "a" is "t_b" + float z = t_b.x * t_a.y - t_b.y * t_a.x; + if(is_right.has_value()) { + if(is_right.value()) { + if(z >= 0.0f) { + return false; + } + } else if(z < 0.0f) { return false; } - } else if(z < 0.0f) { - return false; + } else { + is_right = z < 0.0f; } - } else { - is_right = z < 0.0f; } + return true; } - return true; } - inline sf::Color invert_color(const sf::Color &other) { - return sf::Color(255 - other.r, 255 - other.g, 255 - other.b); + inline Color invert_color(const Color &other) { + return Color{ + (unsigned char)(255 - (int)other.r), + (unsigned char)(255 - (int)other.g), + (unsigned char)(255 - (int)other.b), + other.a}; } inline void draw_edit_tri(Tri::State *state) { if(state->get_flags().test(Tri::State::F_TRI_EDIT_MODE)) { - ImGui::Begin("Edit Tri Color Picker"); - ImGui::ColorPicker4("Tri Color", state->get_selected_tri_color()); - if(ImGui::Button("Close")) { + GuiFade(1.0f); + if(!GuiWindowBox({500.0f, 4.0f, 242.0f, 292.0f}, "Edit Tri Color Picker")) { + auto &colorArray = state->get_selected_tri_color(); + Color color = GuiColorPicker( + {4.0f, 4.0f, 234.0f, 264.0f}, + {(unsigned char)(colorArray[0] * 255.0f), + (unsigned char)(colorArray[1] * 255.0f), + (unsigned char)(colorArray[2] * 255.0f), + (unsigned char)(colorArray[3] * 255.0f)}); + colorArray = { + color.r / 255.0f, + color.g / 255.0f, + color.b / 255.0f, + color.a / 255.0f + }; + if(GuiButton({4.0f, 268.0f, 234.0f, 16.0f}, "Close")) { + state->close_selected_tri_mode(); + } + } else { state->close_selected_tri_mode(); } - ImGui::End(); + } + } + + inline void make_counter_clockwise(std::array &tri) { + // 2D cross product to get the sign to determine if clockwise + glm::vec2 t_a, t_b; + t_b = tri[1] - tri[0]; + t_a = tri[2] - tri[0]; + + float z = t_b.x * t_a.y - t_b.y * t_a.x; + if(z >= 0.0f) { + // is clockwise, swap first with last + t_a = tri[0]; + tri[0] = tri[2]; + tri[2] = t_a; } } } diff --git a/src/main.cpp b/src/main.cpp index cf20737..62be79f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,10 +1,3 @@ -#include -#include -#include - -#include -#include - #include "state.hpp" #ifdef _MSC_VER diff --git a/src/raygui.cpp b/src/raygui.cpp new file mode 100644 index 0000000..bec96f3 --- /dev/null +++ b/src/raygui.cpp @@ -0,0 +1,2 @@ +#define RAYGUI_IMPLEMENTATION +#include diff --git a/src/shape.cpp b/src/shape.cpp new file mode 100644 index 0000000..d5b37b8 --- /dev/null +++ b/src/shape.cpp @@ -0,0 +1,42 @@ +#include "shape.hpp" + +#include +#include + +Tri::Shape::Shape() : +transform(glm::identity()), +fillColor(RAYWHITE), +outlineColor(BLACK) +{} + +Tri::Shape::Shape(Color fillColor) : +transform(glm::identity()), +fillColor(fillColor), +outlineColor(BLACK) +{} + +Tri::Shape::Shape(Color fillColor, Color outlineColor) : +transform(glm::identity()), +fillColor(fillColor), +outlineColor(outlineColor) +{} + +Tri::Shape& Tri::Shape::resetTransform() { + transform = glm::identity(); + return *this; +} + +Tri::Shape& Tri::Shape::translate(const glm::vec2 move) { + transform = glm::translate(transform, move); + return *this; +} + +Tri::Shape& Tri::Shape::rotate(const float angle) { + transform = glm::rotate(transform, angle); + return *this; +} + +Tri::Shape& Tri::Shape::scale(const glm::vec2 scale) { + transform = glm::scale(transform, scale); + return *this; +} diff --git a/src/shape.hpp b/src/shape.hpp new file mode 100644 index 0000000..090cd2f --- /dev/null +++ b/src/shape.hpp @@ -0,0 +1,37 @@ +#ifndef TRIANGLES_SHAPE_HPP +#define TRIANGLES_SHAPE_HPP + +#include + +#include +#include + +namespace Tri { + +struct Shape { + Shape(); + Shape(Color fillColor); + Shape(Color fillColor, Color outlineColor); + virtual ~Shape() {} + + glm::mat3 transform; + Color fillColor; + Color outlineColor; + + Shape& resetTransform(); + Shape& translate(const glm::vec2 move); + Shape& rotate(const float angle); + Shape& scale(const glm::vec2 scale); + + virtual Shape& draw() = 0; + + virtual void getVertices(std::vector &verticesOut) const = 0; + virtual void getTransformedVertices(std::vector &verticesOut) const = 0; + + virtual float getRadius() const = 0; + virtual void setRadius(float radius) = 0; +}; // struct Shape + +} // namespace Tri + +#endif diff --git a/src/state.cpp b/src/state.cpp index 042fb58..516574f 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include "helpers.hpp" @@ -18,226 +18,245 @@ Tri::State::State(int argc, char **argv) : width(800), height(600), -dt(sf::microseconds(16666)), +dt(1.0f/60.0f), notification_alpha(1.0f), -window(sf::VideoMode(800, 600), "Triangles", sf::Style::Titlebar | sf::Style::Close), trisIndex(0), currentTri_state(CurrentState::NONE), currentTri_maxState(CurrentState::NONE), colorPickerColor{1.0f, 1.0f, 1.0f, 1.0f}, bgColorPickerColor{0.0f, 0.0f, 0.0f}, -bgColor(sf::Color::Black), -inputWidthHeight{800, 600}, -pi(std::acos(-1.0f)) +bgColor(BLACK), +pi(std::acos(-1.0f)), +inputWidth(800), +inputHeight(600) { + InitWindow(width, height, "Triangles"); + SetTargetFPS(60); + flags.set(F_IS_RUNNING); // is running - ImGui::SFML::Init(window); - window.setFramerateLimit(60); set_notification_text("Press \"H\" for help"); pointCircle.setRadius(7.0f); - pointCircle.setOrigin(7.0f, 7.0f); - pointCircle.setFillColor(sf::Color::White); - pointCircle.setOutlineColor(sf::Color::Black); - pointCircle.setOutlineThickness(1.0f); + pointCircle.translate({7.0f, 7.0f}); + pointCircle.fillColor = WHITE; + pointCircle.outlineColor = BLACK; saveFilenameBuffer.fill(0); - if(!drawCache.create(800, 600)) { -#ifndef NDEBUG - puts("ERROR: Failed to initialize RenderTexture (draw cache)"); -#endif - flags.reset(F_DRAW_CACHE_INITIALIZED); - } else { - flags.set(F_DRAW_CACHE_INITIALIZED); - flags.set(F_DRAW_CACHE_DIRTY); - drawCacheSprite.setTexture(drawCache.getTexture(), true); - } + drawCache = LoadRenderTexture(width, height); + flags.set(F_DRAW_CACHE_INITIALIZED); + flags.set(F_DRAW_CACHE_DIRTY); + + GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0x303030); } Tri::State::~State() { - window.close(); - ImGui::SFML::Shutdown(); + UnloadRenderTexture(drawCache); + CloseWindow(); } void Tri::State::handle_events() { - while(window.pollEvent(event)) { - ImGui::SFML::ProcessEvent(event); - if(event.type == sf::Event::Closed) { - window.close(); - flags.reset(F_IS_RUNNING); - } else if(event.type == sf::Event::KeyPressed) { - if(!flags.test(F_DISPLAY_SAVE)) { - // TODO use a switch statement - if(event.key.code == sf::Keyboard::H) { - flags.flip(F_DISPLAY_HELP); - } else if(event.key.code == sf::Keyboard::U) { - flags.set(F_DRAW_CACHE_DIRTY); - if(currentTri_state > 0) { - switch(currentTri_state) { - case FIRST: - currentTri_state = CurrentState::NONE; - break; - case SECOND: - currentTri_state = CurrentState::FIRST; - break; - default: - assert(!"Unreachable code"); - break; - } - } else if(trisIndex > 0) { - --trisIndex; - } - } else if(event.key.code == sf::Keyboard::R) { - flags.set(F_DRAW_CACHE_DIRTY); - if(currentTri_state != CurrentState::NONE - && currentTri_state < currentTri_maxState) { - switch(currentTri_state) { - case NONE: - currentTri_state = CurrentState::FIRST; - break; - case FIRST: - currentTri_state = CurrentState::SECOND; - break; - default: - assert(!"Unreachable code"); - break; - } - } else if(tris.size() > trisIndex) { - ++trisIndex; - } else if(currentTri_state < currentTri_maxState) { - switch(currentTri_state) { - case NONE: - currentTri_state = CurrentState::FIRST; - break; - case FIRST: - currentTri_state = CurrentState::SECOND; - break; - default: - assert(!"Unreachable code"); - break; - } - } - } else if(event.key.code == sf::Keyboard::C) { - if(flags.test(F_DISPLAY_COLOR_P)) { - close_color_picker(); - } else { - flags.set(F_DISPLAY_COLOR_P); - } - } else if(event.key.code == sf::Keyboard::B) { - if(flags.test(F_DISPLAY_BG_COLOR_P)) { - close_bg_color_picker(); - } else { - flags.set(F_DISPLAY_BG_COLOR_P); - } - } else if(event.key.code == sf::Keyboard::S) { - flags.flip(F_DISPLAY_SAVE); - } else if(event.key.code == sf::Keyboard::P) { - flags.flip(F_COPY_COLOR_MODE); - if(flags.test(F_COPY_COLOR_MODE)) { - set_notification_text( - "Copy color mode\n" - "Click to change\n" - "current draw color\n" - "to what was\n" - "clicked on"); - } else { - notification_alpha = 0.0f; - } - } else if(event.key.code == sf::Keyboard::I) { - flags.flip(F_DISPLAY_CHANGE_SIZE); - if(!flags.test(F_DISPLAY_CHANGE_SIZE)) { - inputWidthHeight[0] = width; - inputWidthHeight[1] = height; - } - } else if(event.key.code == sf::Keyboard::E) { - if(flags.test(F_TRI_EDIT_MODE)) { - close_selected_tri_mode(); - } else { - flags.flip(F_SELECT_TRI_MODE); - if(flags.test(F_SELECT_TRI_MODE)) { - set_notification_text("Click on a tri\nto edit it"); - } + if(WindowShouldClose()) { + flags.reset(F_IS_RUNNING); + } + + int keyPressed = GetKeyPressed(); + while(keyPressed > 0) { + if(!flags.test(F_DISPLAY_SAVE)) { + switch(keyPressed) { + case KEY_H: + flags.flip(F_DISPLAY_HELP); + break; + case KEY_U: + flags.set(F_DRAW_CACHE_DIRTY); + if(currentTri_state > 0) { + switch(currentTri_state) { + case FIRST: + currentTri_state = CurrentState::NONE; + break; + case SECOND: + currentTri_state = CurrentState::FIRST; + break; + default: + assert(!"Unreachable code"); + break; } + } else if(trisIndex > 0) { + --trisIndex; } - } - } else if(event.type == sf::Event::MouseButtonPressed) { - if(can_draw()) { - switch(currentTri_state) { - case CurrentState::NONE: - currentTri[0] = sf::Vector2f(event.mouseButton.x, event.mouseButton.y); - if(trisIndex < tris.size()) { - tris.resize(trisIndex); - } - currentTri_state = CurrentState::FIRST; - currentTri_maxState = CurrentState::FIRST; - break; - case CurrentState::FIRST: - currentTri[1] = sf::Vector2f(event.mouseButton.x, event.mouseButton.y); - if(trisIndex < tris.size()) { - tris.resize(trisIndex); - } - currentTri_state = CurrentState::SECOND; - currentTri_maxState = CurrentState::SECOND; - break; - case CurrentState::SECOND: - currentTri[2] = sf::Vector2f(event.mouseButton.x, event.mouseButton.y); - if(trisIndex < tris.size()) { - tris.resize(trisIndex); + break; + case KEY_R: + flags.set(F_DRAW_CACHE_DIRTY); + if(currentTri_state != CurrentState::NONE + && currentTri_state < currentTri_maxState) { + switch(currentTri_state) { + case NONE: + currentTri_state = CurrentState::FIRST; + break; + case FIRST: + currentTri_state = CurrentState::SECOND; + break; + default: + assert(!"Unreachable code"); + break; } + } else if(tris.size() > trisIndex) { ++trisIndex; - tris.emplace_back(sf::ConvexShape(3)); - tris.back().setPoint(0, currentTri[0]); - tris.back().setPoint(1, currentTri[1]); - tris.back().setPoint(2, currentTri[2]); - tris.back().setFillColor(pointCircle.getFillColor()); - currentTri_state = CurrentState::NONE; - currentTri_maxState = CurrentState::NONE; - flags.set(F_DRAW_CACHE_DIRTY); - break; - } - } else if(flags.test(F_COPY_COLOR_MODE)) { - auto color = drawCache.getTexture().copyToImage() - .getPixel(event.mouseButton.x, event.mouseButton.y); - colorPickerColor[0] = color.r / 255.0f; - colorPickerColor[1] = color.g / 255.0f; - colorPickerColor[2] = color.b / 255.0f; - colorPickerColor[3] = 1.0f; - pointCircle.setFillColor(color); - flags.reset(F_COPY_COLOR_MODE); - set_notification_text("Color set"); - } else if(flags.test(F_SELECT_TRI_MODE)) { - sf::Vector2f mouseXY = window.mapPixelToCoords( - {event.mouseButton.x, event.mouseButton.y}); - for(unsigned int i = trisIndex; i-- > 0; ) { - if(is_within_shape(tris.at(i), mouseXY)) { - selectedTri = i; - tris[i].setOutlineColor(invert_color(tris[i].getFillColor())); - flags.reset(F_SELECT_TRI_MODE); - flags.set(F_TRI_EDIT_MODE); - flags.set(F_TRI_EDIT_DRAW_TRI); - selectedTriBlinkTimer = 1.0f; - selectedTriColor[0] = tris[i].getFillColor().r / 255.0f; - selectedTriColor[1] = tris[i].getFillColor().g / 255.0f; - selectedTriColor[2] = tris[i].getFillColor().b / 255.0f; - selectedTriColor[3] = tris[i].getFillColor().a / 255.0f; + } else if(currentTri_state < currentTri_maxState) { + switch(currentTri_state) { + case NONE: + currentTri_state = CurrentState::FIRST; + break; + case FIRST: + currentTri_state = CurrentState::SECOND; + break; + default: + assert(!"Unreachable code"); break; } } - if(!flags.test(F_TRI_EDIT_MODE)) { - set_notification_text("Did not select\nanything"); + break; + case KEY_C: + if(flags.test(F_DISPLAY_COLOR_P)) { + close_color_picker(); + } else { + flags.set(F_DISPLAY_COLOR_P); } + break; + case KEY_B: + if(flags.test(F_DISPLAY_BG_COLOR_P)) { + close_bg_color_picker(); + } else { + flags.set(F_DISPLAY_BG_COLOR_P); + } + break; + case KEY_S: + flags.flip(F_DISPLAY_SAVE); + break; + case KEY_P: + flags.flip(F_COPY_COLOR_MODE); + if(flags.test(F_COPY_COLOR_MODE)) { + set_notification_text( + "Copy color mode\n" + "Click to change\n" + "current draw color\n" + "to what was\n" + "clicked on"); + } else { + notification_alpha = 0.0f; + } + break; + case KEY_I: + flags.flip(F_DISPLAY_CHANGE_SIZE); + if(!flags.test(F_DISPLAY_CHANGE_SIZE)) { + inputWidth = width; + inputHeight = height; + } + break; + case KEY_E: + if(flags.test(F_TRI_EDIT_MODE)) { + close_selected_tri_mode(); + } else { + flags.flip(F_SELECT_TRI_MODE); + if(flags.test(F_SELECT_TRI_MODE)) { + set_notification_text("Click on a tri\nto edit it"); + } + } + break; + } + } + keyPressed = GetKeyPressed(); + } + + if(IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { + if(can_draw()) { + switch(currentTri_state) { + case CurrentState::NONE: + currentTri[0] = {GetMouseX(), GetMouseY()}; + if(trisIndex < tris.size()) { + tris.resize(trisIndex); + } + currentTri_state = CurrentState::FIRST; + currentTri_maxState = CurrentState::FIRST; + break; + case CurrentState::FIRST: + currentTri[1] = {GetMouseX(), GetMouseY()}; + if(trisIndex < tris.size()) { + tris.resize(trisIndex); + } + currentTri_state = CurrentState::SECOND; + currentTri_maxState = CurrentState::SECOND; + break; + case CurrentState::SECOND: + currentTri[2] = {GetMouseX(), GetMouseY()}; + if(trisIndex < tris.size()) { + tris.resize(trisIndex); + } + ++trisIndex; + make_counter_clockwise(currentTri); + tris.emplace_back(currentTri, pointCircle.fillColor); + currentTri_state = CurrentState::NONE; + currentTri_maxState = CurrentState::NONE; + flags.set(F_DRAW_CACHE_DIRTY); + break; + } + } else if(flags.test(F_COPY_COLOR_MODE)) { + check_draw_cache(); + Image drawImage = GetTextureData(drawCache.texture); + Color *colors = LoadImageColors(drawImage); + int mx = GetMouseX(); + int my = GetMouseY(); + if(mx < 0) { mx = 0; } + else if(mx >= (int)width) { mx = width - 1; } + if(my < 0) { my = 0; } + else if(my >= (int)height) { my = height - 1; } + + colorPickerColor[0] = colors[mx + my * width].r; + colorPickerColor[1] = colors[mx + my * width].g; + colorPickerColor[2] = colors[mx + my * width].b; + colorPickerColor[3] = 1.0f; + pointCircle.fillColor = colors[mx + my * width]; + flags.reset(F_COPY_COLOR_MODE); + set_notification_text("Color set"); + + UnloadImageColors(colors); + UnloadImage(drawImage); + } else if(flags.test(F_SELECT_TRI_MODE)) { + int mx = GetMouseX(); + int my = GetMouseY(); + if(mx < 0) { mx = 0; } + else if(mx >= (int)width) { mx = width - 1; } + if(my < 0) { my = 0; } + else if(my >= (int)height) { my = height - 1; } + + for(unsigned int i = trisIndex; i-- > 0; ) { + if(is_within_shape(tris.at(i), {mx, my})) { + selectedTri = i; + tris[i].outlineColor = invert_color(tris[i].fillColor); + flags.reset(F_SELECT_TRI_MODE); + flags.set(F_TRI_EDIT_MODE); + flags.set(F_TRI_EDIT_DRAW_TRI); + selectedTriBlinkTimer = 1.0f; + selectedTriColor[0] = tris[i].fillColor.r / 255.0f; + selectedTriColor[1] = tris[i].fillColor.g / 255.0f; + selectedTriColor[2] = tris[i].fillColor.b / 255.0f; + selectedTriColor[3] = tris[i].fillColor.a / 255.0f; + break; + } + } + if(!flags.test(F_TRI_EDIT_MODE)) { + set_notification_text("Did not select\nanything"); } } } } void Tri::State::update() { - ImGui::SFML::Update(window, dt); + dt = GetFrameTime(); if(notification_alpha > 0.0f) { - notification_alpha -= dt.asSeconds() * STARTING_HELP_FADE_RATE; + notification_alpha -= dt * STARTING_HELP_FADE_RATE; if(notification_alpha < 0.0f) { notification_alpha = 0.0f; } @@ -245,11 +264,11 @@ void Tri::State::update() { if(flags.test(F_COLOR_P_COLOR_DIRTY)) { flags.reset(F_COLOR_P_COLOR_DIRTY); - pointCircle.setFillColor(sf::Color( + pointCircle.fillColor = Color{ (unsigned char)(255 * colorPickerColor[0]), (unsigned char)(255 * colorPickerColor[1]), (unsigned char)(255 * colorPickerColor[2]), - (unsigned char)(255 * colorPickerColor[3]))); + (unsigned char)(255 * colorPickerColor[3])}; } if(flags.test(F_BG_COLOR_P_COLOR_DIRTY)) { @@ -260,14 +279,40 @@ void Tri::State::update() { } if(flags.test(F_TRI_EDIT_MODE)) { - selectedTriBlinkTimer -= dt.asSeconds() * TRIANGLES_EDIT_TRI_BLINK_RATE; + selectedTriBlinkTimer -= dt * TRIANGLES_EDIT_TRI_BLINK_RATE; if(selectedTriBlinkTimer <= 0.0f) { selectedTriBlinkTimer = 1.0f; flags.flip(F_TRI_EDIT_DRAW_TRI); } } +} + +void Tri::State::draw() { + // Should be able to directly draw a texture held by the RenderTexture2D + if(flags.test(F_DRAW_CACHE_INITIALIZED)) { + // draw cache initialized + check_draw_cache(); + BeginDrawing(); + DrawTexture(drawCache.texture, 0, 0, WHITE); + } else { + BeginDrawing(); + draw_to_target(nullptr); + } + + if(flags.test(F_TRI_EDIT_MODE) && flags.test(F_TRI_EDIT_DRAW_TRI)) { +// tris.at(selectedTri).setOutlineThickness(4.0f); + tris[selectedTri].draw(); +// tris.at(selectedTri).setOutlineThickness(0.0f); + } + + if(can_draw()) { + for(unsigned int i = 0; i < currentTri_state; ++i) { + pointCircle.resetTransform(); + pointCircle.translate(currentTri[i]); + pointCircle.draw(); + } + } - // Seems misleading, but imgui handles setting up the window during update Tri::draw_notification(this); Tri::draw_color_picker(this); Tri::draw_bg_color_picker(this); @@ -276,49 +321,28 @@ void Tri::State::update() { Tri::draw_save(this); Tri::draw_help(this); - ImGui::EndFrame(); + EndDrawing(); } -void Tri::State::draw() { - if(flags.test(F_DRAW_CACHE_INITIALIZED)) { - // draw cache initialized - if(flags.test(F_DRAW_CACHE_DIRTY)) { - // draw cache dirty - flags.reset(F_DRAW_CACHE_DIRTY); - draw_to_target(&drawCache); - drawCache.display(); +void Tri::State::draw_to_target(RenderTexture2D *target) { + if(target) { + BeginTextureMode(*target); + ClearBackground(bgColor); + + // draw tris + for(unsigned int i = 0; i < trisIndex; ++i) { + tris[i].draw(); } - window.draw(drawCacheSprite); + EndTextureMode(); } else { - draw_to_target(&window); - } + // Expects BeginDrawing() already having been called prior to this fn + ClearBackground(bgColor); - if(flags.test(F_TRI_EDIT_MODE) && flags.test(F_TRI_EDIT_DRAW_TRI)) { - tris.at(selectedTri).setOutlineThickness(4.0f); - window.draw(tris[selectedTri]); - tris.at(selectedTri).setOutlineThickness(0.0f); - } - - if(can_draw()) { - for(unsigned int i = 0; i < currentTri_state; ++i) { - pointCircle.setPosition(currentTri[i]); - window.draw(pointCircle); + // draw tris + for(unsigned int i = 0; i < trisIndex; ++i) { + tris[i].draw(); } } - - // draw gui stuff - ImGui::SFML::Render(window); - - window.display(); -} - -void Tri::State::draw_to_target(sf::RenderTarget *target) { - target->clear(bgColor); - - // draw tris - for(unsigned int i = 0; i < trisIndex; ++i) { - target->draw(tris[i]); - } } unsigned int Tri::State::get_width() const { @@ -349,12 +373,12 @@ void Tri::State::set_notification_text(const char *text) { notification_alpha = 1.0f; } -float* Tri::State::get_color() { +std::array& Tri::State::get_color() { flags.set(F_COLOR_P_COLOR_DIRTY); return colorPickerColor; } -float* Tri::State::get_bg_color() { +std::array& Tri::State::get_bg_color() { flags.set(F_BG_COLOR_P_COLOR_DIRTY); return bgColorPickerColor; } @@ -364,36 +388,30 @@ Tri::State::FilenameBufferType* Tri::State::get_save_filename_buffer() { } bool Tri::State::do_save() { - sf::RenderTexture saveTexture; - if(!saveTexture.create(width, height)) { -#ifndef NDEBUG - puts("ERROR: Failed to create texture for saving"); -#endif - failedMessage = std::string("Failed to create texture for saving"); - return false; - } + RenderTexture2D saveTexture = LoadRenderTexture(width, height); draw_to_target(&saveTexture); - saveTexture.display(); - sf::Image saveImage = saveTexture.getTexture().copyToImage(); - std::string filename = std::string(saveFilenameBuffer.data()); - if(saveImage.saveToFile(filename)) { + Image saveImage = GetTextureData(saveTexture.texture); + UnloadRenderTexture(saveTexture); + if(ExportImage(saveImage, saveFilenameBuffer.data())) { #ifndef NDEBUG - printf("Saved to \"%s\"\n", filename.c_str()); + printf("Saved to \"%s\"\n", saveFilenameBuffer.data()); #endif failedMessage.clear(); + UnloadImage(saveImage); return true; } else { #ifndef NDEBUG - printf("ERROR: Failed to save \"%s\"\n", filename.c_str()); + printf("ERROR: Failed to save \"%s\"\n", saveFilenameBuffer.data()); #endif failedMessage = std::string("Failed to save (does the name end in \".png\"?)"); + UnloadImage(saveImage); return false; } } -std::string_view Tri::State::failed_message() const { +const std::string& Tri::State::failed_message() const { return failedMessage; } @@ -439,38 +457,35 @@ void Tri::State::close_bg_color_picker() { bool Tri::State::change_width_height() { std::bitset<2> warnings; - if(inputWidthHeight[0] < 0 || inputWidthHeight[1] < 0) { + if(inputWidth < 0 || inputHeight < 0) { failedMessage = "Width or Height cannot be less than 0"; return false; } - if(inputWidthHeight[0] < 200) { - inputWidthHeight[0] = 200; - warnings.set(F_DISPLAY_HELP); + if(inputWidth < 800) { + inputWidth = 800; + warnings.set(0); } - if(inputWidthHeight[1] < 150) { - inputWidthHeight[1] = 150; - warnings.set(F_IS_RUNNING); + if(inputHeight < 600) { + inputHeight = 600; + warnings.set(1); } if(warnings.test(0) && warnings.test(1)) { - set_notification_text("Width set to 200\nHeight set to 150"); + set_notification_text("Width set to 800\nHeight set to 600"); } else if(warnings.test(0)) { - set_notification_text("Width set to 200"); + set_notification_text("Width set to 800"); } else if(warnings.test(1)) { - set_notification_text("Height set to 150"); + set_notification_text("Height set to 600"); } - this->width = inputWidthHeight[0]; - this->height = inputWidthHeight[1]; + this->width = inputWidth; + this->height = inputHeight; - window.setSize({this->width, this->height}); - sf::View newView( - sf::Vector2f(width / 2.0f, height / 2.0f), - sf::Vector2f(width, height)); - window.setView(newView); + SetWindowSize(this->width, this->height); + + UnloadRenderTexture(drawCache); + drawCache = LoadRenderTexture(this->width, this->height); - drawCache.create(width, height); - drawCacheSprite.setTexture(drawCache.getTexture(), true); flags.set(F_DRAW_CACHE_DIRTY); currentTri_state = CurrentState::NONE; @@ -478,10 +493,6 @@ bool Tri::State::change_width_height() { return true; } -int* Tri::State::get_input_width_height() { - return inputWidthHeight; -} - void Tri::State::close_input_width_height_window() { flags.reset(F_DISPLAY_CHANGE_SIZE); } @@ -490,21 +501,40 @@ float Tri::State::get_pi() const { return pi; } -float* Tri::State::get_selected_tri_color() { - tris.at(selectedTri).setFillColor(sf::Color( +std::array& Tri::State::get_selected_tri_color() { + tris.at(selectedTri).fillColor = Color{ (unsigned char)(255.0f * selectedTriColor[0]), (unsigned char)(255.0f * selectedTriColor[1]), (unsigned char)(255.0f * selectedTriColor[2]), - (unsigned char)(255.0f * selectedTriColor[3]))); + (unsigned char)(255.0f * selectedTriColor[3])}; return selectedTriColor; } void Tri::State::close_selected_tri_mode() { - tris.at(selectedTri).setFillColor(sf::Color( + tris.at(selectedTri).fillColor = Color{ (unsigned char)(255.0f * selectedTriColor[0]), (unsigned char)(255.0f * selectedTriColor[1]), (unsigned char)(255.0f * selectedTriColor[2]), - (unsigned char)(255.0f * selectedTriColor[3]))); + (unsigned char)(255.0f * selectedTriColor[3])}; flags.set(F_DRAW_CACHE_DIRTY); reset_modes(); } + +bool Tri::State::check_draw_cache() { + if(flags.test(F_DRAW_CACHE_INITIALIZED) && flags.test(F_DRAW_CACHE_DIRTY)) { + // draw cache initialized and dirty + flags.reset(F_DRAW_CACHE_DIRTY); + draw_to_target(&drawCache); + return true; + } else { + return false; + } +} + +int* Tri::State::get_input_width() { + return &inputWidth; +} + +int* Tri::State::get_input_height() { + return &inputHeight; +} diff --git a/src/state.hpp b/src/state.hpp index 1a3f011..53b3061 100644 --- a/src/state.hpp +++ b/src/state.hpp @@ -7,8 +7,10 @@ #include #include -#include -#include +#include "glm/glm.hpp" + +#include "triangle.hpp" +#include "circle.hpp" namespace Tri { class State { @@ -40,47 +42,44 @@ namespace Tri { BitsetType flags; unsigned int width; unsigned int height; - const sf::Time dt; + float dt; float notification_alpha; - typedef std::array NotificationBufferType; - NotificationBufferType notification_text; + typedef std::array NBufferType; + NBufferType notification_text; - sf::RenderWindow window; - std::vector tris; + std::vector tris; unsigned int trisIndex; - sf::Vector2f currentTri[3]; + std::array currentTri; CurrentState currentTri_state; CurrentState currentTri_maxState; - sf::CircleShape pointCircle; + Circle pointCircle; - sf::Event event; - - float colorPickerColor[4]; - float bgColorPickerColor[3]; - sf::Color bgColor; + std::array colorPickerColor; + std::array bgColorPickerColor; + Color bgColor; typedef std::array FilenameBufferType; FilenameBufferType saveFilenameBuffer; std::string failedMessage; - sf::RenderTexture drawCache; - sf::Sprite drawCacheSprite; - - int inputWidthHeight[2]; + RenderTexture2D drawCache; const float pi; unsigned int selectedTri; - float selectedTriColor[4]; + std::array selectedTriColor; float selectedTriBlinkTimer; + int inputWidth; + int inputHeight; + public: void handle_events(); void update(); void draw(); private: - void draw_to_target(sf::RenderTarget *target); + void draw_to_target(RenderTexture2D *target); public: unsigned int get_width() const; @@ -95,12 +94,12 @@ namespace Tri { void set_notification_text(const char *text); public: - float* get_color(); - float* get_bg_color(); + std::array& get_color(); + std::array& get_bg_color(); FilenameBufferType* get_save_filename_buffer(); bool do_save(); - std::string_view failed_message() const; + const std::string& failed_message() const; void close_save(); private: @@ -113,14 +112,20 @@ namespace Tri { void close_bg_color_picker(); bool change_width_height(); - int* get_input_width_height(); void close_input_width_height_window(); float get_pi() const; - float* get_selected_tri_color(); + std::array& get_selected_tri_color(); void close_selected_tri_mode(); + private: + bool check_draw_cache(); + + public: + int* get_input_width(); + int* get_input_height(); + }; } diff --git a/src/triangle.cpp b/src/triangle.cpp new file mode 100644 index 0000000..b7a6126 --- /dev/null +++ b/src/triangle.cpp @@ -0,0 +1,63 @@ +#include "triangle.hpp" + +Tri::Triangle::Triangle() : +Shape(), +vertices{ + glm::vec2{0.0f, 0.0f}, + glm::vec2{1.0f, 0.0f}, + glm::vec2{0.0f, 1.0f} +} +{} + +Tri::Triangle::Triangle(std::array vertices) : +Shape(), +vertices(vertices) +{} + +Tri::Triangle::Triangle(std::array vertices, Color fillColor) : +Shape(fillColor), +vertices(vertices) +{} + +Tri::Triangle::Triangle(std::array vertices, Color fillColor, Color outlineColor) : +Shape(fillColor, outlineColor), +vertices(vertices) +{} + +Tri::Shape& Tri::Triangle::draw() { + std::array transformed = { + transform * glm::vec3(vertices[0].x, vertices[0].y, 1.0f), + transform * glm::vec3(vertices[1].x, vertices[1].y, 1.0f), + transform * glm::vec3(vertices[2].x, vertices[2].y, 1.0f) + }; + DrawTriangle( + {transformed[0].x, transformed[0].y}, + {transformed[1].x, transformed[1].y}, + {transformed[2].x, transformed[2].y}, + fillColor); + DrawTriangleLines( + {transformed[0].x, transformed[0].y}, + {transformed[1].x, transformed[1].y}, + {transformed[2].x, transformed[2].y}, + outlineColor); + return *this; +} + +void Tri::Triangle::getVertices(std::vector &verticesOut) const { + verticesOut.clear(); + for(const glm::vec2 &vertex : vertices) { + verticesOut.push_back(vertex); + } +} + +void Tri::Triangle::getTransformedVertices(std::vector &verticesOut) const { + verticesOut.clear(); + for(glm::vec2 vertex : vertices) { + vertex = transform * glm::vec3(vertex.x, vertex.y, 1.0f); + verticesOut.push_back(vertex); + } +} + +float Tri::Triangle::getRadius() const { + return 0.0f; +} diff --git a/src/triangle.hpp b/src/triangle.hpp new file mode 100644 index 0000000..5e45c74 --- /dev/null +++ b/src/triangle.hpp @@ -0,0 +1,29 @@ +#ifndef TRIANGLES_TRIANGLE_HPP +#define TRIANGLES_TRIANGLE_HPP + +#include + +#include "shape.hpp" + +namespace Tri { + +struct Triangle : public Shape { + Triangle(); + Triangle(std::array vertices); + Triangle(std::array vertices, Color fillColor); + Triangle(std::array vertices, Color fillColor, Color outlineColor); + + std::array vertices; + + virtual Shape& draw() override; + + virtual void getVertices(std::vector &verticesOut) const override; + virtual void getTransformedVertices(std::vector &verticesOut) const override; + + virtual float getRadius() const override; + virtual void setRadius(float /* radius */) override {}; +}; // struct Triangle + +} // namespace Tri + +#endif diff --git a/src/unittest/test_helpers.cpp b/src/unittest/test_helpers.cpp index f0ff926..a8ca92c 100644 --- a/src/unittest/test_helpers.cpp +++ b/src/unittest/test_helpers.cpp @@ -1,18 +1,17 @@ #include "catch.hpp" -#include - #include "helpers.hpp" +#include "triangle.hpp" TEST_CASE("Test is_within_shape", "[Triangles]") { - sf::ConvexShape shape; - shape.setPointCount(3); - shape.setPoint(0, {0.0f, 10.0f}); - shape.setPoint(1, {10.0f, 10.0f}); - shape.setPoint(2, {10.0f, 0.0f}); + Tri::Triangle triangle({{ + {0.0f, 10.0f}, + {10.0f, 10.0f}, + {10.0f, 0.0f} + }}); - CHECK(Tri::is_within_shape(shape, {2.0f, 2.0f}) == false); - CHECK(Tri::is_within_shape(shape, {5.0f, 15.0f}) == false); - CHECK(Tri::is_within_shape(shape, {15.0f, 5.0f}) == false); - CHECK(Tri::is_within_shape(shape, {7.0f, 7.0f}) == true); + CHECK(Tri::is_within_shape(triangle, {2.0f, 2.0f}) == false); + CHECK(Tri::is_within_shape(triangle, {5.0f, 15.0f}) == false); + CHECK(Tri::is_within_shape(triangle, {15.0f, 5.0f}) == false); + CHECK(Tri::is_within_shape(triangle, {7.0f, 7.0f}) == true); } diff --git a/third_party/glm b/third_party/glm new file mode 160000 index 0000000..ace16e4 --- /dev/null +++ b/third_party/glm @@ -0,0 +1 @@ +Subproject commit ace16e47780dcef815294715237f51e9129b6eb3 diff --git a/third_party/imgui b/third_party/imgui deleted file mode 160000 index 9418dcb..0000000 --- a/third_party/imgui +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9418dcb69355558f70de260483424412c5ca2fce diff --git a/third_party/imgui-sfml b/third_party/imgui-sfml deleted file mode 160000 index 488c321..0000000 --- a/third_party/imgui-sfml +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 488c321155547cb499697dac155aa6269d53c21f diff --git a/third_party/raygui b/third_party/raygui new file mode 160000 index 0000000..6b21662 --- /dev/null +++ b/third_party/raygui @@ -0,0 +1 @@ +Subproject commit 6b216626ec8486b98ec38ae307bfb2cf32f9efcc