Added smooth zooming and translation, and zoom in/out based on mouse position.

This commit is contained in:
2025-05-09 13:38:26 -05:00
parent 5900c3cd33
commit e71f12c7c6
3 changed files with 92 additions and 27 deletions

View File

@@ -69,7 +69,9 @@ protected:
JGL::RenderTarget *canvas;
float u_time;
float u_scale = 2.f;
float u_scale_goal = 2.f;
Vector2 u_translation {0.4f, 0.f};
Vector2 u_translation_goal {0.4f, 0.f};
Color4 u_rgb_1 = Colors::Greens::Chartreuse;
Color4 u_rgb_2 = Colors::Reds::LightCoral;
Color4 u_rgb_3 = Colors::Yellow;
@@ -97,6 +99,8 @@ public:
using CmdArgs = std::vector<std::string>;
void PositionCmd(const CmdArgs& args);
void ZoomCmd(const CmdArgs &args);
void ParseCmdLineMessage(const std::string& message);
/// Loads the Julia set program -- consists of a shader program and special widget for parameters.
@@ -122,6 +126,8 @@ public:
void ZoomInTowards(const Vector2 &dir, float rate);
void ZoomOutTowards(const Vector2 &dir, float rate);
void ZoomOut(float rate);
/// Performs a logic update.
@@ -151,11 +157,24 @@ public:
void OnKeyUp(const ReWindow::KeyUpEvent &e) override;
void OnMouseWheel(const ReWindow::MouseWheelEvent &e) override {
// Test of zooming in toward the mouse.
auto mouse_pos_ipair = GetMouseCoordinates();
auto resolution_ipair = GetSize();
auto mouse_pos = Vector2(mouse_pos_ipair.x, mouse_pos_ipair.y);
auto resolution = Vector2(resolution_ipair.x, resolution_ipair.y);
auto mp_norm = mouse_pos / resolution;
mp_norm -= {.5f, .5f};
mp_norm *= 2.f;
mp_norm = {-mp_norm.x, mp_norm.y};
if (e.WheelMovement > 0) {
ZoomOut(0.1);
ZoomOutTowards(mp_norm, 0.1f);
}
if (e.WheelMovement < 0) {
ZoomIn(0.1);
ZoomInTowards(mp_norm, 0.1f);
}
}

View File

