From 89596ee2beb94923515ac3cc19347f26961a27b1 Mon Sep 17 00:00:00 2001 From: Yggdrasil75 Date: Mon, 24 Nov 2025 15:00:23 -0500 Subject: [PATCH] pushing this. --- makefile | 2 +- tests/g2temp.cpp | 557 ++++++++++++++++++++++++++++++++++++++ util/grid/grid2.hpp | 154 ++++++++++- util/output/bmpwriter.hpp | 33 ++- util/simblocks/temp.hpp | 37 +++ util/vectorlogic/vec2.hpp | 8 +- 6 files changed, 770 insertions(+), 21 deletions(-) create mode 100644 tests/g2temp.cpp create mode 100644 util/simblocks/temp.hpp diff --git a/makefile b/makefile index b396943..eb98198 100644 --- a/makefile +++ b/makefile @@ -17,7 +17,7 @@ PKG_FLAGS := $(LINUX_GL_LIBS) `pkg-config --static --cflags --libs glfw3` CXXFLAGS += $(PKG_FLAGS) # Source files -SRC := $(SRC_DIR)/g2chromatic2.cpp +SRC := $(SRC_DIR)/g2temp.cpp SRC += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp SRC += $(IMGUI_DIR)/backends/imgui_impl_glfw.cpp $(IMGUI_DIR)/backends/imgui_impl_opengl3.cpp SRC += $(SRC_DIR)/stb_image.cpp diff --git a/tests/g2temp.cpp b/tests/g2temp.cpp new file mode 100644 index 0000000..c52c17d --- /dev/null +++ b/tests/g2temp.cpp @@ -0,0 +1,557 @@ +#include +#include +#include +#include +#include +#include +#include +#include "../util/grid/grid2.hpp" +#include "../util/output/aviwriter.hpp" +#include "../util/output/bmpwriter.hpp" +#include "../util/timing_decorator.cpp" + +#include "../imgui/imgui.h" +#include "../imgui/backends/imgui_impl_glfw.h" +#include "../imgui/backends/imgui_impl_opengl3.h" +#include +#include "../stb/stb_image.h" + +#include +#include +#include +#include +#include + +#ifndef M_PI +#define M_PI = 3.1415 +#endif + +std::mutex m; +std::atomic isGenerating{false}; +std::future generationFuture; + +std::mutex previewMutex; +std::atomic updatePreview{false}; +frame currentPreviewFrame; +GLuint textu = 0; +std::string previewText; + +struct Shared { + std::mutex mutex; + Grid2 grid; + bool hasNewFrame = false; + int currentFrame = 0; +}; + +struct AnimationConfig { + int width = 1024; + int height = 1024; + int totalFrames = 480; + float fps = 30.0f; + int numSeeds = 8; + int noisemod = 42; +}; + +Grid2 setup(AnimationConfig config) { + TIME_FUNCTION; + Grid2 grid; + std::vector pos; + std::vector colors; + std::vector sizes; + for (int y = 0; y < config.height; ++y) { + for (int x = 0; x < config.width; ++x) { + float gradient = (x + y) / float(config.width + config.height - 2); + pos.push_back(Vec2(x,y)); + colors.push_back(Vec4(gradient, gradient, gradient, 1.0f)); + sizes.push_back(1.0f); + } + } + grid.bulkAddObjects(pos,colors,sizes); + return grid; +} + +void Preview(Grid2& grid) { + TIME_FUNCTION; + int width; + int height; + //std::vector rgbData; + + frame rgbData = grid.getGridAsFrame(frame::colormap::RGB); + std::cout << "Frame looks like: " << rgbData << std::endl; + bool success = BMPWriter::saveBMP("output/grayscalesource.bmp", rgbData); + if (!success) { + std::cout << "yo! this failed in Preview" << std::endl; + } +} + +void livePreview(Grid2& grid) { + // std::lock_guard lock(previewMutex); + + // //currentPreviewFrame = grid.getGridAsFrame(frame::colormap::RGBA); + // Vec2 min; + // Vec2 max; + // grid.getBoundingBox(min, max); + // currentPreviewFrame = grid.getTempAsFrame(min,max, Vec2(1024,1024)); + + // glGenTextures(1, &textu); + // glBindTexture(GL_TEXTURE_2D, textu); + // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + // glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + // glBindTexture(GL_TEXTURE_2D, textu); + // glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, currentPreviewFrame.getWidth(), currentPreviewFrame.getHeight(), + // 0, GL_RGBA, GL_UNSIGNED_BYTE, currentPreviewFrame.getData().data()); + + // updatePreview = true; +} + +std::vector> pickSeeds(Grid2 grid, AnimationConfig config) { + TIME_FUNCTION; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> xDist(0, config.width - 1); + std::uniform_int_distribution<> yDist(0, config.height - 1); + std::uniform_real_distribution<> colorDist(0.2f, 0.8f); + + std::vector> seeds; + + for (int i = 0; i < config.numSeeds; ++i) { + Vec2 point(xDist(gen), yDist(gen)); + Vec4 color(colorDist(gen), colorDist(gen), colorDist(gen), 255); + size_t id = grid.getOrCreatePositionVec(point, 0.0, true); + grid.setColor(id, color); + seeds.push_back(std::make_tuple(id,point, color)); + } + return seeds; +} + +void pickTempSeeds(Grid2& grid, AnimationConfig config) { + std::cout << "pickTempSeeds()" << std::endl; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> xDist(0, config.width - 1); + std::uniform_int_distribution<> yDist(0, config.height - 1); + std::uniform_real_distribution<> temp(0.0f, 100.0f); + + for (int i = 0; i < config.numSeeds; ++i){ + grid.setTemp(Vec2(xDist(gen),yDist(gen)).floor(), temp(gen)); + } +} + +void expandPixel(Grid2& grid, AnimationConfig config, std::vector>& seeds) { + TIME_FUNCTION; + std::vector> newseeds; + + std::unordered_set visitedThisFrame; + for (const auto& seed : seeds) { + visitedThisFrame.insert(std::get<0>(seed)); + } + + //#pragma omp parallel for + for (const std::tuple& seed : seeds) { + size_t id = std::get<0>(seed); + Vec2 seedPOS = std::get<1>(seed); + Vec4 seedColor = std::get<2>(seed); + std::vector neighbors = grid.getNeighbors(id); + //grid.setSize(id, grid.getSize(id)+4); + for (size_t neighbor : neighbors) { + if (visitedThisFrame.count(neighbor)) { + continue; + } + visitedThisFrame.insert(neighbor); + + Vec2 neipos = grid.getPositionID(neighbor); + Vec4 neighborColor = grid.getColor(neighbor); + float distance = seedPOS.distance(neipos); + float angle = seedPOS.directionTo(neipos); + + float normalizedAngle = (angle + M_PI) / (2.0f * M_PI); + float blendFactor = 0.3f + 0.4f * std::sin(normalizedAngle * 2.0f * M_PI); + blendFactor = std::clamp(blendFactor, 0.1f, 0.9f); + + Vec4 newcolor = Vec4( + seedColor.r * blendFactor + neighborColor.r * (1.0f - blendFactor), + seedColor.g * (1.0f - blendFactor) + neighborColor.g * blendFactor, + seedColor.b * (0.5f + 0.5f * std::sin(normalizedAngle * 4.0f * M_PI)), + 1.0f + ); + + newcolor = newcolor.clamp(0.0f, 1.0f); + + grid.setColor(neighbor, newcolor); + newseeds.emplace_back(neighbor, neipos, newcolor); + } + } + seeds.clear(); + seeds.shrink_to_fit(); + seeds = std::move(newseeds); +} + +//bool exportavi(std::vector> frames, AnimationConfig config) { +bool exportavi(std::vector frames, AnimationConfig config) { + TIME_FUNCTION; + std::string filename = "output/chromatic_transformation.avi"; + + std::cout << "Frame count: " << frames.size() << std::endl; + + // Log compression statistics for all frames + std::cout << "\n=== Frame Compression Statistics ===" << std::endl; + size_t totalOriginalSize = 0; + size_t totalCompressedSize = 0; + + for (int i = 0; i < frames.size(); ++i) { + totalOriginalSize += frames[i].getSourceSize(); + totalCompressedSize += frames[i].getTotalCompressedSize(); + } + + double overallRatio = static_cast(totalOriginalSize) / totalCompressedSize; + double overallSavings = (1.0 - 1.0/overallRatio) * 100.0; + + std::cout << "\n=== Overall Compression Summary ===" << std::endl; + std::cout << "Total frames: " << frames.size() << std::endl; + std::cout << "Compressed frames: " << frames.size() << std::endl; + std::cout << "Total original size: " << totalOriginalSize << " bytes (" + << std::fixed << std::setprecision(2) << (totalOriginalSize / (1024.0 * 1024.0)) << " MB)" << std::endl; + std::cout << "Total compressed size: " << totalCompressedSize << " bytes (" + << std::fixed << std::setprecision(2) << (totalCompressedSize / (1024.0 * 1024.0)) << " MB)" << std::endl; + std::cout << "Overall compression ratio: " << std::fixed << std::setprecision(2) << overallRatio << ":1" << std::endl; + std::cout << "Overall space savings: " << std::fixed << std::setprecision(1) << overallSavings << "%" << std::endl; + + std::filesystem::path dir = "output"; + if (!std::filesystem::exists(dir)) { + if (!std::filesystem::create_directories(dir)) { + std::cout << "Failed to create output directory!" << std::endl; + return false; + } + } + + bool success = AVIWriter::saveAVIFromCompressedFrames(filename, frames, frames[0].getWidth(), frames[0].getHeight(), config.fps); + + if (success) { + // Check if file actually exists + if (std::filesystem::exists(filename)) { + auto file_size = std::filesystem::file_size(filename); + std::cout << "\nAVI file created successfully: " << filename + << " (" << file_size << " bytes, " + << std::fixed << std::setprecision(2) << (file_size / (1024.0 * 1024.0)) << " MB)" << std::endl; + } + } else { + std::cout << "Failed to save AVI file!" << std::endl; + } + + return success; +} + +void mainLogic(const AnimationConfig& config, Shared& state, int gradnoise) { + TIME_FUNCTION; + if (isGenerating) return; //apparently sometimes this function is called twice. dont know how, but this might resolve that. + isGenerating = true; + try { + Grid2 grid; + if (gradnoise == 0) { + grid = setup(config); + } else if (gradnoise == 1) { + grid = grid.noiseGenGrid(0,0,config.height, config.width, 0.01, 1.0, false, config.noisemod); + } + grid.setDefault(Vec4(0,0,0,0)); + { + std:: lock_guard lock(state.mutex); + state.grid = grid; + state.hasNewFrame = true; + state.currentFrame = 0; + } + pickTempSeeds(grid,config); + + //std::vector> seeds = pickSeeds(grid, config); + std::cout << "generated grid" << std::endl; + Preview(grid); + std::cout << "generated preview" << std::endl; + frame tempData = grid.getTempAsFrame(Vec2(0,0), Vec2(config.height,config.width), Vec2(256,256)); + std::cout << "Temp frame looks like: " << tempData << std::endl; + bool success = BMPWriter::saveBMP("output/temperature.bmp", tempData); + if (!success) { + std::cout << "yo! this failed in Preview" << std::endl; + } + + std::vector frames; + + // for (int i = 0; i < config.totalFrames; ++i){ + // // Check if we should stop the generation + // if (!isGenerating) { + // std::cout << "Generation cancelled at frame " << i << std::endl; + // return; + // } + + // //expandPixel(grid,config,seeds); + + // std::lock_guard lock(state.mutex); + // state.grid = grid; + // state.hasNewFrame = true; + // state.currentFrame = i; + + // // Print compression info for this frame + // if (i % 10 == 0 ) { + // frame bgrframe; + // std::cout << "Processing frame " << i + 1 << "/" << config.totalFrames << std::endl; + // bgrframe = grid.getGridAsFrame(frame::colormap::BGR); + // frames.push_back(bgrframe); + // //bgrframe.decompress(); + // //BMPWriter::saveBMP(std::format("output/grayscalesource.{}.bmp", i), bgrframe); + // bgrframe.compressFrameLZ78(); + // //bgrframe.printCompressionStats(); + // } + // } + // exportavi(frames,config); + } + catch (const std::exception& e) { + std::cerr << "errored at: " << e.what() << std::endl; + } + isGenerating = false; +} + +// Function to cancel ongoing generation +void cancelGeneration() { + if (isGenerating) { + isGenerating = false; + // Wait for the thread to finish (with timeout to avoid hanging) + if (generationFuture.valid()) { + auto status = generationFuture.wait_for(std::chrono::milliseconds(100)); + if (status != std::future_status::ready) { + std::cout << "Waiting for generation thread to finish..." << std::endl; + } + } + } +} + +static void glfw_error_callback(int error, const char* description) +{ + fprintf(stderr, "GLFW Error %d: %s\n", error, description); +} + +int main() { + //static bool window = true; + glfwSetErrorCallback(glfw_error_callback); + if (!glfwInit()) { + std::cerr << "gui stuff is dumb in c++." << std::endl; + glfwTerminate(); + return 1; + } + // COPIED VERBATIM FROM IMGUI. + #if defined(IMGUI_IMPL_OPENGL_ES2) + // GL ES 2.0 + GLSL 100 (WebGL 1.0) + const char* glsl_version = "#version 100"; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + #elif defined(IMGUI_IMPL_OPENGL_ES3) + // GL ES 3.0 + GLSL 300 es (WebGL 2.0) + const char* glsl_version = "#version 300 es"; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + #elif defined(__APPLE__) + // GL 3.2 + GLSL 150 + const char* glsl_version = "#version 150"; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac + #else + // GL 3.0 + GLSL 130 + const char* glsl_version = "#version 130"; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only + //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only + #endif + //ImGui::SetNextWindowSize(ImVec2(1110,667)); + //auto beg = ImGui::Begin("Gradient thing", &window); + //if (beg) { + // std::cout << "stuff breaks at 223" << std::endl; + bool application_not_closed = true; + //float main_scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor()); + GLFWwindow* window = glfwCreateWindow((int)(1280), (int)(800), "Chromatic gradient generator thing", nullptr, nullptr); + if (window == nullptr) + return 1; + glfwMakeContextCurrent(window); + glfwSwapInterval(1); + //IMGUI_CHECKVERSION(); //this might be more important than I realize. but cant run with it so currently ignoring. + ImGui::CreateContext(); + // std::cout << "context created" << std::endl; + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + ImGui::StyleColorsDark(); + ImGuiStyle& style = ImGui::GetStyle(); + //style.ScaleAllSizes(1); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + //style.FontScaleDpi = 1; //will need to implement my own scaling at some point. currently just ignoring it. + ImGui_ImplGlfw_InitForOpenGL(window, true); + + + #ifdef __EMSCRIPTEN__ + ImGui_ImplGlfw_InstallEmscriptenCallbacks(window, "#canvas"); + #endif + ImGui_ImplOpenGL3_Init(glsl_version); + + + // std::cout << "created glfw window" << std::endl; + + + bool show_demo_window = true; + bool show_another_window = false; + ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + static float f = 30.0f; + static int i1 = 1024; + static int i2 = 1024; + static int i3 = 480; + static int i4 = 8; + static int noisemod = 42; + static float fs = 1.0; + int gradnoise = 0; + + std::future mainlogicthread; + Shared state; + Grid2 grid; + AnimationConfig config; + previewText = "Please generate"; + mainlogicthread = std::async(std::launch::async, mainLogic, config, std::ref(state), gradnoise); + while (!glfwWindowShouldClose(window)) { + glfwPollEvents(); + + // Start the Dear ImGui frame + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + { + + ImGui::Begin("settings"); + + ImGui::SliderFloat("fps", &f, 20.0f, 60.0f); + ImGui::SliderInt("width", &i1, 256, 4096); + ImGui::SliderInt("height", &i2, 256, 4096); + ImGui::SliderInt("frame count", &i3, 10, 5000); + ImGui::SliderInt("number of Seeds", &i4, 0, 10); + ImGui::SliderInt("Noise Mod", &noisemod, 0, 1000); + ImGui::SliderFloat("Scale Preview", &fs, 0.0, 2.0); + ImGui::RadioButton("Gradient", &gradnoise, 0); + ImGui::RadioButton("Perlin Noise", &gradnoise, 1); + + if (isGenerating) { + ImGui::BeginDisabled(); + } + + if (ImGui::Button("Generate Animation")) { + config = AnimationConfig(i1, i2, i3, f, i4, noisemod); + mainlogicthread = std::async(std::launch::async, mainLogic, config, std::ref(state), gradnoise); + } + + if (isGenerating && textu != 0) { + ImGui::EndDisabled(); + + ImGui::SameLine(); + if (ImGui::Button("Cancel")) { + cancelGeneration(); + } + // Check for new frames from the generation thread + bool hasNewFrame = false; + { + std::lock_guard lock(state.mutex); + if (state.hasNewFrame) { + livePreview(state.grid); + state.hasNewFrame = false; + previewText = "Generating... Frame: " + std::to_string(state.currentFrame); + } + } + + ImGui::Text(previewText.c_str()); + + if (textu != 0) { + ImVec2 imageSize = ImVec2(config.width * fs, config.height * fs); + ImVec2 uv_min = ImVec2(0.0f, 0.0f); + ImVec2 uv_max = ImVec2(1.0f, 1.0f); + ImGui::Image((void*)(intptr_t)textu, imageSize, uv_min, uv_max); + } else { + ImGui::Text("Generating preview..."); + } + + } else if (isGenerating) { + ImGui::EndDisabled(); + + ImGui::SameLine(); + if (ImGui::Button("Cancel")) { + cancelGeneration(); + } + // Check for new frames from the generation thread + bool hasNewFrame = false; + { + std::lock_guard lock(state.mutex); + if (state.hasNewFrame) { + livePreview(state.grid); + state.hasNewFrame = false; + previewText = "Generating... Frame: " + std::to_string(state.currentFrame); + } + } + + ImGui::Text(previewText.c_str()); + + } else if (textu != 0){ + //ImGui::EndDisabled(); + + ImGui::Text(previewText.c_str()); + + if (textu != 0) { + ImVec2 imageSize = ImVec2(config.width * 0.5f, config.height * 0.5f); + ImVec2 uv_min = ImVec2(0.0f, 0.0f); + ImVec2 uv_max = ImVec2(1.0f, 1.0f); + ImGui::Image((void*)(intptr_t)textu, imageSize, uv_min, uv_max); + } else { + ImGui::Text("Generating preview..."); + } + + } else { + ImGui::Text("No preview available"); + ImGui::Text("Start generation to see live preview"); + } + //std::cout << "sleeping" << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + //std::cout << "ending" << std::endl; + ImGui::End(); + } + + + // std::cout << "ending frame" << std::endl; + ImGui::Render(); + int display_w, display_h; + glfwGetFramebufferSize(window, &display_w, &display_h); + glViewport(0, 0, display_w, display_h); + glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); + glClear(GL_COLOR_BUFFER_BIT); + + // std::cout << "rendering" << std::endl; + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + glfwSwapBuffers(window); + //mainlogicthread.join(); + + // std::cout << "swapping buffers" << std::endl; + } + cancelGeneration(); + + + // std::cout << "shutting down" << std::endl; + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); + + // std::cout << "destroying" << std::endl; + glfwDestroyWindow(window); + if (textu != 0) { + glDeleteTextures(1, &textu); + textu = 0; + } + glfwTerminate(); + FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED); + + // std::cout << "printing" << std::endl; + return 0; +} \ No newline at end of file diff --git a/util/grid/grid2.hpp b/util/grid/grid2.hpp index 41e1320..7da3812 100644 --- a/util/grid/grid2.hpp +++ b/util/grid/grid2.hpp @@ -9,6 +9,7 @@ #include "../output/frame.hpp" #include "../noise/pnoise2.hpp" #include "../simblocks/water.hpp" +#include "../simblocks/temp.hpp" #include #include @@ -145,6 +146,15 @@ public: } } + std::unordered_set find(const Vec2& center) const { + //Vec2 g2pos = worldToGrid(center); + auto cellIt = grid.find(worldToGrid(center)); + if (cellIt != grid.end()) { + return cellIt->second; + } + return std::unordered_set(); + } + std::vector queryRange(const Vec2& center, float radius) const { std::vector results; float radiusSq = radius * radius; @@ -153,16 +163,15 @@ public: Vec2 minGrid = worldToGrid(center - Vec2(radius, radius)); Vec2 maxGrid = worldToGrid(center + Vec2(radius, radius)); + size_t estimatedSize = (maxGrid.x - minGrid.x + 1) * (maxGrid.y - minGrid.y + 1) * 10; + results.reserve(estimatedSize); + // Check all relevant grid cells - //#pragma omp parallel for for (int x = minGrid.x; x <= maxGrid.x; ++x) { for (int y = minGrid.y; y <= maxGrid.y; ++y) { - Vec2 gridPos(x, y); - auto cellIt = grid.find(gridPos); + auto cellIt = grid.find(Vec2(x, y)); if (cellIt != grid.end()) { - for (size_t id : cellIt->second) { - results.push_back(id); - } + results.insert(results.end(), cellIt->second.begin(), cellIt->second.end()); } } } @@ -198,7 +207,8 @@ protected: //TODO: spatial map SpatialGrid spatialGrid; - float spatialCellSize = 2.0f; + //float spatialCellSize = 2.0f; + float spatialCellSize = neighborRadius * 1.5f; // Default background color for empty spaces Vec4 defaultBackgroundColor = Vec4(0.0f, 0.0f, 0.0f, 0.0f); @@ -208,6 +218,9 @@ protected: //water std::unordered_map water; + + std::unordered_map tempMap; + bool updatingView = false; public: bool usable = false; @@ -550,7 +563,7 @@ public: Colors[id] = color; Sizes[id] = size; - spatialGrid.insert(id,pos); + //spatialGrid.insert(id,pos); } shrinkIfNeeded(); @@ -581,9 +594,7 @@ public: } shrinkIfNeeded(); - std::cout << "shrunk. " << std::endl; updateNeighborMap(); - std::cout << "neighbormap updated. " << std::endl; usable = true; return newids; @@ -965,27 +976,49 @@ public: defaultBackgroundColor = Vec4(0.0f, 0.0f, 0.0f, 0.0f); } - // neighbor map + void optimizeSpatialGrid() { + //std::cout << "optimizeSpatialGrid()" << std::endl; + neighborRadius = 1.0; + spatialCellSize = neighborRadius * neighborRadius; + spatialGrid = SpatialGrid(spatialCellSize); + + // Rebuild spatial grid + spatialGrid.clear(); + for (const auto& [id, pos] : Positions) { + spatialGrid.insert(id, pos); + } + } + void updateNeighborMap() { + //std::cout << "updateNeighborMap()" << std::endl; TIME_FUNCTION; neighborMap.clear(); + optimizeSpatialGrid(); // For each object, find nearby neighbors + float radiusSq = neighborRadius * neighborRadius; #pragma omp parallel for for (const auto& [id1, pos1] : Positions) { std::vector neighbors; - float radiusSq = neighborRadius * neighborRadius; - auto candidate_ids = spatialGrid.queryRange(pos1, neighborRadius); + //std::vector candidate_ids = spatialGrid.queryRange(pos1, neighborRadius); + std::unordered_set candidate_idset = spatialGrid.find(pos1); + std::vector candidate_ids; + candidate_ids.reserve(candidate_idset.size()); + for (auto it = candidate_idset.begin(); it != candidate_idset.end();) { + candidate_ids.push_back(std::move(candidate_idset.extract(it++).value())); + } + for (size_t id2 : candidate_ids) { - if (id1 != id2 && Positions.at(id1).distanceSquared(Positions.at(id2)) <= radiusSq) { + if (id1 != id2) { // && Positions.at(id1).distanceSquared(Positions.at(id2)) <= radiusSq) { neighbors.push_back(id2); } } + #pragma omp critical neighborMap[id1] = std::move(neighbors); } } - + // Update neighbor map for a single object void updateNeighborForID(size_t id) { TIME_FUNCTION; @@ -1015,6 +1048,97 @@ public: updateNeighborMap(); // Recompute all neighbors } + + //temp stuff + void setTemp(const Vec2 pos, double temp) { + size_t id = getOrCreatePositionVec(pos, 0.0, true); + setTemp(id, temp); + } + + void setTemp(size_t id, double temp) { + Temp tval = Temp(temp); + tempMap[id] = tval; + } + + double getTemp(size_t id) { + if (tempMap.find(id) != tempMap.end()) { + Temp temp; + double dtemp = temp.calTempIDW(getPositionID(id), getTemps()); + setTemp(id, dtemp); + } + return tempMap.at(id).temp; + } + + std::unordered_map getTemps() { + std::unordered_map out; + for (const auto& [id, temp] : tempMap) { + out[getPositionID(id)] = temp; + } + return out; + } + + double getTemp(const Vec2 pos) { + size_t id = getOrCreatePositionVec(pos, 0.0f, true); + if (tempMap.find(id) == tempMap.end()) { + Temp temp; + double dtemp = temp.calTempIDW(pos, getTemps()); + setTemp(id, dtemp); + return dtemp; + } + else return tempMap.at(id).temp; + } + + frame getTempAsFrame(Vec2 minCorner, Vec2 maxCorner, Vec2 res) { + TIME_FUNCTION; + if (updatingView) return frame(); + updatingView = true; + int pcount = 0; + std::cout << "getTempAsFrame() started" << pcount++ << std::endl; + size_t sheight = maxCorner.x - minCorner.x; + size_t swidth = maxCorner.y - minCorner.y; + // std::cout << "getTempAsFrame() started" << pcount++ << std::endl; + + int width = static_cast(res.x); + int height = static_cast(res.y); + // std::cout << "getTempAsFrame() started" << pcount++ << std::endl; + std::unordered_map tempBuffer; + tempBuffer.reserve(res.x * res.y); + // std::cout << "getTempAsFrame() started" << pcount++ << std::endl; + double maxTemp = 0.0; + double minTemp = 0.0; + // std::cout << "getTempAsFrame() started" << pcount++ << std::endl; + for (int x = 0; x < res.x; x++) { + for (int y = 0; y < res.y; y++) { + Vec2 cposout = Vec2(x,y); + Vec2 cposin = Vec2(x/sheight, y/swidth); + // std::cout << "getTempAsFrame() started" << pcount++ << std::endl; + double ctemp = getTemp(cposin); + + tempBuffer[Vec2(x,y)] = ctemp; + // std::cout << "getTempAsFrame() started" << pcount++ << std::endl; + if (ctemp > maxTemp) maxTemp = ctemp; + else if (ctemp < minTemp) minTemp = ctemp; + } + } + + // std::cout << "getTempAsFrame() middle" << std::endl; + + std::vector rgbaBuffer; + rgbaBuffer.reserve(tempBuffer.size() * 4); + for (const auto& [v2, temp] : tempBuffer) { + size_t index = (v2.y + v2.y) * 4; + double atemp = static_cast((((temp-minTemp) * 100) / (maxTemp-minTemp)) * 255); + rgbaBuffer[index] = atemp; + rgbaBuffer[index+1] = atemp; + rgbaBuffer[index+2] = atemp; + rgbaBuffer[index+3] = 1.0; + } + frame result = frame(res.x,res.y, frame::colormap::RGBA); + result.setData(rgbaBuffer); + return result; + updatingView = false; + } + }; #endif \ No newline at end of file diff --git a/util/output/bmpwriter.hpp b/util/output/bmpwriter.hpp index 8ea5bac..adf4e45 100644 --- a/util/output/bmpwriter.hpp +++ b/util/output/bmpwriter.hpp @@ -89,11 +89,15 @@ public: // Save from 1D vector of uint8_t pixels (BGR order: pixels[i]=b, pixels[i+1]=g, pixels[i+2]=r) static bool saveBMP(const std::string& filename, const std::vector& pixels, int width, int height) { if (pixels.size() != width * height * 3) { + std::cout << "wrong pixel count." << std::endl; + std::cout << "expected: " << width*height*3 << std::endl; + std::cout << "got: " << pixels.size() << std::endl; return false; } // Create directory if needed if (!createDirectoryIfNeeded(filename)) { + std::cout << "directory creation failed" << std::endl; return false; } @@ -110,6 +114,7 @@ public: std::ofstream file(filename, std::ios::binary); if (!file) { + std::cout << "file wasnt made" << std::endl; return false; } @@ -139,7 +144,18 @@ public: } static bool saveBMP(const std::string& filename, frame& frame) { - return saveBMP(filename, frame.getData(), frame.getWidth(), frame.getHeight()); + if (frame.colorFormat == frame::colormap::RGB) { + std::cout << "found correct colormap" << std::endl; + return saveBMP(filename, frame.getData(), frame.getWidth(), frame.getHeight()); + } else if (frame.colorFormat == frame::colormap::RGBA) { + std::cout << "found incorrect colormap. converting from RGBA" << std::endl; + std::vector fdata = convertRGBAtoRGB(frame.getData()); + return saveBMP(filename, fdata, frame.getWidth(), frame.getHeight()); + } + else { + std::cout << "found incorrect colormap." << std::endl; + return false; + } } private: @@ -190,6 +206,21 @@ private: return true; } + + static std::vector convertRGBAtoRGB(const std::vector& rgba) { + std::vector rgb; + rgb.reserve((rgba.size() / 4) * 3); + + for (size_t i = 0; i < rgba.size() / 4; i++) { + size_t index = i * 4; + rgb.push_back(rgba[index + 0]); + rgb.push_back(rgba[index + 1]); + rgb.push_back(rgba[index + 2]); + } + + return rgb; + } + }; #endif \ No newline at end of file diff --git a/util/simblocks/temp.hpp b/util/simblocks/temp.hpp new file mode 100644 index 0000000..0505734 --- /dev/null +++ b/util/simblocks/temp.hpp @@ -0,0 +1,37 @@ +#ifndef temp_hpp +#define temp_hpp + +#include "../vectorlogic/vec2.hpp" +#include "../timing_decorator.hpp" +#include +#include + +class Temp { +private: + +protected: + +public: + double temp; + + static double calTempIDW(const Vec2& testPos, std::unordered_map others) { + TIME_FUNCTION; + double power = 2.0; + double num = 0.0; + double den = 0.0; + for (const auto& [point, temp] : others) { + double dist = testPos.distance(point); + + double weight = 1.0 / std::pow(dist, power); + num += weight * temp.temp; + den += weight; + } + + if (den < 1e-10 && den > -1e-10) { + den = 1e-10; + } + return num / den; + } +}; + +#endif \ No newline at end of file diff --git a/util/vectorlogic/vec2.hpp b/util/vectorlogic/vec2.hpp index 618ca4b..18f99bb 100644 --- a/util/vectorlogic/vec2.hpp +++ b/util/vectorlogic/vec2.hpp @@ -290,10 +290,10 @@ class Vec2 { }; }; -// inline std::ostream& operator<<(std::ostream& os, const Vec2& vec) { -// os << vec.toString(); -// return os; -// } +inline std::ostream& operator<<(std::ostream& os, const Vec2& vec) { + os << vec.toString(); + return os; +} namespace std { template<>