From 75509ea25e13c0d2ed14f1be1d3d9c4b93929f4b Mon Sep 17 00:00:00 2001 From: rich Date: Wed, 5 Feb 2025 14:48:41 -0500 Subject: [PATCH] added click mouse to cycle 1d graph to 2d render and then 2d smoothed render --- src/main.cpp | 216 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 150 insertions(+), 66 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index c1c94d2..27ca819 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,10 +6,10 @@ #include #include #include -#include "ReNoise/perlin.hpp" -#include "ReUnirand/unirand.hpp" - +#include "ReNoise/perlin.hpp" // Contains generatePerlinNoise1D, smoothNoise1D, generatePerlinNoise2D, smoothNoise2D +#include "ReUnirand/unirand.hpp" +#include using J3ML::LinearAlgebra::Vector2; #define WIDTH 800 @@ -18,24 +18,27 @@ using J3ML::LinearAlgebra::Vector2; /** * @brief Maps a noise value in [-1, 1] to a y-coordinate on the window. - * +1 is mapped to the top, -1 to the bottom. + * +1 is at the top, -1 at the bottom. */ float mapNoiseToY(float noiseValue) { return HEIGHT - ((noiseValue + 1.0f) / 2.0f * HEIGHT); } /** - * @brief A window class that displays the Perlin noise graphs. + * @brief A window class that displays Perlin noise in three modes. * - * This class generates a raw Perlin noise array and a smoothed version. - * It then plots each sample as a dot and joins consecutive dots with a line, - * so that the resulting graph appears as connected lines. - * Raw noise is drawn in black; smoothed noise in gray. + * Mode 0: 1D graph – raw noise (red) and smoothed noise (blue) drawn as connected dots. + * Mode 1: 2D raw noise image – the noise field is rendered as a grayscale image. + * Mode 2: 2D smoothed noise image – the smoothed noise field is rendered as a grayscale image. + * + * Pressing the space bar or left mouse button cycles through the modes. */ class PerlinNoiseWindow : public ReWindow::OpenGLWindow { public: PerlinNoiseWindow(const std::string &title, int width, int height) - : ReWindow::OpenGLWindow(title, width, height, 2, 1) + : ReWindow::OpenGLWindow(title, width, height, 2, 1), + renderMode(0), toggleLocked(false), + noise2DWidth(256), noise2DHeight(256) { } @@ -46,76 +49,72 @@ public: // Create a render target with a white background. render_target = new RenderTarget({ WIDTH, HEIGHT }, Colors::White); - // Generate the noise arrays. - const int noiseResolution = 128; - ReUnirand::MarsagliaUniRng rng; - try { - rng.rinit(170); - } catch (const std::exception &e) { - ReWindow::Logger::Error("Error initializing RNG: " + std::string(e.what())); - exit(0); + // Generate 1D noise arrays. + const int noiseResolution = 256; + { + ReUnirand::MarsagliaUniRng rng1; + try { + rng1.rinit(170); + } catch (const std::exception &e) { + ReWindow::Logger::Error("Error initializing RNG (1D): " + std::string(e.what())); + exit(0); + } + rawNoise = ReNoise::generatePerlinNoise1D(noiseResolution, rng1); + smoothedNoise1D = ReNoise::smoothNoise1D(rawNoise, 3); + // Precompute drawing points for 1D noise. + rawPoints.clear(); + smoothPoints.clear(); + float xSpacing = static_cast(WIDTH) / (noiseResolution - 1); + for (int i = 0; i < noiseResolution; ++i) { + float x = i * xSpacing; + float yRaw = mapNoiseToY(rawNoise[i]); + float ySmooth = mapNoiseToY(smoothedNoise1D[i]); + rawPoints.push_back(Vector2(x, yRaw)); + smoothPoints.push_back(Vector2(x, ySmooth)); + } } - rawNoise = ReNoise::generatePerlinNoise1D(noiseResolution, rng); - smoothedNoise = ReNoise::smoothNoise(rawNoise, 3); - // Precompute drawing points, evenly spaced along the x-axis. - rawPoints.clear(); - smoothPoints.clear(); - float xSpacing = static_cast(WIDTH) / (noiseResolution - 1); - for (int i = 0; i < noiseResolution; ++i) { - float x = i * xSpacing; - float yRaw = mapNoiseToY(rawNoise[i]); - float ySmooth = mapNoiseToY(smoothedNoise[i]); - rawPoints.push_back(Vector2(x, yRaw)); - smoothPoints.push_back(Vector2(x, ySmooth)); + // Generate 2D noise arrays. + { + ReUnirand::MarsagliaUniRng rng2; + try { + rng2.rinit(170); + } catch (const std::exception &e) { + ReWindow::Logger::Error("Error initializing RNG (2D): " + std::string(e.what())); + exit(0); + } + noise2D = ReNoise::generatePerlinNoise2D(noise2DWidth, noise2DHeight, rng2); + noise2D_smoothed = ReNoise::smoothNoise2D(noise2D, noise2DWidth, noise2DHeight, 3); } } void OnRefresh(float elapsed) override { - - handleKeyboardInput(); - + handleInput(); JGL::Update({ WIDTH, HEIGHT }); - - // Begin drawing to the render target. J2D::Begin(render_target, true); - // (Assuming the "true" parameter clears the target as in your ReWalker code.) - - // Draw raw Perlin noise graph: - // First, join the dots with line segments. - for (size_t i = 0; i < rawPoints.size() - 1; ++i) { - J2D::DrawLine(Colors::Red, rawPoints[i], rawPoints[i + 1], 2.0f); - } - // Then draw the individual dots. - for (const auto &pt : rawPoints) { - J2D::DrawPoint(Colors::Red, pt, DOT_SIZE); - } - - // Draw smoothed noise graph: - // Join the dots with line segments. - for (size_t i = 0; i < smoothPoints.size() - 1; ++i) { - J2D::DrawLine(Colors::Blue, smoothPoints[i], smoothPoints[i + 1], 2.0f); - } - // Then draw the dots. - for (const auto &pt : smoothPoints) { - J2D::DrawPoint(Colors::Blue, pt, DOT_SIZE); + // Render based on the current mode. + switch (renderMode) { + case 0: + render1D(); + break; + case 1: + render2DRaw(); + break; + case 2: + render2DSmoothed(); + break; + default: + render1D(); + break; } J2D::End(); - // Render the target to the window. J2D::Begin(nullptr, true); J2D::DrawRenderTarget(render_target, { 0, 0 }); J2D::End(); SwapBuffers(); - PollEvents(); // Process window events. - } - - void handleKeyboardInput() { - if (IsKeyDown(Keys::Escape)) { - Close(); - } - + PollEvents(); } ~PerlinNoiseWindow() override { @@ -124,14 +123,99 @@ public: private: RenderTarget* render_target = nullptr; + // 1D noise data: std::vector rawNoise; - std::vector smoothedNoise; + std::vector smoothedNoise1D; std::vector rawPoints; std::vector smoothPoints; + // 2D noise data: + std::vector noise2D; + std::vector noise2D_smoothed; + int noise2DWidth, noise2DHeight; + + // Render mode: 0 = 1D graph, 1 = 2D raw, 2 = 2D smoothed. + int renderMode; + bool toggleLocked; + + /** + * @brief Renders the 1D noise graphs as connected dots. + */ + void render1D() { + // Draw raw 1D noise in red. + for (size_t i = 0; i < rawPoints.size() - 1; ++i) { + J2D::DrawLine(Colors::Red, rawPoints[i], rawPoints[i + 1], 2.0f); + } + for (const auto &pt : rawPoints) { + J2D::DrawPoint(Colors::Red, pt, DOT_SIZE); + } + // Draw smoothed 1D noise in blue. + for (size_t i = 0; i < smoothPoints.size() - 1; ++i) { + J2D::DrawLine(Colors::Blue, smoothPoints[i], smoothPoints[i + 1], 2.0f); + } + for (const auto &pt : smoothPoints) { + J2D::DrawPoint(Colors::Blue, pt, DOT_SIZE); + } + } + + /** + * @brief Renders the 2D raw noise field as a grayscale image. + */ + void render2DRaw() { + float cellWidth = static_cast(WIDTH) / noise2DWidth; + float cellHeight = static_cast(HEIGHT) / noise2DHeight; + for (int j = 0; j < noise2DHeight; ++j) { + for (int i = 0; i < noise2DWidth; ++i) { + float value = noise2D[j * noise2DWidth + i]; + unsigned char gray = static_cast(((value + 1.0f) / 2.0f) * 255); + Color4 cellColor(gray, gray, gray, 255); + Vector2 pos(i * cellWidth, j * cellHeight); + Vector2 size(cellWidth, cellHeight); + J2D::FillRect(cellColor, pos, size); + } + } + } + + /** + * @brief Renders the 2D smoothed noise field as a grayscale image. + */ + void render2DSmoothed() { + float cellWidth = static_cast(WIDTH) / noise2DWidth; + float cellHeight = static_cast(HEIGHT) / noise2DHeight; + for (int j = 0; j < noise2DHeight; ++j) { + for (int i = 0; i < noise2DWidth; ++i) { + float value = noise2D_smoothed[j * noise2DWidth + i]; + unsigned char gray = static_cast(((value + 1.0f) / 2.0f) * 255); + Color4 cellColor(gray, gray, gray, 255); + Vector2 pos(i * cellWidth, j * cellHeight); + Vector2 size(cellWidth, cellHeight); + J2D::FillRect(cellColor, pos, size); + } + } + } + + /** + * @brief Handles keyboard and mouse input to cycle through render modes. + * + * Pressing the space bar or left mouse button cycles the renderMode value. + * A simple lock prevents repeated toggling while the key/button is held down. + */ + void handleInput() { + if (IsKeyDown(Keys::Space) || IsMouseButtonDown(MouseButtons::Left)) { + if (!toggleLocked) { + renderMode = (renderMode + 1) % 3; + toggleLocked = true; + } + } else { + toggleLocked = false; + } + if (IsKeyDown(Keys::Escape)) { + Close(); + } + } }; int main() { - auto window = std::make_unique("Perlin Noise Graph", WIDTH, HEIGHT); + auto window = std::make_unique("Perlin Noise Render", WIDTH, HEIGHT); if (!window->Open()) return -1;