@@ -5,6 +5,8 @@
#include <FractalApp.hpp>
// TODO: Parse command-line args.
int main(int argc, char** argv) {
ReWindow::Logger::Debug.EnableConsole(false);

View File

@@ -39,6 +39,8 @@ void FractalInspectorApp::ReloadShader() {
LoadShaders();
}
void FractalInspectorApp::PositionCmd(const CmdArgs &args) {
if (args.empty()) { // Output position + scale
console->Log(std::format("Pos: {}, {}, Zoom: {}", u_translation.x, u_translation.y, u_scale));
@@ -46,15 +48,21 @@ void FractalInspectorApp::PositionCmd(const CmdArgs &args) {
}
if (args.size() >= 2) { // Set Position
u_translation.x = std::stof(args[0]);
u_translation.y = std::stof(args[1]);
u_translation_goal.x = std::stof(args[0]);
u_translation_goal.y = std::stof(args[1]);
if (args.size() >= 3) { // Also set scale
u_scale = std::stof(args[2]);
u_scale_goal = std::stof(args[2]);
}
}
console->Log(std::format("{}", args.size()));
}
void FractalInspectorApp::ZoomCmd(const CmdArgs& args) {
if (args.empty()) {
console->Log(std::format("Zoom: {}", u_scale)); return;
}
}
void FractalInspectorApp::ParseCmdLineMessage(const std::string &message) {
auto tokens = misc::string_expand(message);
@@ -68,13 +76,23 @@ void FractalInspectorApp::ParseCmdLineMessage(const std::string &message) {
// Remove 0th element from tokens before passing to command delegates.
tokens.erase(tokens.begin());
if (cmd == "r" || cmd == "reload") {
ReloadShader();
} else if (cmd == "pos") {
return PositionCmd(tokens);
} else {
console->Log(std::format("No such command: {}", cmd), Colors::Red);
if (misc::string_matches(cmd, {"r", "reload", "refresh"})) {
return ReloadShader();
}
if (misc::string_matches(cmd, {"zoom"})) {
return ZoomCmd(tokens);
}
if (misc::string_matches(cmd, {"pos", "position", "tp"})) {
return PositionCmd(tokens);
}
if (misc::string_matches(cmd, {"reset"})) {
}
console->Log(std::format("No such command: {}", cmd), Colors::Red);
}
void FractalInspectorApp::LoadJulia() {
@@ -352,7 +370,6 @@ bool FractalInspectorApp::Open() {
JGL::Update(vec_size);
glClearColor(0.f, 0.f, 0.f, 0.f);
glEnable(GL_DEPTH_TEST);
@@ -364,7 +381,11 @@ bool FractalInspectorApp::Open() {
scene = new JUI::Scene();
CreateMenu();
canvas = new RenderTarget(vec_size);
SampleRate canvas_samples = SampleRate::X16;
FilteringMode canvas_filtering = FilteringMode::MIPMAP_NEAREST;
canvas = new RenderTarget(vec_size, Colors::Transparent, false, canvas_samples, canvas_filtering);
LoadShaders();
console->Log(std::format("OpenGL Renderer: {}", GetGLRenderer()));
@@ -409,28 +430,46 @@ void FractalInspectorApp::ZoomIn(float rate) {
rate = rate * u_scale;
u_scale -= rate;
u_scale_goal -= rate;
// Stupid hack to make zoom originate at the center of the screen.
u_translation -= Vector2(0.5f, 0.5f)*rate;
u_translation_goal -= Vector2(0.5f, 0.5f)*rate;
}
void FractalInspectorApp::ZoomInTowards(const Vector2& dir, float rate) {}
void FractalInspectorApp::ZoomInTowards(const Vector2& dir, float rate) {
rate = rate * u_scale;
u_scale_goal -= rate;
u_translation_goal -= Vector2(0.5f, 0.5f)*rate;
u_translation_goal += dir*rate;
}
void FractalInspectorApp::ZoomOutTowards(const Vector2& dir, float rate) {
rate = rate * u_scale;
u_scale_goal += rate;
// Stupid hack to make zoom originate at the center of the screen.
u_translation_goal += Vector2(0.5f, 0.5f)*rate;
u_translation_goal -= dir*rate;
}
void FractalInspectorApp::ZoomOut(float rate) {
rate = rate * u_scale;
u_scale += rate;
u_scale_goal += rate;
// Stupid hack to make zoom originate at the center of the screen.
u_translation += Vector2(0.5f, 0.5f)*rate;
u_translation_goal += Vector2(0.5f, 0.5f)*rate;
}
void FractalInspectorApp::Update(float elapsed) {
scene->Update(elapsed);
if (IsKeyDown(Keys::F6)) {
u_scale = 2.f;
u_translation = {.4, 0};
u_scale_goal = 2.f;
u_translation_goal = {.4, 0};
}
// TODO: Vary the rate appropriately.
@@ -450,19 +489,25 @@ void FractalInspectorApp::Update(float elapsed) {
if (IsKeyDown(Keys::DownArrow))
u_translation.y += rate;
u_translation_goal.y += rate;
if (IsKeyDown(Keys::UpArrow))
u_translation.y -= rate;
u_translation_goal.y -= rate;
if (IsKeyDown(Keys::LeftArrow))
u_translation.x += rate;
u_translation_goal.x += rate;
if (IsKeyDown(Keys::RightArrow))
u_translation.x -= rate;
u_translation_goal.x -= rate;
u_time += elapsed;
u_julia_value = Vector2::Lerp(u_julia_value, u_julia_set, 0.001f);
float lerp_rate = 0.1f;
float julia_lerp_rate = 0.01f;
u_translation = Vector2::Lerp(u_translation, u_translation_goal, lerp_rate);
u_scale = Math::Lerp(u_scale, u_scale_goal, lerp_rate);
u_julia_value = Vector2::Lerp(u_julia_value, u_julia_set, julia_lerp_rate);
UpdateShaderUniforms(elapsed);
@@ -576,7 +621,6 @@ void FractalInspectorApp::OnMouseButtonDown(const ReWindow::MouseButtonDownEvent
void FractalInspectorApp::OnMouseButtonUp(const ReWindow::MouseButtonUpEvent &e) {
auto btn = ToJUIEnum(e.Button);
if (scene->ObserveMouseInput(btn, false))
@@ -612,7 +656,7 @@ void FractalInspectorApp::OnMouseMove(const ReWindow::MouseMoveEvent &e) {
if (panning)
u_translation += pan_direction*pan_rate;
u_translation_goal += pan_direction*pan_rate;
last = nmp;
}