diff --git a/CMakeLists.txt b/CMakeLists.txt index 54dddc0..4071804 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,7 @@ CPMAddPackage(NAME JGL URL https://git.redacted.cc/josh/JGL/archive/Prerelease-55.zip) CPMAddPackage(NAME JUI - URL https://git.redacted.cc/josh/ReJUI/archive/Prerelease-5.17.zip) + URL https://git.redacted.cc/josh/ReJUI/archive/Prerelease-6.zip) add_executable(FractalInspector main.cpp) diff --git a/include/FractalProgram.hpp b/include/FractalProgram.hpp new file mode 100644 index 0000000..e147463 --- /dev/null +++ b/include/FractalProgram.hpp @@ -0,0 +1,10 @@ +#pragma once + + +namespace FractalInspector { + class FractalProgram { + public: + protected: + private: + }; +} \ No newline at end of file diff --git a/include/FractalPrograms/Mandelbrot.hpp b/include/FractalPrograms/Mandelbrot.hpp new file mode 100644 index 0000000..3f59c93 --- /dev/null +++ b/include/FractalPrograms/Mandelbrot.hpp @@ -0,0 +1,2 @@ +#pragma once + diff --git a/main.cpp b/main.cpp index 5ace098..efc7247 100644 --- a/main.cpp +++ b/main.cpp @@ -1,3 +1,5 @@ +/// Fractal Inspector Application + #include #include #include @@ -10,6 +12,7 @@ #include #include "JUI/Widgets/UtilityBar.hpp" +#include std::vector string_expand(const std::string& input, char delimiter = ' '); @@ -40,99 +43,6 @@ std::vector string_expand(const std::string& input, char delimiter) using namespace JUI::UDimLiterals; -/// A JUI Widget that displays a HSV color input dialog. -class ColorPicker : public JUI::Rect { -public: - Event OnColorValueChanged; - void SetColorValue(const Color4& color); - Color4 GetColorValue() const; - - JUI::TextRect* hex_label; - - - float hue = 0; - float sat = 1.f; - float bri = 1.f; // AKA val - - ColorPicker() : Rect() { - - Size({100_percent, 100_percent}); - auto* row_layout = new JUI::VerticalListLayout(this); - row_layout->Padding(5_px); - - hue_slider = new JUI::Slider(row_layout); - hue_slider->Size({100_percent, 28_px}); - hue_slider->Minimum(0.f); hue_slider->Maximum(1.f); - hue_slider->Interval(1e-3f); - hue_slider->ValueChanged += [this] (float val) mutable { - hue_label->SetContent(std::format("Hue: {}", Math::FloorInt(val*360))); - - hue = val * 360; - - hue_slider->ScrubberColor(Color4::FromHSV(hue, 1.f, 1.f)); - - OnColorValueChanged.Invoke(Color4::FromHSV(hue, sat, bri)); - }; - - hue_label = new JUI::Text(hue_slider); - hue_label->AlignCenterHorizontally(); - hue_label->AlignTop(); - hue_label->SetTextColor(Colors::Black); - hue_label->SetContent("Hue: 0"); - - sat_slider = new JUI::Slider(row_layout); - sat_slider->Size({100_percent, 28_px}); - sat_slider->Minimum(0); sat_slider->Maximum(1); - sat_slider->Interval(1e-3f); - sat_slider->ValueChanged += [this] (float val) mutable { - sat_label->SetContent(std::format("Saturation: {}%", val*100.f)); - - sat = val; - OnColorValueChanged.Invoke(Color4::FromHSV(hue, sat, bri)); - }; - - sat_label = new JUI::Text(sat_slider); - sat_label->AlignCenterHorizontally(); - sat_label->AlignTop(); - sat_label->SetTextColor(Colors::Black); - sat_label->SetContent("Saturation: 100%"); - - bri_slider = new JUI::Slider(row_layout); - bri_slider->Size({100_percent, 28_px}); - bri_slider->Minimum(0); bri_slider->Maximum(1); - bri_slider->Interval(1e-3f); - bri_slider->ValueChanged += [this] (float val) mutable { - bri_label->SetContent(std::format("Brightness: {}%", val*100.f)); - - bri = val; - - OnColorValueChanged.Invoke(Color4::FromHSV(hue, sat, bri)); - }; - - bri_label = new JUI::Text(bri_slider); - bri_label->AlignCenterHorizontally(); - bri_label->AlignTop(); - bri_label->SetTextColor(Colors::Black); - bri_label->SetContent("Brightness: 100%"); - - - - auto* hue_box = new JUI::Rect(); - } - - explicit ColorPicker(Widget* parent) : ColorPicker() { - Parent(parent); - } - -protected: - JUI::Slider* hue_slider; - JUI::Slider* sat_slider; - JUI::Slider* bri_slider; - JUI::Text* hue_label; - JUI::Text* sat_label; - JUI::Text* bri_label; -private: -}; class ReShaderProgram : public ReWindow::OpenGLWindow { public: @@ -140,6 +50,9 @@ public: JUI::UtilityBar* toolbar; JUI::CommandLine *console; JUI::Window* info_dialog; + + JUI::Window* mandelbrotset_dialog; + JGL::Shader shader; JGL::RenderTarget *canvas; float u_time; @@ -147,6 +60,8 @@ public: Vector2 u_translation {0.4f, 0.f}; Color4 u_rgb_1 = Colors::Greens::Chartreuse; Color4 u_rgb_2 = Colors::Reds::LightCoral; + Color4 u_rgb_3 = Colors::Yellow; + Color4 u_rgb_4 = Colors::Green; std::string frag_name; std::string vert_name; @@ -202,6 +117,44 @@ public: console->Log(std::format("{}", args.size())); } + void ParseCmdLineMessage(const std::string& message) { + auto tokens = string_expand(message); + + if (tokens.size() == 0) { + console->Log("No command input!", Colors::Red); + return; + } + + std::string cmd = tokens[0]; + + // Remove 0th element from tokens before passing to command delegates. + tokens.erase(tokens.begin()); + + if (cmd == "r" || cmd == "reload") { + ReloadShader(); + } else if (cmd == "lf") { + if (tokens.size() > 1) { + std::string fragment = tokens[1]; + // TODO: Check for file in question. + LoadShaderWithDefaultVertex(fragment); + return; + } + console->Log("Error: Command syntax is lf ", Colors::Red); + } else if (cmd == "ls") { + if (tokens.size() > 1) { + std::string shader = tokens[1]; + // TODO: Check for file in question. + LoadShader(shader); + return; + } + console->Log("Error: Command syntax is ls ", Colors::Red); + } else if (cmd == "pos") { + return PositionCmd(tokens); + } else { + console->Log(std::format("No such command: {}", cmd), Colors::Red); + } + } + void CreateMenu() { using namespace JUI::UDimLiterals; @@ -226,6 +179,7 @@ public: auto* layout = new JUI::VerticalListLayout(info_dialog->ViewportInstance()); layout->Padding(0_px); + // TODO: Code like this ends up being a common construct in JUI programs: Make a TextList widget. auto line_item = [&] (const std::string& text, int size = 20, const Color4& color = Colors::White) { auto* content = new JUI::TextRect(layout); content->Size({100_percent, 25_px}); @@ -247,74 +201,102 @@ public: // TODO: Utilize for things later. auto* wind = new JUI::Window(scene); - wind->SetTitle("Color-Picker A"); - wind->MinSize({200, 150}); - wind->Size({200_px, 150_px}); + wind->SetTitle("Color-Pickers"); + wind->MinSize({600, 150}); + wind->Size({600_px, 150_px}); wind->Position({50_px, 50_px}); + + auto* col_layout = new JUI::HorizontalListLayout(wind->Content()); //wind->Close(); - ColorPicker* pp = new ColorPicker(wind->ViewportInstance()); + JUI::ColorPicker* pp = new JUI::ColorPicker(col_layout); + pp->Size({25_percent, 100_percent}); pp->OnColorValueChanged += [this] (const Color4& color) mutable { u_rgb_1 = color; }; - - auto* wind2 = new JUI::Window(scene); + /*auto* wind2 = new JUI::Window(scene); wind2->SetTitle("Color-Picker B"); wind2->MinSize({200, 150}); wind2->Size({200_px, 150_px}); - wind2->Position({50_px, 250_px}); + wind2->Position({50_px, 250_px});*/ - ColorPicker* pp2 = new ColorPicker(wind2->ViewportInstance()); + JUI::ColorPicker* pp2 = new JUI::ColorPicker(col_layout); + pp2->Size({25_percent, 100_percent}); pp2->OnColorValueChanged += [this] (const Color4& color) mutable { u_rgb_2 = color; }; + JUI::ColorPicker* pp3 = new JUI::ColorPicker(col_layout); + pp3->Size({25_percent, 100_percent}); + pp3->OnColorValueChanged += [this] (const Color4& color) mutable { + u_rgb_3 = color; + }; + + JUI::ColorPicker* pp4 = new JUI::ColorPicker(col_layout); + pp4->Size({25_percent, 100_percent}); + pp4->OnColorValueChanged += [this] (const Color4& color) mutable { + u_rgb_3 = color; + }; + + console = new JUI::CommandLine(scene); console->MinSize(Vector2(200, 200)); console->MaxSize(Vector2(GetWidth()-10, GetHeight()-10)); console->Size({100_percent, 200_px}); console->Position({0_percent, JUI::UDim(-200, 1)}); console->OnInput += [this] (const std::string& message) { - auto tokens = string_expand(message); - - if (tokens.size() == 0) { - console->Log("No command input!", Colors::Red); - return; - } - - std::string cmd = tokens[0]; - - // Remove 0th element from tokens before passing to command delegates. - tokens.erase(tokens.begin()); - - - - - if (cmd == "r" || cmd == "reload") { - ReloadShader(); - } else if (cmd == "lf") { - if (tokens.size() > 1) { - std::string fragment = tokens[1]; - // TODO: Check for file in question. - LoadShaderWithDefaultVertex(fragment); - return; - } - console->Log("Error: Command syntax is lf ", Colors::Red); - } else if (cmd == "ls") { - if (tokens.size() > 1) { - std::string shader = tokens[1]; - // TODO: Check for file in question. - LoadShader(shader); - return; - } - console->Log("Error: Command syntax is ls ", Colors::Red); - } else if (cmd == "pos") { - return PositionCmd(tokens); - } else { - console->Log(std::format("No such command: {}", cmd), Colors::Red); - } + ParseCmdLineMessage(message); }; + + + mandelbrotset_dialog = new JUI::Window(scene); + mandelbrotset_dialog->Position({20_px, 300_px}); + + auto* mandelbrotset_layout = new JUI::VerticalListLayout(mandelbrotset_dialog->ViewportInstance()); + mandelbrotset_layout->LayoutOrder(JUI::LayoutOrder::V::BOTTOM); + + + int idx = 0; + + auto mb_line_item = [&] (const std::string& text, int size = 20, const Color4& color = Colors::White) { + + auto* content = new JUI::TextRect(mandelbrotset_layout); + content->Size({100_percent, 25_px}); + content->SetContent(text); + content->SetTextSize(size); + content->SetTextColor(color); + content->BGColor(Colors::Transparent); + content->AlignCenterHorizontally(); + content->LayoutOrder(idx++); + }; + + mb_line_item("The Mandelbrot Set"); + mb_line_item("Imagine a simple rule: "); + mb_line_item("Take a number, square it, and add back the original number."); + + // TODO: Collapsible section: Evaluation of mandelbrot set with z = 0. + mb_line_item("If we start with zero, and keep repeating this rule, we always get zero. Boring, right?"); + + // TODO: Collapsible section: Evaluation of mandelbrot set with z = 1. + mb_line_item("But what if we start with a different number. Let's say 1."); + mb_line_item("1 squared is 1, plus 1 is 2."); + mb_line_item("2 squared is 4, plus 1 is 5."); + mb_line_item("5 squared is 25, plus 1 is 26."); + mb_line_item("The numbers keep getting bigger and bigger -- they 'escape' to infinity."); + + // TODO: Collapsible section: Evaluation of mandelbrot set with z = -1. + mb_line_item("Now, let's try a number like -1."); + mb_line_item("-1 squared is 1, plus -1 is 0."); + mb_line_item("0 squared is 0, plus -1 is -1"); + mb_line_item("-1 squared is 1, plus -1 is 0"); + mb_line_item("The numbers get stuck in a loop between -1 and 0 -- they don't escape"); + + mb_line_item("The Mandelbrot set is a collection of all the starting numbers that don't escape to infinity when you repeat this simple rule."); + mb_line_item("These numbers, when plotted on a complex number plane, create this incredibly intricate and beautiful fractal shape."); + mb_line_item("Color-code the 'escape velocity' of the input numbers that do escape, and you get a result like the one you see."); + + } bool Open() override @@ -423,6 +405,8 @@ public: shader.SetVector2("u_mouse", u_mouse); shader.SetVector3("u_rgb_1", Vector3(u_rgb_1.RN(), u_rgb_1.GN(), u_rgb_1.BN())); shader.SetVector3("u_rgb_2", Vector3(u_rgb_2.RN(), u_rgb_2.GN(), u_rgb_2.BN())); + shader.SetVector3("u_rgb_3", Vector3(u_rgb_3.RN(), u_rgb_3.GN(), u_rgb_3.BN())); + shader.SetVector3("u_rgb_4", Vector3(u_rgb_4.RN(), u_rgb_4.GN(), u_rgb_4.BN())); shader.SetFloat("u_scale", u_scale); shader.SetVector2("u_translation", u_translation); } diff --git a/shaders/mandelbrot.frag.glsl b/shaders/mandelbrot.frag.glsl index 25894a9..506c43f 100644 --- a/shaders/mandelbrot.frag.glsl +++ b/shaders/mandelbrot.frag.glsl @@ -15,6 +15,8 @@ uniform float u_scale; uniform vec3 u_rgb_1; uniform vec3 u_rgb_2; +uniform vec3 u_rgb_3; +uniform vec3 u_rgb_4; #define N 256. // Number of iterations? #define B 4. // What does B mean? @@ -81,7 +83,7 @@ void main() { float n = iterate(uv) / N; - vec3 col = pal(fract(n + 0.5), vec3(.5), u_rgb_1, u_rgb_2, vec3(.0, .1, .2)); + vec3 col = pal(fract(n + 0.5), u_rgb_1, u_rgb_2, u_rgb_3, u_rgb_4); //if (n == 1.) {n = 0; } gl_FragColor = vec4(n == 1. ? vec3(0) : col, 1.);