Setup 4 ColorPickers for Mandelbrot set. Will start implementing other fractals soon.
This commit is contained in:
@@ -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)
|
||||
|
||||
|
10
include/FractalProgram.hpp
Normal file
10
include/FractalProgram.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace FractalInspector {
|
||||
class FractalProgram {
|
||||
public:
|
||||
protected:
|
||||
private:
|
||||
};
|
||||
}
|
2
include/FractalPrograms/Mandelbrot.hpp
Normal file
2
include/FractalPrograms/Mandelbrot.hpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#pragma once
|
||||
|
324
main.cpp
324
main.cpp
@@ -1,3 +1,5 @@
|
||||
/// Fractal Inspector Application
|
||||
|
||||
#include <iostream>
|
||||
#include <JGL/logger/logger.h>
|
||||
#include <ReWindow/types/Window.h>
|
||||
@@ -10,6 +12,7 @@
|
||||
#include <Color4.hpp>
|
||||
|
||||
#include "JUI/Widgets/UtilityBar.hpp"
|
||||
#include <JUI/Widgets/ColorPicker.hpp>
|
||||
|
||||
std::vector<std::string> string_expand(const std::string& input, char delimiter = ' ');
|
||||
|
||||
@@ -40,99 +43,6 @@ std::vector<std::string> 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<Color4> 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,80 +117,7 @@ public:
|
||||
console->Log(std::format("{}", args.size()));
|
||||
}
|
||||
|
||||
void CreateMenu() {
|
||||
using namespace JUI::UDimLiterals;
|
||||
|
||||
toolbar = new JUI::UtilityBar(scene);
|
||||
toolbar->AddSubmenu("File"); toolbar->AddSubmenu("Edit");
|
||||
auto* btn_toggle_console = toolbar->AddButton("Console");
|
||||
|
||||
btn_toggle_console->OnClickEvent += [this] (auto a, auto b) mutable {
|
||||
console->Toggle();
|
||||
};
|
||||
|
||||
auto* btn_toggle_info_dialog = toolbar->AddButton("Info");
|
||||
|
||||
// TODO: Implement JUI structure that makes blocks of text easy to impelement.
|
||||
info_dialog = new JUI::Window(scene);
|
||||
info_dialog->SetTitle("FractalInspector - Read Me");
|
||||
|
||||
btn_toggle_info_dialog->OnClickEvent += [this] (auto a, auto b) mutable {
|
||||
info_dialog->Toggle();
|
||||
};
|
||||
|
||||
auto* layout = new JUI::VerticalListLayout(info_dialog->ViewportInstance());
|
||||
layout->Padding(0_px);
|
||||
|
||||
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});
|
||||
content->SetContent(text);
|
||||
content->SetTextSize(size);
|
||||
content->SetTextColor(color);
|
||||
content->BGColor(Colors::Transparent);
|
||||
content->AlignCenterHorizontally();
|
||||
};
|
||||
|
||||
|
||||
// TODO: Is this text useful and relevant, or does it come off as egotistic autofellatio?
|
||||
line_item("FractalInspector V 1.0");
|
||||
line_item("Developed & Maintained by Josh O'Leary");
|
||||
line_item("(c) 2025 Redacted Software - redacted.cc");
|
||||
line_item("This project is part of an independent research project to develop high-performance,", 18);
|
||||
line_item("useful software that stands in contrast to all modern software trends.", 18);
|
||||
line_item("This program is written in C++, from scratch.");
|
||||
|
||||
// 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->Position({50_px, 50_px});
|
||||
//wind->Close();
|
||||
|
||||
ColorPicker* pp = new ColorPicker(wind->ViewportInstance());
|
||||
pp->OnColorValueChanged += [this] (const Color4& color) mutable {
|
||||
u_rgb_1 = color;
|
||||
};
|
||||
|
||||
|
||||
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});
|
||||
|
||||
ColorPicker* pp2 = new ColorPicker(wind2->ViewportInstance());
|
||||
pp2->OnColorValueChanged += [this] (const Color4& color) mutable {
|
||||
u_rgb_2 = 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) {
|
||||
void ParseCmdLineMessage(const std::string& message) {
|
||||
auto tokens = string_expand(message);
|
||||
|
||||
if (tokens.size() == 0) {
|
||||
@@ -288,9 +130,6 @@ public:
|
||||
// Remove 0th element from tokens before passing to command delegates.
|
||||
tokens.erase(tokens.begin());
|
||||
|
||||
|
||||
|
||||
|
||||
if (cmd == "r" || cmd == "reload") {
|
||||
ReloadShader();
|
||||
} else if (cmd == "lf") {
|
||||
@@ -314,7 +153,150 @@ public:
|
||||
} else {
|
||||
console->Log(std::format("No such command: {}", cmd), Colors::Red);
|
||||
}
|
||||
}
|
||||
|
||||
void CreateMenu() {
|
||||
using namespace JUI::UDimLiterals;
|
||||
|
||||
toolbar = new JUI::UtilityBar(scene);
|
||||
toolbar->AddSubmenu("File"); toolbar->AddSubmenu("Edit");
|
||||
auto* btn_toggle_console = toolbar->AddButton("Console");
|
||||
|
||||
btn_toggle_console->OnClickEvent += [this] (auto a, auto b) mutable {
|
||||
console->Toggle();
|
||||
};
|
||||
|
||||
auto* btn_toggle_info_dialog = toolbar->AddButton("Info");
|
||||
|
||||
// TODO: Implement JUI structure that makes blocks of text easy to impelement.
|
||||
info_dialog = new JUI::Window(scene);
|
||||
info_dialog->SetTitle("FractalInspector - Read Me");
|
||||
|
||||
btn_toggle_info_dialog->OnClickEvent += [this] (auto a, auto b) mutable {
|
||||
info_dialog->Toggle();
|
||||
};
|
||||
|
||||
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});
|
||||
content->SetContent(text);
|
||||
content->SetTextSize(size);
|
||||
content->SetTextColor(color);
|
||||
content->BGColor(Colors::Transparent);
|
||||
content->AlignCenterHorizontally();
|
||||
};
|
||||
|
||||
|
||||
// TODO: Is this text useful and relevant, or does it come off as egotistic autofellatio?
|
||||
line_item("FractalInspector V 1.0");
|
||||
line_item("Developed & Maintained by Josh O'Leary");
|
||||
line_item("(c) 2025 Redacted Software - redacted.cc");
|
||||
line_item("This project is part of an independent research project to develop high-performance,", 18);
|
||||
line_item("useful software that stands in contrast to all modern software trends.", 18);
|
||||
line_item("This program is written in C++, from scratch.");
|
||||
|
||||
// TODO: Utilize for things later.
|
||||
auto* wind = new JUI::Window(scene);
|
||||
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();
|
||||
|
||||
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);
|
||||
wind2->SetTitle("Color-Picker B");
|
||||
wind2->MinSize({200, 150});
|
||||
wind2->Size({200_px, 150_px});
|
||||
wind2->Position({50_px, 250_px});*/
|
||||
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
@@ -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.);
|
||||
|
Reference in New Issue
Block a user