diff --git a/.gitmodules b/.gitmodules index 01cfbf3..6d1cff1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "external/argparse"] path = external/argparse url = https://github.com/p-ranav/argparse.git +[submodule "external/stb"] + path = external/stb + url = https://github.com/nothings/stb.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 543b967..2ab381e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,6 +78,7 @@ target_include_directories(poncaplot PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/external/nanogui/include" "${CMAKE_CURRENT_SOURCE_DIR}/external/ponca/" "${CMAKE_CURRENT_SOURCE_DIR}/external/argparse/include" + "${CMAKE_CURRENT_SOURCE_DIR}/external/stb/" "${CMAKE_CURRENT_SOURCE_DIR}/src/") # Link settings diff --git a/external/stb b/external/stb new file mode 160000 index 0000000..f4a71b1 --- /dev/null +++ b/external/stb @@ -0,0 +1 @@ +Subproject commit f4a71b13373436a2866c5d68f8f80ac6f0bc1ffe diff --git a/src/cli.cpp b/src/cli.cpp index 4b66b76..ef27c02 100644 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -1,13 +1,23 @@ #include "cli.h" -#include "myview.h" #include "dataManager.h" #include "drawingPass.h" -#include "drawingPasses/distanceField.h" +//#include "drawingPasses/distanceField.h" #include "argparse/argparse.hpp" +#define STB_IMAGE_STATIC +#define STB_IMAGE_IMPLEMENTATION +#define STB_IMAGE_WRITE_IMPLEMENTATION +#if defined(_MSC_VER) +# pragma warning (disable: 4505) // don't warn about dead code in stb_image.h +#elif defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +#endif +#include "stb_image.h" +#include "stb_image_write.h" +#include // floor PoncaPlotCLI::PoncaPlotCLI(DataManager* mgr) : m_dataMgr(mgr){ @@ -16,6 +26,19 @@ PoncaPlotCLI::PoncaPlotCLI(DataManager* mgr) : m_dataMgr(mgr){ bool PoncaPlotCLI::run(int argc, char **argv) { + struct { + std::string inputPath {}; + struct { + std::string name {"Oriented Sphere"}; + float scale {40}; + } fitting; + struct { + std::string path {}; + size_t width {500}; + size_t height {500}; + } output; + } params; + std::vector names; names.resize(m_dataMgr->nbSupportedDrawingPasses); for (const auto& p : m_dataMgr->supportedDrawingPasses) @@ -27,35 +50,110 @@ PoncaPlotCLI::run(int argc, char **argv) { argparse::ArgumentParser program("poncaplot-cli"); - - program.add_argument("scale") - .help("scale size (in pixels)") - .scan<'g', float>() - .default_value(10); program.add_argument("-i", "--input") .required() .help("input file (.pts or .txt)"); - program.add_argument("-o", "--output") - .help("output file (image)"); - program.add_argument("-f", "--fitType") - .help("fit type: [" + namesStr + "]"); + // output controls + { + program.add_argument("-o", "--output") + .help("output file (image)"); + program.add_argument("-w", "--width") + .help("output image width (in pixels)") + .default_value(params.output.width); + program.add_argument("-h", "--height") + .help("output image height (in pixels)") + .default_value(params.output.height); + } + + // fitting controls + { + auto ft = program.add_argument("-f", "--fitType") + .default_value(params.fitting.name) + .help("fit type: [" + namesStr + "]"); + for (const auto& type : m_dataMgr->supportedDrawingPasses) + ft.add_choice(type.first); + + program.add_argument("-s") + .help("scale size (in pixels)") + .scan<'g', float>() + .default_value(params.fitting.scale); + } + + // return value of the method: do we skip the GUI ? + bool skipGUI = true; + + bool loaded = false; try { program.parse_args(argc, argv); - auto inputPath = program.get("-i"); - if (! inputPath.empty()) + params.inputPath = program.get("-i"); + if (! params.inputPath.empty()) { - m_dataMgr->loadPointCloud(inputPath); - // load other properties - if( ! program.is_used("-o")) return false; // be sure that GUI is started. + loaded = m_dataMgr->loadPointCloud(params.inputPath);; + + // load fit properties + if (program.is_used("-f")) params.fitting.name = program.get("-f"); + if (program.is_used("-s")) params.fitting.scale = program.get("-s"); + + // load output properties + auto output = program.present("-o"); + if( output ) { + params.output.path = output.value(); + if (program.is_used("-w")) params.output.width = program.get("-w"); + if (program.is_used("-h")) params.output.height = program.get("-h"); + } else + skipGUI = false; // no output is set: display GUI with parameters set } } catch (const std::exception& err) { std::cout << err.what() << std::endl; std::cout << program; - return false; + skipGUI = false; + } + + // configure and do rendering + if (loaded && skipGUI){ + auto texture = new float [params.output.width * params.output.height * 4]; + + // configure fitting + std::cout << "Configure fitting" << std::endl; + auto pass = m_dataMgr->getDrawingPass(params.fitting.name); + + if( auto fit = dynamic_cast(pass) ) { + fit->m_scale = params.fitting.scale; + } + + // configure renderer + std::cout << "Configure renderer" << std::endl; + std::array renderPasses{ + new FillPass( {1,1,1,1}) + , pass + , new ColorMap({1,1,1,1}) +// , new DisplayPoint({0,0,0,1}) + }; + + // render + std::cout << "Render" << std::endl; + const auto& points = m_dataMgr->getKdTree(); + for (auto* p : renderPasses) { + p->render(points, texture, int(params.output.width), int(params.output.height)); + } + + std::cout << "Save image" << std::endl; + { + auto buffer = new char [params.output.width * params.output.height * 4]; + std::transform(texture, texture + params.output.width * params.output.height * 4, + buffer, + [](float in) -> char{ + return char(std::floor(in*255.f)); + }); + stbi_write_png(params.output.path.c_str(), params.output.width, params.output.height, + 4, buffer, params.output.width * 4); + stbi_image_free(buffer); + } + + delete[] texture; } - // do processing - return true; + return skipGUI; }