diff --git a/.gitmodules b/.gitmodules index 4d0c24d7..f02dec2b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -11,3 +11,6 @@ [submodule "cpp/vendor/glfw"] path = cpp/vendor/glfw url = https://github.com/glfw/glfw.git +[submodule "cpp/vendor/implot3d"] + path = cpp/vendor/implot3d + url = https://github.com/brenocq/implot3d.git diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index fde4edb9..84747b76 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -33,6 +33,18 @@ add_library(implot STATIC target_include_directories(implot PUBLIC ${IMPLOT_DIR}) target_link_libraries(implot PUBLIC imgui) +# --- Vendor: ImPlot3D --- +# Pinned to v0.4 (commit 41ae3e447c0de20ecab95d38a4b4dc0835a3efc2). +# See cpp/vendor/implot3d.VENDORING.md for update procedure. +set(IMPLOT3D_DIR ${CMAKE_CURRENT_SOURCE_DIR}/vendor/implot3d) +add_library(implot3d STATIC + ${IMPLOT3D_DIR}/implot3d.cpp + ${IMPLOT3D_DIR}/implot3d_items.cpp + ${IMPLOT3D_DIR}/implot3d_meshes.cpp +) +target_include_directories(implot3d PUBLIC ${IMPLOT3D_DIR}) +target_link_libraries(implot3d PUBLIC imgui) + # --- Vendor: Tracy (optional) --- if(TRACY_ENABLE) set(TRACY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/vendor/tracy) @@ -102,7 +114,7 @@ target_include_directories(fn_framework PUBLIC target_compile_definitions(fn_framework PUBLIC FN_CPP_ROOT="${CMAKE_CURRENT_SOURCE_DIR}" ) -target_link_libraries(fn_framework PUBLIC imgui implot) +target_link_libraries(fn_framework PUBLIC imgui implot implot3d) if(TRACY_ENABLE) target_link_libraries(fn_framework PUBLIC tracy) endif() diff --git a/cpp/apps/primitives_gallery/CMakeLists.txt b/cpp/apps/primitives_gallery/CMakeLists.txt index 6812bf9f..fefa8a23 100644 --- a/cpp/apps/primitives_gallery/CMakeLists.txt +++ b/cpp/apps/primitives_gallery/CMakeLists.txt @@ -5,6 +5,7 @@ add_imgui_app(primitives_gallery demos_viz.cpp demos_graph.cpp demos_gfx.cpp + demos_3d.cpp demos_text_editor.cpp demos_gl_texture.cpp demos_extras.cpp @@ -39,6 +40,9 @@ add_imgui_app(primitives_gallery ${CMAKE_SOURCE_DIR}/functions/viz/gauge.cpp ${CMAKE_SOURCE_DIR}/functions/viz/heatmap.cpp ${CMAKE_SOURCE_DIR}/functions/viz/table_view.cpp + # 3D viz primitives (issue 0028, ImPlot3D) + ${CMAKE_SOURCE_DIR}/functions/viz/surface_plot_3d.cpp + ${CMAKE_SOURCE_DIR}/functions/viz/scatter_3d.cpp # Graph stack (instanced GPU + Barnes-Hut + spatial hash) ${CMAKE_SOURCE_DIR}/functions/viz/graph_types.cpp ${CMAKE_SOURCE_DIR}/functions/viz/graph_renderer.cpp diff --git a/cpp/apps/primitives_gallery/demos.h b/cpp/apps/primitives_gallery/demos.h index b7f22c32..84b3c80a 100644 --- a/cpp/apps/primitives_gallery/demos.h +++ b/cpp/apps/primitives_gallery/demos.h @@ -34,6 +34,8 @@ void demo_candlestick(); void demo_gauge(); void demo_heatmap(); void demo_table_view(); +void demo_surface_plot_3d(); // issue 0028, ImPlot3D +void demo_scatter_3d(); // issue 0028, ImPlot3D // --- Gfx --- void demo_shader_canvas(); diff --git a/cpp/apps/primitives_gallery/demos_3d.cpp b/cpp/apps/primitives_gallery/demos_3d.cpp new file mode 100644 index 00000000..30f8c505 --- /dev/null +++ b/cpp/apps/primitives_gallery/demos_3d.cpp @@ -0,0 +1,100 @@ +// demos_3d — demos para los primitivos viz/* basados en ImPlot3D. +// Issue 0028: surface_plot_3d real + scatter_3d. + +#include "demos.h" +#include "demo.h" + +#include "viz/surface_plot_3d.h" +#include "viz/scatter_3d.h" + +#include +#include +#include +#include + +namespace gallery { + +// --------------------------------------------------------------------------- +// surface_plot_3d +// --------------------------------------------------------------------------- + +void demo_surface_plot_3d() { + demo_header("surface_plot_3d", "v2.0.0", + "Superficie 3D ImPlot3D (z = A * sin(fx*x) * cos(fy*y)) con sliders para " + "ajustar las frecuencias en tiempo real. Drag para orbitar, wheel para zoom."); + + section("Malla 64x64 — sin(fx*x) * cos(fy*y)"); + + static float fx = 0.20f; + static float fy = 0.20f; + static float amp = 1.0f; + + ImGui::SliderFloat("fx", &fx, 0.05f, 1.0f, "%.2f"); + ImGui::SliderFloat("fy", &fy, 0.05f, 1.0f, "%.2f"); + ImGui::SliderFloat("amplitud", &, 0.1f, 3.0f, "%.2f"); + + constexpr int N = 64; + static std::vector z(N * N); + for (int j = 0; j < N; ++j) { + for (int i = 0; i < N; ++i) { + z[j * N + i] = amp * std::sin(fx * float(i)) * std::cos(fy * float(j)); + } + } + + fn::SurfacePlot3DConfig cfg{}; + cfg.z = z.data(); + cfg.nx = N; cfg.ny = N; + cfg.x_min = 0.f; cfg.x_max = float(N); + cfg.y_min = 0.f; cfg.y_max = float(N); + cfg.size = ImVec2(-1.f, 420.f); + fn::surface_plot_3d("##gallery_surface", cfg); +} + +// --------------------------------------------------------------------------- +// scatter_3d +// --------------------------------------------------------------------------- + +void demo_scatter_3d() { + demo_header("scatter_3d", "v1.0.0", + "Scatter 3D ImPlot3D con color por punto. 3 clusters gaussianos sinteticos " + "(N=500) para simular una visualizacion tipica de PCA / clustering."); + + section("3 clusters gaussianos (500 puntos)"); + + constexpr int N = 500; + static std::vector xs(N), ys(N), zs(N); + static std::vector colors(N); + static bool initialized = false; + + if (!initialized) { + std::mt19937 rng(42); + std::normal_distribution g(0.f, 0.4f); + const ImU32 palette[3] = { + IM_COL32(255, 99, 71, 255), // tomate + IM_COL32( 65, 170, 255, 255), // azul + IM_COL32(120, 220, 120, 255), // verde + }; + const float cx[3] = {-1.5f, 1.5f, 0.f}; + const float cy[3] = { 0.f, 0.f, 2.0f}; + const float cz[3] = { 0.f, 1.0f,-1.0f}; + for (int i = 0; i < N; ++i) { + int c = i % 3; + xs[i] = cx[c] + g(rng); + ys[i] = cy[c] + g(rng); + zs[i] = cz[c] + g(rng); + colors[i] = palette[c]; + } + initialized = true; + } + + fn::Scatter3DConfig cfg{}; + cfg.xs = xs.data(); + cfg.ys = ys.data(); + cfg.zs = zs.data(); + cfg.colors = colors.data(); + cfg.n = N; + cfg.size = ImVec2(-1.f, 420.f); + fn::scatter_3d("##gallery_clusters", cfg); +} + +} // namespace gallery diff --git a/cpp/apps/primitives_gallery/main.cpp b/cpp/apps/primitives_gallery/main.cpp index 269bfe6b..9e34e670 100644 --- a/cpp/apps/primitives_gallery/main.cpp +++ b/cpp/apps/primitives_gallery/main.cpp @@ -61,6 +61,8 @@ static const DemoEntry k_demos[] = { {"gauge", "gauge", "Viz", &gallery::demo_gauge}, {"heatmap", "heatmap", "Viz", &gallery::demo_heatmap}, {"table_view", "table_view", "Viz", &gallery::demo_table_view}, + {"surface_plot_3d", "surface_plot_3d", "Viz", &gallery::demo_surface_plot_3d}, + {"scatter_3d", "scatter_3d", "Viz", &gallery::demo_scatter_3d}, // Gfx (shaders_lab core) {"shader_canvas", "shader_canvas", "Gfx", &gallery::demo_shader_canvas}, {"gl_texture", "gl_texture_load", "Gfx", &gallery::demo_gl_texture}, // wave 1 diff --git a/cpp/framework/app_base.cpp b/cpp/framework/app_base.cpp index 7643885e..f1185999 100644 --- a/cpp/framework/app_base.cpp +++ b/cpp/framework/app_base.cpp @@ -4,6 +4,7 @@ #include "imgui_impl_glfw.h" #include "imgui_impl_opengl3.h" #include "implot.h" +#include "implot3d.h" #include "core/tokens.h" #include "core/icon_font.h" #include "core/app_settings.h" @@ -51,6 +52,7 @@ int run_app(AppConfig config, std::function render_fn) { IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImPlot::CreateContext(); + ImPlot3D::CreateContext(); ImGuiIO& io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; @@ -163,6 +165,7 @@ int run_app(AppConfig config, std::function render_fn) { // Cleanup ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); + ImPlot3D::DestroyContext(); ImPlot::DestroyContext(); ImGui::DestroyContext(); glfwDestroyWindow(window); diff --git a/cpp/functions/viz/scatter_3d.cpp b/cpp/functions/viz/scatter_3d.cpp new file mode 100644 index 00000000..95a45004 --- /dev/null +++ b/cpp/functions/viz/scatter_3d.cpp @@ -0,0 +1,33 @@ +#include "viz/scatter_3d.h" + +#include "imgui.h" +#include "implot3d.h" + +namespace fn { + +void scatter_3d(const char* title, const Scatter3DConfig& cfg) { + if (!cfg.xs || !cfg.ys || !cfg.zs || cfg.n < 1) { + ImGui::BeginGroup(); + ImGui::TextDisabled("%s", title ? title : "##scatter_3d"); + ImGui::TextWrapped("scatter_3d: necesita xs, ys, zs != nullptr y n >= 1 (recibido n=%d)", + cfg.n); + ImGui::EndGroup(); + return; + } + + if (ImPlot3D::BeginPlot(title, cfg.size)) { + ImPlot3D::SetupAxes("x", "y", "z"); + + // ImPlot3DSpec::MarkerSizes / MarkerFillColors esperan punteros no + // const; los arrays de cfg vienen como const*. Como ImPlot3D solo los + // lee, hacemos un const_cast acotado a esta llamada. + ImPlot3DSpec spec; + if (cfg.sizes) spec.MarkerSizes = const_cast(cfg.sizes); + if (cfg.colors) spec.MarkerFillColors = const_cast(cfg.colors); + + ImPlot3D::PlotScatter("##points", cfg.xs, cfg.ys, cfg.zs, cfg.n, spec); + ImPlot3D::EndPlot(); + } +} + +} // namespace fn diff --git a/cpp/functions/viz/scatter_3d.h b/cpp/functions/viz/scatter_3d.h new file mode 100644 index 00000000..f45ef4a9 --- /dev/null +++ b/cpp/functions/viz/scatter_3d.h @@ -0,0 +1,29 @@ +#pragma once + +#include "imgui.h" + +namespace fn { + +// Configuracion para scatter_3d. +// +// `xs`, `ys`, `zs` son arrays de tamano `n`. `sizes` y `colors` son +// opcionales: si se pasan, se usan por punto (size en pixeles, color como +// ImU32 RGBA). Si son nullptr, ImPlot3D usa los valores por defecto del +// estilo activo. +struct Scatter3DConfig { + const float* xs = nullptr; + const float* ys = nullptr; + const float* zs = nullptr; + const float* sizes = nullptr; // opcional, length = n + const ImU32* colors = nullptr; // opcional, length = n + int n = 0; + ImVec2 size = ImVec2(-1.f, 400.f); +}; + +// Renderiza un scatter 3D usando ImPlot3D::PlotScatter. +// +// Llamar dentro de un frame ImGui activo. Si xs / ys / zs son nullptr o +// n < 1, renderiza un mensaje informativo. +void scatter_3d(const char* title, const Scatter3DConfig& cfg); + +} // namespace fn diff --git a/cpp/functions/viz/scatter_3d.md b/cpp/functions/viz/scatter_3d.md new file mode 100644 index 00000000..3fd868eb --- /dev/null +++ b/cpp/functions/viz/scatter_3d.md @@ -0,0 +1,77 @@ +--- +name: scatter_3d +kind: component +lang: cpp +domain: viz +version: "1.0.0" +purity: pure +signature: "void scatter_3d(const char* title, const fn::Scatter3DConfig& cfg)" +description: "Scatter 3D ImPlot3D con tamano y color opcional por punto, orbit/zoom/pan nativos" +tags: [implot3d, chart, visualization, gpu, scatter, 3d, points] +uses_functions: [] +uses_types: [] +returns: [] +returns_optional: false +error_type: "" +imports: [imgui, implot3d] +tested: false +tests: [] +test_file_path: "" +file_path: "cpp/functions/viz/scatter_3d.cpp" +framework: imgui +params: + - name: title + desc: "Titulo / id interno del plot" + - name: cfg + desc: "fn::Scatter3DConfig — xs, ys, zs (length n), sizes opcional, colors opcional (ImU32 RGBA), size del plot" +output: "Renderiza una nube de puntos 3D dentro del frame ImGui actual; soporta orbit (drag), zoom (wheel) y pan" +--- + +# scatter_3d + +Scatter 3D usando `ImPlot3D::PlotScatter`. Soporta tamano y color opcional por punto via `ImPlot3DSpec::MarkerSizes` / `MarkerFillColors`. + +Util para visualizaciones de PCA / clustering / nubes de puntos sinteticas. Llamar dentro de un frame ImGui activo. + +## Caracteristicas + +- **Per-point size**: pasar `cfg.sizes` (length = n, en pixeles) o dejar `nullptr` para usar el tamano del estilo activo. +- **Per-point color**: pasar `cfg.colors` (length = n, ImU32 RGBA) o dejar `nullptr` para que ImPlot3D use el color del colormap activo. +- **Orbit / zoom / pan**: nativos de ImPlot3D. + +## Ejemplo + +```cpp +#include "viz/scatter_3d.h" +#include "imgui.h" +#include +#include + +const int N = 500; +std::vector xs(N), ys(N), zs(N); +std::vector colors(N); + +std::mt19937 rng(42); +std::normal_distribution g(0.f, 0.4f); +const ImU32 palette[3] = { + IM_COL32(255, 99, 71, 255), // tomate + IM_COL32( 65,170,255, 255), // azul + IM_COL32(120,220,120, 255), // verde +}; +const float cx[3] = {-1.5f, 1.5f, 0.f}; +const float cy[3] = { 0.f, 0.f, 2.0f}; +const float cz[3] = { 0.f, 1.0f,-1.0f}; +for (int i = 0; i < N; ++i) { + int c = i % 3; + xs[i] = cx[c] + g(rng); + ys[i] = cy[c] + g(rng); + zs[i] = cz[c] + g(rng); + colors[i] = palette[c]; +} + +fn::Scatter3DConfig cfg{}; +cfg.xs = xs.data(); cfg.ys = ys.data(); cfg.zs = zs.data(); +cfg.colors = colors.data(); +cfg.n = N; +fn::scatter_3d("##clusters", cfg); +``` diff --git a/cpp/functions/viz/surface_plot_3d.cpp b/cpp/functions/viz/surface_plot_3d.cpp index 99c2f0bd..99a52941 100644 --- a/cpp/functions/viz/surface_plot_3d.cpp +++ b/cpp/functions/viz/surface_plot_3d.cpp @@ -1,12 +1,50 @@ #include "viz/surface_plot_3d.h" -#include "imgui.h" -void surface_plot_3d(const char* title, const float* values, int rows, int cols, - float z_min, float z_max) { - ImGui::BeginGroup(); - ImGui::TextDisabled("[STUB] %s", title); - ImGui::TextWrapped("surface_plot_3d requires ImPlot3D. " - "Add it to cpp/vendor/implot3d/ and rebuild."); - ImGui::Text("Data: %dx%d, range [%.2f, %.2f]", rows, cols, z_min, z_max); - ImGui::EndGroup(); +#include "imgui.h" +#include "implot3d.h" + +#include + +namespace fn { + +void surface_plot_3d(const char* title, const SurfacePlot3DConfig& cfg) { + if (!cfg.z || cfg.nx < 2 || cfg.ny < 2) { + ImGui::BeginGroup(); + ImGui::TextDisabled("%s", title ? title : "##surface_plot_3d"); + ImGui::TextWrapped("surface_plot_3d: necesita z != nullptr y nx,ny >= 2 (recibido %dx%d)", + cfg.nx, cfg.ny); + ImGui::EndGroup(); + return; + } + + // Genera coordenadas X / Y por vertice (row-major, mismo layout que z). + // ImPlot3D::PlotSurface espera arrays paralelos de nx*ny valores. + const int total = cfg.nx * cfg.ny; + std::vector xs(total); + std::vector ys(total); + const float dx = (cfg.nx > 1) ? (cfg.x_max - cfg.x_min) / float(cfg.nx - 1) : 0.f; + const float dy = (cfg.ny > 1) ? (cfg.y_max - cfg.y_min) / float(cfg.ny - 1) : 0.f; + for (int j = 0; j < cfg.ny; ++j) { + const float y = cfg.y_min + dy * float(j); + const int row = j * cfg.nx; + for (int i = 0; i < cfg.nx; ++i) { + xs[row + i] = cfg.x_min + dx * float(i); + ys[row + i] = y; + } + } + + if (ImPlot3D::BeginPlot(title, cfg.size)) { + ImPlot3D::SetupAxes(cfg.x_label, cfg.y_label, cfg.z_label); + ImPlot3D::PlotSurface("##surface", xs.data(), ys.data(), cfg.z, + cfg.nx, cfg.ny); + ImPlot3D::EndPlot(); + } + + if (cfg.show_colormap) { + // ImPlot3D rinde su propia leyenda dentro del plot; el flag aqui se + // reserva como contrato API por si en el futuro se necesita una + // colormap-bar externa (no implementado todavia). + } } + +} // namespace fn diff --git a/cpp/functions/viz/surface_plot_3d.h b/cpp/functions/viz/surface_plot_3d.h index 9e1f43c8..f293b1e3 100644 --- a/cpp/functions/viz/surface_plot_3d.h +++ b/cpp/functions/viz/surface_plot_3d.h @@ -1,8 +1,37 @@ #pragma once -// [STUB] Renders a 3D surface plot using ImPlot3D. -// Requires ImPlot3D to be vendored in cpp/vendor/implot3d/. -// Until then, displays a placeholder message inside an ImGui group. -// Call within an ImGui frame (inside fn::run_app render callback). -void surface_plot_3d(const char* title, const float* values, int rows, int cols, - float z_min, float z_max); +#include "imgui.h" + +namespace fn { + +// Configuracion para surface_plot_3d. +// +// `z` es la malla de alturas en row-major: z[j * nx + i] es la altura en +// (x_i, y_j). Las coordenadas X / Y se generan automaticamente entre +// [x_min, x_max] y [y_min, y_max] (lineales, mismo nx / ny que `z`). +// +// `size.x = -1` extiende el plot a todo el ancho disponible. `size.y` define +// la altura en pixeles (default 400). +struct SurfacePlot3DConfig { + const float* z = nullptr; // length = nx * ny, row-major + int nx = 0; + int ny = 0; + float x_min = 0.f; + float x_max = 1.f; + float y_min = 0.f; + float y_max = 1.f; + const char* x_label = "x"; + const char* y_label = "y"; + const char* z_label = "z"; + ImVec2 size = ImVec2(-1.f, 400.f); + bool show_colormap = true; +}; + +// Renderiza una superficie 3D usando ImPlot3D::PlotSurface. +// +// Llamar dentro de un frame ImGui activo (p.ej. dentro de la lambda render +// pasada a fn::run_app). Si `cfg.z == nullptr` o nx / ny < 2 la funcion +// renderiza un mensaje informativo en lugar del plot. +void surface_plot_3d(const char* title, const SurfacePlot3DConfig& cfg); + +} // namespace fn diff --git a/cpp/functions/viz/surface_plot_3d.md b/cpp/functions/viz/surface_plot_3d.md index 940765eb..ef6537d9 100644 --- a/cpp/functions/viz/surface_plot_3d.md +++ b/cpp/functions/viz/surface_plot_3d.md @@ -3,17 +3,17 @@ name: surface_plot_3d kind: component lang: cpp domain: viz -version: "1.0.0" +version: "2.0.0" purity: pure -signature: "void surface_plot_3d(const char* title, const float* values, int rows, int cols, float z_min, float z_max)" -description: "[STUB] Renderiza una superficie 3D — requiere ImPlot3D (no vendoreado aun)" -tags: [implot3d, chart, visualization, gpu, surface, 3d, stub] +signature: "void surface_plot_3d(const char* title, const fn::SurfacePlot3DConfig& cfg)" +description: "Superficie 3D ImPlot3D — malla z[nx*ny] row-major + ranges X/Y, eje rotatorio drag-to-orbit" +tags: [implot3d, chart, visualization, gpu, surface, 3d] uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "" -imports: [imgui] +imports: [imgui, implot3d] tested: false tests: [] test_file_path: "" @@ -21,41 +21,49 @@ file_path: "cpp/functions/viz/surface_plot_3d.cpp" framework: imgui params: - name: title - desc: "Titulo de la superficie, se muestra como header del plot" - - name: values - desc: "Array row-major de alturas Z con dimension rows*cols" - - name: rows - desc: "Numero de filas de la grilla de la superficie" - - name: cols - desc: "Numero de columnas de la grilla de la superficie" - - name: z_min - desc: "Valor minimo del eje Z para escalar el colormap" - - name: z_max - desc: "Valor maximo del eje Z para escalar el colormap" -output: "Renderiza un placeholder informativo en el frame ImGui actual; cuando ImPlot3D este disponible, renderizara la superficie 3D" + desc: "Titulo / id interno del plot" + - name: cfg + desc: "fn::SurfacePlot3DConfig — z (nx*ny row-major), nx, ny, x/y_min, x/y_max, labels, size, show_colormap" +output: "Renderiza una superficie 3D dentro del frame ImGui actual; soporta orbit (drag), zoom (wheel) y pan via ImPlot3D" --- # surface_plot_3d -**STUB** — la implementacion real requiere [ImPlot3D](https://github.com/brenocq/implot3d), que todavia no esta vendoreado en `cpp/vendor/implot3d/`. +Superficie 3D renderizada con [ImPlot3D](https://github.com/brenocq/implot3d) (vendoreado en `cpp/vendor/implot3d/`, pinned v0.4 commit `41ae3e44`). -Mientras tanto la funcion renderiza un grupo ImGui con un mensaje informativo que muestra el titulo, las dimensiones de la grilla y el rango Z. La firma es definitiva y no cambiara cuando se integre ImPlot3D. +La API toma una malla densa `z[nx*ny]` en row-major (`z[j*nx + i]` es la altura en el vertice `(x_i, y_j)`) y ranges para los ejes X / Y. Las coordenadas X / Y por vertice se generan internamente en lineal entre `[x_min, x_max]` y `[y_min, y_max]` — el caller solo necesita pasar el grid de alturas. -## Dependencia pendiente +Llamar dentro de un frame ImGui activo (lambda de `fn::run_app` o similar). El contexto de ImPlot3D lo crea / destruye `app_base` junto al de ImPlot. -Para activar la implementacion real: +## Caracteristicas -1. Clonar o copiar ImPlot3D en `cpp/vendor/implot3d/` -2. Anadir `implot3d.cpp` al build system (CMake / Makefile) -3. Reemplazar el cuerpo de `surface_plot_3d` por la llamada a `ImPlot3D::BeginPlot3D` / `ImPlot3D::PlotSurface` / `ImPlot3D::EndPlot3D` -4. Actualizar `imports` del .md a `[imgui, implot3d]` y quitar el tag `stub` +- **Orbit**: drag con LMB rota la camara alrededor del centro del plot. +- **Zoom**: scroll wheel. +- **Pan**: drag con MMB. +- **Colormap**: ImPlot3D aplica el colormap activo segun la altura Z. `show_colormap` esta reservado como contrato API por si se anyade colorbar externa. -## Uso +## Ejemplo ```cpp -// values es un array row-major de rows*cols floats -float grid[4 * 4] = { ... }; -surface_plot_3d("Mi Superficie", grid, 4, 4, -1.0f, 1.0f); +#include "viz/surface_plot_3d.h" +#include +#include + +constexpr int N = 64; +std::vector z(N * N); +for (int j = 0; j < N; ++j) + for (int i = 0; i < N; ++i) + z[j*N + i] = std::sin(0.2f * i) * std::cos(0.2f * j); + +fn::SurfacePlot3DConfig cfg{}; +cfg.z = z.data(); +cfg.nx = N; cfg.ny = N; +cfg.x_min = 0; cfg.x_max = float(N); +cfg.y_min = 0; cfg.y_max = float(N); +fn::surface_plot_3d("##terreno", cfg); ``` -Debe llamarse dentro del render callback de `fn::run_app` (o cualquier contexto con un frame ImGui activo). +## Limites practicos + +- Mallas hasta ~256×256 son fluidas a 60 FPS en GPUs modernas. Por encima de eso conviene downsamplear. +- ImPlot3D dibuja los triangulos en CPU y los empuja al ImDrawList — no es shader-based. diff --git a/cpp/vendor/implot3d b/cpp/vendor/implot3d new file mode 160000 index 00000000..41ae3e44 --- /dev/null +++ b/cpp/vendor/implot3d @@ -0,0 +1 @@ +Subproject commit 41ae3e447c0de20ecab95d38a4b4dc0835a3efc2 diff --git a/cpp/vendor/implot3d.VENDORING.md b/cpp/vendor/implot3d.VENDORING.md new file mode 100644 index 00000000..34ccb626 --- /dev/null +++ b/cpp/vendor/implot3d.VENDORING.md @@ -0,0 +1,41 @@ +# ImPlot3D — vendoring notes + +3D plotting library for Dear ImGui by Breno Cunha Queiroz, sister project to +[ImPlot](https://github.com/epezent/implot). + +- **Upstream:** https://github.com/brenocq/implot3d +- **License:** MIT (see `cpp/vendor/implot3d/LICENSE`) +- **Version:** v0.4 +- **Pinned commit:** `41ae3e447c0de20ecab95d38a4b4dc0835a3efc2` + (2026-04-05, _"chore: bump version to v0.4"_) + +The vendor lives as a git submodule in `cpp/vendor/implot3d` and is checked +out at the pinned commit above. + +## Files compiled into the `implot3d` CMake target + +- `implot3d.cpp` +- `implot3d_items.cpp` +- `implot3d_meshes.cpp` + +`implot3d_demo.cpp` and the contents of `example/` are intentionally left +out of the build — kept for reference only. + +## Update procedure + +```bash +cd cpp/vendor/implot3d +git fetch origin +git checkout +cd ../../.. +git add cpp/vendor/implot3d +# Update the pinned commit in this file. +git commit -m "chore(vendor): bump implot3d to " +``` + +## Used by + +- `cpp/functions/viz/surface_plot_3d.cpp` — `ImPlot3D::PlotSurface` +- `cpp/functions/viz/scatter_3d.cpp` — `ImPlot3D::PlotScatter` +- `cpp/framework/app_base.cpp` — creates/destroys the ImPlot3D context + alongside the ImPlot one. diff --git a/dev/issues/0028-cpp-implot3d-3d-viz.md b/dev/issues/completed/0028-cpp-implot3d-3d-viz.md similarity index 100% rename from dev/issues/0028-cpp-implot3d-3d-viz.md rename to dev/issues/completed/0028-cpp-implot3d-3d-viz.md