feat: Refactor visualization to generate BMP files instead of SVG

Signed-off-by: szdytom <szdytom@qq.com>
This commit is contained in:
方而静 2025-08-01 20:40:36 +08:00
parent 2b5f62da08
commit 4d88ea4a29
Signed by: szTom
GPG Key ID: 072D999D60C6473C
4 changed files with 341 additions and 267 deletions

View File

@ -1,50 +1,39 @@
#include "bmp.h"
#include "generation.h"
#include "tile.h"
#include "tilemap.h"
#include <cstdlib>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>
// Color mapping for different base tile types
const char *get_tile_color(istd::BaseTileType type) {
// Get BMP color for different base tile types
BmpColors::Color get_tile_color(istd::BaseTileType type) {
switch (type) {
case istd::BaseTileType::Land:
return "#90EE90"; // Light green
return BmpColors::LAND;
case istd::BaseTileType::Mountain:
return "#8B4513"; // Saddle brown
return BmpColors::MOUNTAIN;
case istd::BaseTileType::Sand:
return "#F4A460"; // Sandy brown
return BmpColors::SAND;
case istd::BaseTileType::Water:
return "#1E90FF"; // Dodger blue
return BmpColors::WATER;
case istd::BaseTileType::Ice:
return "#B0E0E6"; // Powder blue
return BmpColors::ICE;
default:
return "#808080"; // Gray for unknown types
return BmpColors::Color(128, 128, 128); // Gray for unknown types
}
}
// Generate SVG file from tilemap
void generate_svg(const istd::TileMap &tilemap, const std::string &filename) {
std::ofstream file(filename);
if (!file.is_open()) {
std::cerr << "Error: Could not open output file: " << filename
<< std::endl;
return;
}
// Generate BMP file from tilemap
void generate_bmp(const istd::TileMap &tilemap, const std::string &filename) {
const int chunks_per_side = tilemap.get_size();
const int tiles_per_chunk = istd::Chunk::size;
const int total_tiles = chunks_per_side * tiles_per_chunk;
const int tile_size = 2; // Size of each tile in SVG pixels
const int svg_size = total_tiles * tile_size;
const int tile_size = 2; // Size of each tile in pixels
const int image_size = total_tiles * tile_size;
// SVG header
file << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
file << "<svg width=\"" << svg_size << "\" height=\"" << svg_size
<< "\" xmlns=\"http://www.w3.org/2000/svg\">\n";
file << "<title>Tilemap Visualization</title>\n";
BmpWriter bmp(image_size, image_size);
// Generate tiles
for (int chunk_y = 0; chunk_y < chunks_per_side; ++chunk_y) {
@ -58,64 +47,51 @@ void generate_svg(const istd::TileMap &tilemap, const std::string &filename) {
int global_x = chunk_x * tiles_per_chunk + tile_x;
int global_y = chunk_y * tiles_per_chunk + tile_y;
int svg_x = global_x * tile_size;
int svg_y = global_y * tile_size;
auto color = get_tile_color(tile.base);
const char *color = get_tile_color(tile.base);
file << "<rect x=\"" << svg_x << "\" y=\"" << svg_y
<< "\" width=\"" << tile_size << "\" height=\""
<< tile_size << "\" fill=\"" << color << "\"/>\n";
// Draw a tile_size x tile_size block
for (int dy = 0; dy < tile_size; ++dy) {
for (int dx = 0; dx < tile_size; ++dx) {
int pixel_x = global_x * tile_size + dx;
int pixel_y = global_y * tile_size + dy;
bmp.set_pixel(
pixel_x, pixel_y, color.r, color.g, color.b
);
}
}
}
}
}
}
// Add grid lines for chunk boundaries
file << "<!-- Chunk boundaries -->\n";
// Add chunk boundary lines (optional - makes file larger)
/*
for (int i = 0; i <= chunks_per_side; ++i) {
int pos = i * tiles_per_chunk * tile_size;
// Vertical lines
file << "<line x1=\"" << pos << "\" y1=\"0\" x2=\"" << pos << "\" y2=\""
<< svg_size << "\" stroke=\"black\" stroke-width=\"2\"/>\n";
// Horizontal lines
file << "<line x1=\"0\" y1=\"" << pos << "\" x2=\"" << svg_size
<< "\" y2=\"" << pos
<< "\" stroke=\"black\" stroke-width=\"2\"/>\n";
int pos = i * tiles_per_chunk * tile_size;
// Vertical lines
for (int y = 0; y < image_size; ++y) {
if (pos < image_size) {
bmp.set_pixel(pos, y, 0, 0, 0); // Black
}
}
// Horizontal lines
for (int x = 0; x < image_size; ++x) {
if (pos < image_size) {
bmp.set_pixel(x, pos, 0, 0, 0); // Black
}
}
}
*/
if (!bmp.save(filename)) {
std::cerr << "Error: Could not save BMP file: " << filename
<< std::endl;
return;
}
// Legend
file << "<!-- Legend -->\n";
file << "<g transform=\"translate(10, 10)\">\n";
file << "<rect x=\"0\" y=\"0\" width=\"200\" height=\"140\" fill=\"white\" "
"stroke=\"black\" stroke-width=\"1\" opacity=\"0.9\"/>\n";
file << "<text x=\"10\" y=\"20\" font-family=\"Arial\" font-size=\"14\" "
"font-weight=\"bold\">Legend</text>\n";
const std::pair<istd::BaseTileType, const char *> legend_items[] = {
{istd::BaseTileType::Land, "Land" },
{istd::BaseTileType::Mountain, "Mountain"},
{istd::BaseTileType::Sand, "Sand" },
{istd::BaseTileType::Water, "Water" },
{istd::BaseTileType::Ice, "Ice" }
};
for (int i = 0; i < 5; ++i) {
int y_pos = 40 + i * 20;
file << "<rect x=\"10\" y=\"" << (y_pos - 10)
<< "\" width=\"15\" height=\"15\" fill=\""
<< get_tile_color(legend_items[i].first)
<< "\" stroke=\"black\" stroke-width=\"1\"/>\n";
file << "<text x=\"30\" y=\"" << y_pos
<< "\" font-family=\"Arial\" font-size=\"12\">"
<< legend_items[i].second << "</text>\n";
}
file << "</g>\n";
file << "</svg>\n";
file.close();
std::cout << "SVG file generated: " << filename << std::endl;
std::cout << "BMP file generated: " << filename << std::endl;
std::cout << "Image size: " << image_size << "x" << image_size << " pixels"
<< std::endl;
std::cout << "Tilemap size: " << total_tiles << "x" << total_tiles
<< " tiles" << std::endl;
std::cout << "Chunks: " << chunks_per_side << "x" << chunks_per_side
@ -159,8 +135,8 @@ void print_statistics(const istd::TileMap &tilemap) {
int main(int argc, char *argv[]) {
// Parse command line arguments
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " <seed> <output_file.svg>\n";
std::cerr << "Example: " << argv[0] << " 12345 output.svg\n";
std::cerr << "Usage: " << argv[0] << " <seed> <output_file.bmp>\n";
std::cerr << "Example: " << argv[0] << " 12345 output.bmp\n";
return 1;
}
@ -169,8 +145,8 @@ int main(int argc, char *argv[]) {
// Validate output filename
if (output_filename.length() < 4
|| output_filename.substr(output_filename.length() - 4) != ".svg") {
std::cerr << "Error: Output filename must end with .svg\n";
|| output_filename.substr(output_filename.length() - 4) != ".bmp") {
std::cerr << "Error: Output filename must end with .bmp\n";
return 1;
}
@ -191,9 +167,9 @@ int main(int argc, char *argv[]) {
std::cout << "Generating terrain..." << std::endl;
istd::map_generate(tilemap, config);
// Generate SVG output
std::cout << "Creating SVG visualization..." << std::endl;
generate_svg(tilemap, output_filename);
// Generate BMP output
std::cout << "Creating BMP visualization..." << std::endl;
generate_bmp(tilemap, output_filename);
// Print statistics
print_statistics(tilemap);

239
tilemap/examples/bmp.h Normal file
View File

@ -0,0 +1,239 @@
#ifndef BMP_H
#define BMP_H
#include <cstdint>
#include <fstream>
#include <string>
#include <vector>
/**
* @brief Simple BMP image writer - single header library
*
* Usage:
* BmpWriter bmp(width, height);
* bmp.set_pixel(x, y, r, g, b);
* bmp.save("output.bmp");
*/
class BmpWriter {
private:
int width_;
int height_;
std::vector<std::uint8_t> pixels_;
struct BmpHeader {
// BMP file header (14 bytes)
std::uint8_t signature[2] = {'B', 'M'};
std::uint32_t file_size;
std::uint32_t reserved = 0;
std::uint32_t data_offset = 54; // Header size
// DIB header (40 bytes)
std::uint32_t dib_header_size = 40;
std::int32_t width;
std::int32_t height;
std::uint16_t planes = 1;
std::uint16_t bits_per_pixel = 24;
std::uint32_t compression = 0;
std::uint32_t image_size = 0;
std::int32_t x_pixels_per_meter = 2835; // 72 DPI
std::int32_t y_pixels_per_meter = 2835; // 72 DPI
std::uint32_t colors_used = 0;
std::uint32_t colors_important = 0;
};
public:
/**
* @brief Create a BMP writer with specified dimensions
* @param width Image width in pixels
* @param height Image height in pixels
*/
BmpWriter(int width, int height): width_(width), height_(height) {
pixels_.resize(width * height * 3, 0); // RGB format
}
/**
* @brief Set a pixel color
* @param x X coordinate (0 to width-1)
* @param y Y coordinate (0 to height-1)
* @param r Red component (0-255)
* @param g Green component (0-255)
* @param b Blue component (0-255)
*/
void set_pixel(
int x, int y, std::uint8_t r, std::uint8_t g, std::uint8_t b
) {
if (x >= 0 && x < width_ && y >= 0 && y < height_) {
// BMP stores pixels bottom-to-top
int flipped_y = height_ - 1 - y;
int index = (flipped_y * width_ + x) * 3;
pixels_[index] = b; // Blue
pixels_[index + 1] = g; // Green
pixels_[index + 2] = r; // Red
}
}
/**
* @brief Set a pixel using a grayscale value
* @param x X coordinate
* @param y Y coordinate
* @param gray Grayscale value (0-255)
*/
void set_pixel_gray(int x, int y, std::uint8_t gray) {
set_pixel(x, y, gray, gray, gray);
}
/**
* @brief Set a pixel using a normalized value [0,1]
* @param x X coordinate
* @param y Y coordinate
* @param value Normalized value (0.0 = black, 1.0 = white)
*/
void set_pixel_normalized(int x, int y, double value) {
value = std::max(0.0, std::min(1.0, value)); // Clamp to [0,1]
std::uint8_t gray = static_cast<std::uint8_t>(value * 255);
set_pixel_gray(x, y, gray);
}
/**
* @brief Fill the entire image with a color
* @param r Red component (0-255)
* @param g Green component (0-255)
* @param b Blue component (0-255)
*/
void fill(std::uint8_t r, std::uint8_t g, std::uint8_t b) {
for (int y = 0; y < height_; ++y) {
for (int x = 0; x < width_; ++x) {
set_pixel(x, y, r, g, b);
}
}
}
/**
* @brief Draw a rectangle
* @param x1 Left coordinate
* @param y1 Top coordinate
* @param x2 Right coordinate
* @param y2 Bottom coordinate
* @param r Red component (0-255)
* @param g Green component (0-255)
* @param b Blue component (0-255)
*/
void draw_rect(
int x1, int y1, int x2, int y2, std::uint8_t r, std::uint8_t g,
std::uint8_t b
) {
for (int y = y1; y <= y2; ++y) {
for (int x = x1; x <= x2; ++x) {
set_pixel(x, y, r, g, b);
}
}
}
/**
* @brief Save the image to a BMP file
* @param filename Output filename
* @return true if successful, false otherwise
*/
bool save(const std::string &filename) {
std::ofstream file(filename, std::ios::binary);
if (!file.is_open()) {
return false;
}
// Calculate row padding (BMP rows must be multiple of 4 bytes)
int row_size = width_ * 3;
int padding = (4 - (row_size % 4)) % 4;
int padded_row_size = row_size + padding;
// Prepare header
BmpHeader header;
header.width = width_;
header.height = height_;
header.image_size = padded_row_size * height_;
header.file_size = 54 + header.image_size;
// Write BMP file header
file.write(reinterpret_cast<const char *>(header.signature), 2);
file.write(reinterpret_cast<const char *>(&header.file_size), 4);
file.write(reinterpret_cast<const char *>(&header.reserved), 4);
file.write(reinterpret_cast<const char *>(&header.data_offset), 4);
// Write DIB header
file.write(reinterpret_cast<const char *>(&header.dib_header_size), 4);
file.write(reinterpret_cast<const char *>(&header.width), 4);
file.write(reinterpret_cast<const char *>(&header.height), 4);
file.write(reinterpret_cast<const char *>(&header.planes), 2);
file.write(reinterpret_cast<const char *>(&header.bits_per_pixel), 2);
file.write(reinterpret_cast<const char *>(&header.compression), 4);
file.write(reinterpret_cast<const char *>(&header.image_size), 4);
file.write(
reinterpret_cast<const char *>(&header.x_pixels_per_meter), 4
);
file.write(
reinterpret_cast<const char *>(&header.y_pixels_per_meter), 4
);
file.write(reinterpret_cast<const char *>(&header.colors_used), 4);
file.write(reinterpret_cast<const char *>(&header.colors_important), 4);
// Write pixel data with padding
std::vector<std::uint8_t> padding_bytes(padding, 0);
for (int y = 0; y < height_; ++y) {
// Write row data
file.write(
reinterpret_cast<const char *>(&pixels_[y * width_ * 3]),
row_size
);
// Write padding
if (padding > 0) {
file.write(
reinterpret_cast<const char *>(padding_bytes.data()),
padding
);
}
}
file.close();
return true;
}
/**
* @brief Get image width
*/
int width() const {
return width_;
}
/**
* @brief Get image height
*/
int height() const {
return height_;
}
};
// Predefined colors for convenience
namespace BmpColors {
struct Color {
std::uint8_t r, g, b;
constexpr Color(std::uint8_t red, std::uint8_t green, std::uint8_t blue)
: r(red), g(green), b(blue) {}
};
constexpr Color BLACK(0, 0, 0);
constexpr Color WHITE(255, 255, 255);
constexpr Color RED(255, 0, 0);
constexpr Color GREEN(0, 255, 0);
constexpr Color BLUE(0, 0, 255);
constexpr Color YELLOW(255, 255, 0);
constexpr Color CYAN(0, 255, 255);
constexpr Color MAGENTA(255, 0, 255);
// Tile type colors for terrain visualization
constexpr Color LAND(144, 238, 144); // Light green
constexpr Color MOUNTAIN(139, 69, 19); // Saddle brown
constexpr Color SAND(244, 164, 96); // Sandy brown
constexpr Color WATER(30, 144, 255); // Dodger blue
constexpr Color ICE(176, 224, 230); // Powder blue
} // namespace BmpColors
#endif // BMP_H

View File

@ -1,38 +1,16 @@
#include "bmp.h"
#include "noise.h"
#include <cstdlib>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
// Convert a noise value [0,1] to a grayscale color
std::string noise_to_grayscale(double noise_value) {
// Clamp to [0,1] range
noise_value = std::max(0.0, std::min(1.0, noise_value));
// Convert to 0-255 range
int gray = static_cast<int>(noise_value * 255);
// Convert to hex color
std::ostringstream oss;
oss << "#" << std::hex << std::setfill('0') << std::setw(2) << gray
<< std::setw(2) << gray << std::setw(2) << gray;
return oss.str();
}
// Generate comparison SVG showing both raw and uniform noise side by side
void generate_comparison_svg(
// Generate comparison BMP showing both raw and uniform noise side by side
void generate_comparison_bmp(
const std::string &filename, int size, double scale, std::uint64_t seed,
int octaves = 3, double persistence = 0.5
) {
std::ofstream file(filename);
if (!file.is_open()) {
std::cerr << "Error: Could not open output file: " << filename
<< std::endl;
return;
}
// Create noise generators
istd::PerlinNoise raw_noise(seed);
istd::UniformPerlinNoise uniform_noise(seed);
@ -40,17 +18,18 @@ void generate_comparison_svg(
// Calibrate uniform noise
uniform_noise.calibrate(scale, octaves, persistence);
const int pixel_size = 2;
const int panel_width = size * pixel_size;
const int svg_width = panel_width * 2 + 20; // Space for two panels + gap
const int svg_height = size * pixel_size;
const int panel_width = size;
const int gap = 10;
const int total_width = panel_width * 2 + gap;
// SVG header
file << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
file << "<svg width=\"" << svg_width << "\" height=\"" << svg_height
<< "\" xmlns=\"http://www.w3.org/2000/svg\">\n";
file << "<title>Noise Comparison: Raw vs Uniform (Scale: " << scale
<< ", Octaves: " << octaves << ", Seed: " << seed << ")</title>\n";
BmpWriter bmp(total_width, size);
// Fill gap with white
for (int y = 0; y < size; ++y) {
for (int x = panel_width; x < panel_width + gap; ++x) {
bmp.set_pixel(x, y, 255, 255, 255);
}
}
// Generate histograms for statistics
std::vector<int> raw_histogram(10, 0);
@ -67,19 +46,12 @@ void generate_comparison_svg(
int bin = std::min(9, static_cast<int>(noise_value * 10));
raw_histogram[bin]++;
std::string color = noise_to_grayscale(noise_value);
int svg_x = x * pixel_size;
int svg_y = y * pixel_size;
file << "<rect x=\"" << svg_x << "\" y=\"" << svg_y << "\" width=\""
<< pixel_size << "\" height=\"" << pixel_size << "\" fill=\""
<< color << "\"/>\n";
bmp.set_pixel_normalized(x, y, noise_value);
}
}
// Generate right panel (uniform noise)
int panel_offset = panel_width + 20;
int panel_offset = panel_width + gap;
for (int y = 0; y < size; ++y) {
for (int x = 0; x < size; ++x) {
double noise_value = uniform_noise.uniform_noise(x, y);
@ -88,51 +60,17 @@ void generate_comparison_svg(
int bin = std::min(9, static_cast<int>(noise_value * 10));
uniform_histogram[bin]++;
std::string color = noise_to_grayscale(noise_value);
int svg_x = panel_offset + x * pixel_size;
int svg_y = y * pixel_size;
file << "<rect x=\"" << svg_x << "\" y=\"" << svg_y << "\" width=\""
<< pixel_size << "\" height=\"" << pixel_size << "\" fill=\""
<< color << "\"/>\n";
bmp.set_pixel_normalized(panel_offset + x, y, noise_value);
}
}
// Add labels
file << "<text x=\"" << (panel_width / 2)
<< "\" y=\"20\" font-family=\"Arial\" font-size=\"16\" "
"font-weight=\"bold\" text-anchor=\"middle\" fill=\"white\" "
"stroke=\"black\" stroke-width=\"1\">Raw Perlin Noise</text>\n";
file << "<text x=\"" << (panel_offset + panel_width / 2)
<< "\" y=\"20\" font-family=\"Arial\" font-size=\"16\" "
"font-weight=\"bold\" text-anchor=\"middle\" fill=\"white\" "
"stroke=\"black\" stroke-width=\"1\">Uniform Distribution</text>\n";
if (!bmp.save(filename)) {
std::cerr << "Error: Could not save BMP file: " << filename
<< std::endl;
return;
}
// Add parameter info
file << "<g transform=\"translate(10, " << (svg_height - 120) << ")\">\n";
file << "<rect x=\"0\" y=\"0\" width=\"200\" height=\"100\" fill=\"white\" "
"stroke=\"black\" stroke-width=\"1\" opacity=\"0.9\"/>\n";
file << "<text x=\"10\" y=\"20\" font-family=\"Arial\" font-size=\"12\" "
"font-weight=\"bold\">Parameters</text>\n";
file << "<text x=\"10\" y=\"35\" font-family=\"Arial\" "
"font-size=\"10\">Size: "
<< size << "x" << size << "</text>\n";
file << "<text x=\"10\" y=\"50\" font-family=\"Arial\" "
"font-size=\"10\">Scale: "
<< scale << "</text>\n";
file << "<text x=\"10\" y=\"65\" font-family=\"Arial\" "
"font-size=\"10\">Octaves: "
<< octaves << "</text>\n";
file << "<text x=\"10\" y=\"80\" font-family=\"Arial\" "
"font-size=\"10\">Seed: "
<< seed << "</text>\n";
file << "</g>\n";
file << "</svg>\n";
file.close();
std::cout << "Noise comparison SVG generated: " << filename << std::endl;
std::cout << "Noise comparison BMP generated: " << filename << std::endl;
std::cout << "Size: " << size << "x" << size << " pixels per panel"
<< std::endl;
std::cout << "Parameters: scale=" << scale << ", octaves=" << octaves
@ -154,7 +92,7 @@ void generate_comparison_svg(
int main(int argc, char *argv[]) {
// Default parameters
std::uint64_t seed = 12345;
std::string output_filename = "noise_comparison.svg";
std::string output_filename = "noise_comparison.bmp";
double scale = 0.08;
int octaves = 3;
double persistence = 0.5;
@ -178,8 +116,8 @@ int main(int argc, char *argv[]) {
if (argc == 1 || argc > 6) {
std::cout << "Usage: " << argv[0]
<< " [seed] [output.svg] [scale] [octaves] [persistence]\n";
std::cout << "Defaults: seed=12345, output=noise_comparison.svg, "
<< " [seed] [output.bmp] [scale] [octaves] [persistence]\n";
std::cout << "Defaults: seed=12345, output=noise_comparison.bmp, "
"scale=0.08, octaves=3, persistence=0.5\n";
std::cout << "This will generate a side-by-side comparison of raw vs "
"uniform Perlin noise\n";
@ -195,7 +133,7 @@ int main(int argc, char *argv[]) {
<< std::endl;
// Generate the comparison
generate_comparison_svg(
generate_comparison_bmp(
output_filename, 256, scale, seed, octaves, persistence
);

View File

@ -1,51 +1,21 @@
#include "bmp.h"
#include "noise.h"
#include <cstdlib>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>
// Convert a noise value [0,1] to a grayscale color
std::string noise_to_grayscale(double noise_value) {
// Clamp to [0,1] range
noise_value = std::max(0.0, std::min(1.0, noise_value));
// Convert to 0-255 range
int gray = static_cast<int>(noise_value * 255);
// Convert to hex color
std::ostringstream oss;
oss << "#" << std::hex << std::setfill('0') << std::setw(2) << gray
<< std::setw(2) << gray << std::setw(2) << gray;
return oss.str();
}
// Generate SVG visualization of Perlin noise
void generate_noise_svg(
// Generate BMP file from Perlin noise
void generate_noise_bmp(
const std::string &filename, int size, double scale, std::uint64_t seed,
int octaves = 1, double persistence = 0.5
) {
std::ofstream file(filename);
if (!file.is_open()) {
std::cerr << "Error: Could not open output file: " << filename
<< std::endl;
return;
}
// Create noise generator
istd::PerlinNoise noise(seed);
const int pixel_size = 2; // Size of each pixel in SVG units
const int svg_size = size * pixel_size;
BmpWriter bmp(size, size);
// SVG header
file << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
file << "<svg width=\"" << svg_size << "\" height=\"" << svg_size
<< "\" xmlns=\"http://www.w3.org/2000/svg\">\n";
file << "<title>Perlin Noise Visualization (Scale: " << scale
<< ", Octaves: " << octaves << ", Seed: " << seed << ")</title>\n";
// Generate noise values and create rectangles
// Generate noise values and statistics
double min_value = 1.0, max_value = 0.0;
for (int y = 0; y < size; ++y) {
@ -63,66 +33,17 @@ void generate_noise_svg(
min_value = std::min(min_value, noise_value);
max_value = std::max(max_value, noise_value);
std::string color = noise_to_grayscale(noise_value);
int svg_x = x * pixel_size;
int svg_y = y * pixel_size;
file << "<rect x=\"" << svg_x << "\" y=\"" << svg_y << "\" width=\""
<< pixel_size << "\" height=\"" << pixel_size << "\" fill=\""
<< color << "\"/>\n";
bmp.set_pixel_normalized(x, y, noise_value);
}
}
// Add information text
file << "<!-- Statistics: Min=" << min_value << " Max=" << max_value
<< " -->\n";
// Add parameter info text overlay
file << "<g transform=\"translate(10, 10)\">\n";
file << "<rect x=\"0\" y=\"0\" width=\"300\" height=\"120\" fill=\"white\" "
"stroke=\"black\" stroke-width=\"1\" opacity=\"0.9\"/>\n";
file << "<text x=\"10\" y=\"20\" font-family=\"Arial\" font-size=\"14\" "
"font-weight=\"bold\">Perlin Noise Parameters</text>\n";
file << "<text x=\"10\" y=\"40\" font-family=\"Arial\" "
"font-size=\"12\">Size: "
<< size << "x" << size << "</text>\n";
file << "<text x=\"10\" y=\"55\" font-family=\"Arial\" "
"font-size=\"12\">Scale: "
<< scale << "</text>\n";
file << "<text x=\"10\" y=\"70\" font-family=\"Arial\" "
"font-size=\"12\">Seed: "
<< seed << "</text>\n";
file << "<text x=\"10\" y=\"85\" font-family=\"Arial\" "
"font-size=\"12\">Octaves: "
<< octaves << "</text>\n";
file << "<text x=\"10\" y=\"100\" font-family=\"Arial\" "
"font-size=\"12\">Range: ["
<< std::fixed << std::setprecision(3) << min_value << ", " << max_value
<< "]</text>\n";
file << "</g>\n";
// Add grayscale legend
file << "<g transform=\"translate(" << (svg_size - 60) << ", 10)\">\n";
file << "<text x=\"0\" y=\"15\" font-family=\"Arial\" font-size=\"12\" "
"font-weight=\"bold\">Value</text>\n";
for (int i = 0; i <= 10; ++i) {
double value = i / 10.0;
std::string color = noise_to_grayscale(value);
int y_pos = 20 + i * 15;
file << "<rect x=\"0\" y=\"" << y_pos
<< "\" width=\"20\" height=\"12\" fill=\"" << color
<< "\" stroke=\"black\" stroke-width=\"0.5\"/>\n";
file << "<text x=\"25\" y=\"" << (y_pos + 9)
<< "\" font-family=\"Arial\" font-size=\"10\">" << std::fixed
<< std::setprecision(1) << value << "</text>\n";
if (!bmp.save(filename)) {
std::cerr << "Error: Could not save BMP file: " << filename
<< std::endl;
return;
}
file << "</g>\n";
file << "</svg>\n";
file.close();
std::cout << "Perlin noise SVG generated: " << filename << std::endl;
std::cout << "Perlin noise BMP generated: " << filename << std::endl;
std::cout << "Size: " << size << "x" << size << " pixels" << std::endl;
std::cout << "Scale: " << scale << ", Octaves: " << octaves << std::endl;
std::cout << "Value range: [" << std::fixed << std::setprecision(3)
@ -132,7 +53,7 @@ void generate_noise_svg(
int main(int argc, char *argv[]) {
// Default parameters
std::uint64_t seed = 12345;
std::string output_filename = "perlin_noise.svg";
std::string output_filename = "perlin_noise.bmp";
double scale = 0.02;
int octaves = 1;
double persistence = 0.5;
@ -156,12 +77,12 @@ int main(int argc, char *argv[]) {
if (argc == 1 || argc > 6) {
std::cout << "Usage: " << argv[0]
<< " [seed] [output.svg] [scale] [octaves] [persistence]\n";
std::cout << "Defaults: seed=12345, output=perlin_noise.svg, "
<< " [seed] [output.bmp] [scale] [octaves] [persistence]\n";
std::cout << "Defaults: seed=12345, output=perlin_noise.bmp, "
"scale=0.02, octaves=1, persistence=0.5\n";
std::cout << "Examples:\n";
std::cout << " " << argv[0] << " 54321 noise1.svg 0.01\n";
std::cout << " " << argv[0] << " 12345 octave_noise.svg 0.02 4 0.5\n";
std::cout << " " << argv[0] << " 54321 noise1.bmp 0.01\n";
std::cout << " " << argv[0] << " 12345 octave_noise.bmp 0.02 4 0.5\n";
if (argc > 6) {
return 1;
}
@ -188,7 +109,7 @@ int main(int argc, char *argv[]) {
<< std::endl;
// Generate the noise visualization
generate_noise_svg(output_filename, 256, scale, seed, octaves, persistence);
generate_noise_bmp(output_filename, 256, scale, seed, octaves, persistence);
return 0;
}