Files
ReWalker/main.cpp

221 lines
6.8 KiB
C++

// ReWalker
// A random walker that moves around the screen with a fading trail and mouse interaction.
// Now with real-time adjustable push force using number keys 1-5!
// Written using Redacted ReWindow https://git.redacted.cc/Redacted/ReWindow
// Written by Rich
// With help from (and thanks to) from Maxine, Josh and Bill
#include <ReWindow/types/Window.h>
#include <ReWindow/Logger.h>
#include <JGL/JGL.h>
#include <Color4.hpp>
#include <memory>
// Define window size and dot size (dot size should be odd for symmetry)
#define WIDTH 800
#define HEIGHT 600
#define DOT_SIZE 5
// Define fading speed (higher = faster fading, range: 0.001 - 0.2)
#define FADE_SPEED 0.05f
constexpr unsigned int TRAIL_LENGTH = 20;
// Enable debug mode (set to 0 to disable debug output)
#define DEBUG 0
/**
* @brief Represents the walker that moves randomly on the screen.
*/
struct Walker {
Vector2 position{}; // Current position of the walker
/**
* @brief Constructor initializes the walker at the center of the screen.
*/
Walker() : position(WIDTH / 2.0f, HEIGHT / 2.0f) {}
/**
* @brief Moves the walker one step in a random direction.
*/
void step() {
float angle = static_cast<float>(rand()) / RAND_MAX * 360.0f;
angle = Math::Radians(angle);
float dx = std::cos(angle);
float dy = std::sin(angle);
// Update position and clamp within screen boundaries
position.x = std::clamp(position.x + dx, 0.0f, static_cast<float>(WIDTH - 1));
position.y = std::clamp(position.y + dy, 0.0f, static_cast<float>(HEIGHT - 1));
#if DEBUG
std::cout << "Walker Position: (" << position.x << ", " << position.y << ")\n";
#endif
}
/**
* @brief Pushes the walker away from a given (mouse) position.
* @param mouseX X coordinate of the mouse.
* @param mouseY Y coordinate of the mouse.
* @param force The force applied to push the walker away.
*/
void pushAway(float mouseX, float mouseY, float force) {
float adjustedMouseY = HEIGHT - mouseY; // Adjust for coordinate system
float deltaX = position.x - mouseX;
float deltaY = position.y - adjustedMouseY;
float distance = std::sqrt(deltaX * deltaX + deltaY * deltaY);
// Only push if the mouse is close enough
if (distance > 0.0f) {
float unitX = deltaX / distance;
float unitY = deltaY / distance;
// Apply dynamic push force
position.x = std::clamp(position.x + unitX * force, 0.0f, static_cast<float>(WIDTH - 1));
position.y = std::clamp(position.y + unitY * force, 0.0f, static_cast<float>(HEIGHT - 1));
}
}
/**
* @brief Resets the walker to the center of the screen.
*/
void resetPosition() {
position = Vector2(WIDTH / 2.0f, HEIGHT / 2.0f);
}
};
/**
* @brief Custom ReWindow window class that manages the random walker simulation.
*/
class RandomWalkerWindow : public ReWindow::OpenGLWindow {
public:
/**
* @brief Constructor initializes the window, walker, and trail buffer.
* @param title Window title
* @param width Window width
* @param height Window height
*/
RandomWalkerWindow(const std::string& title, int width, int height)
: ReWindow::OpenGLWindow(title, width, height, 2, 1),
walker(std::make_unique<Walker>()),
trailBuffer{}, // Initialize trail buffer to white (1.0)
pushForce(3.0f) // Default push force
{}
void OnOpen() override {
if (!JGL::Init({WIDTH, HEIGHT}, 0, 0))
exit(0);
this->render_target = new RenderTarget({WIDTH, HEIGHT}, Colors::White);
}
/**
* @brief Called every frame to update and render the walker with a fading trail.
*/
void OnRefresh(float elapsed) override {
// Handle user inputs
handleMouseInput();
handleKeyboardInput();
// Move the walker
walker->step();
JGL::Update({WIDTH, HEIGHT});
J2D::Begin(render_target, true);
// Update trail (fade effect)
fadeTrail();
// Render the updated trail
drawTrail();
// Update the trail buffer with the walkers current position
plotWalker();
J2D::End();
J2D::Begin(nullptr, true);
J2D::DrawRenderTarget(render_target, {0, 0});
J2D::DrawString(Colors::Black, "Framerate: " + std::to_string((int) (1.f / delta_time)), 0, 0, 1, 16);
J2D::End();
// Swap buffers to display the frame
SwapBuffers();
}
private:
std::unique_ptr<Walker> walker;
std::vector<std::pair<Vector2, float>> trailBuffer{}; // Stores grayscale intensity of pixels (1.0 = white, 0.0 = black)
RenderTarget* render_target = nullptr;
float pushForce; // Variable push force that can be adjusted in real time
/**
* @brief Handles mouse input to push the walker away.
*/
void handleMouseInput() {
auto mousePos = GetMouseCoordinates();
if (IsMouseButtonDown(MouseButtons::Left)) {
walker->pushAway(mousePos.x, mousePos.y, pushForce);
}
}
/**
* @brief Handles keyboard input to reset the walker and adjust push force.
*/
void handleKeyboardInput() {
if (IsKeyDown(Keys::Space)) {
walker->resetPosition();
}
if (IsKeyDown(Keys::Escape)) {
Close();
}
// Adjust push force using number keys 1-5
if (IsKeyDown(Keys::One)) pushForce = 1.0f;
if (IsKeyDown(Keys::Two)) pushForce = 2.0f;
if (IsKeyDown(Keys::Three)) pushForce = 3.0f;
if (IsKeyDown(Keys::Four)) pushForce = 4.0f;
if (IsKeyDown(Keys::Five)) pushForce = 5.0f;
}
/**
* @brief Applies the fade effect to the trail buffer.
*/
void fadeTrail() {
// Fade out existing trail points
for (auto& trailPoint : trailBuffer)
trailPoint.second = std::max(trailPoint.second - FADE_SPEED, 0.0f);
trailBuffer.emplace_back(walker->position, 1.0f);
// Limit trail length
if (trailBuffer.size() > TRAIL_LENGTH)
trailBuffer.erase(trailBuffer.begin());
}
void drawTrail() {
for (const auto& trailPoint : trailBuffer)
J2D::DrawPoint({0, 0, 0, (uint8_t) (trailPoint.second * 255.0f)}, trailPoint.first, DOT_SIZE);
}
void plotWalker() {
J2D::DrawPoint(Colors::Black, walker->position, DOT_SIZE);
}
};
int main() {
auto window = std::make_unique<RandomWalkerWindow>("Random Walker with ReWindow", WIDTH, HEIGHT);
if (!window->Open())
return -1;
// Uncomment this to make things go super fast.
//window->SetVsyncEnabled(false);
while (!window->IsClosing()) {
window->ManagedRefresh();
}
return 0;
}