added click mouse to cycle 1d graph to 2d render and then 2d smoothed render

This commit is contained in:
2025-02-05 14:48:41 -05:00
parent 2c8659629d
commit 75509ea25e

View File

@@ -6,10 +6,10 @@
#include <memory>
#include <vector>
#include <string>
#include "ReNoise/perlin.hpp"
#include "ReUnirand/unirand.hpp"
#include "ReNoise/perlin.hpp" // Contains generatePerlinNoise1D, smoothNoise1D, generatePerlinNoise2D, smoothNoise2D
#include "ReUnirand/unirand.hpp"
#include <J3ML/LinearAlgebra/Vector2.hpp>
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<float>(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<float>(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<float> rawNoise;
std::vector<float> smoothedNoise;
std::vector<float> smoothedNoise1D;
std::vector<Vector2> rawPoints;
std::vector<Vector2> smoothPoints;
// 2D noise data:
std::vector<float> noise2D;
std::vector<float> 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<float>(WIDTH) / noise2DWidth;
float cellHeight = static_cast<float>(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<unsigned char>(((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<float>(WIDTH) / noise2DWidth;
float cellHeight = static_cast<float>(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<unsigned char>(((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<PerlinNoiseWindow>("Perlin Noise Graph", WIDTH, HEIGHT);
auto window = std::make_unique<PerlinNoiseWindow>("Perlin Noise Render", WIDTH, HEIGHT);
if (!window->Open())
return -1;