fix(fn-run): propagar stdout/stderr de bash functions library-style #1

Open
dataforge wants to merge 537 commits from auto/0077-fn-run-bash-mudo into master
16 changed files with 428 additions and 46 deletions
Showing only changes of commit d3397fb17c - Show all commits
+3
View File
@@ -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
+13 -1
View File
@@ -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()
@@ -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
+2
View File
@@ -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();
+100
View File
@@ -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 <imgui.h>
#include <cmath>
#include <random>
#include <vector>
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", &amp, 0.1f, 3.0f, "%.2f");
constexpr int N = 64;
static std::vector<float> 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<float> xs(N), ys(N), zs(N);
static std::vector<ImU32> colors(N);
static bool initialized = false;
if (!initialized) {
std::mt19937 rng(42);
std::normal_distribution<float> 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
+2
View File
@@ -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
+3
View File
@@ -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<void()> 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<void()> render_fn) {
// Cleanup
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImPlot3D::DestroyContext();
ImPlot::DestroyContext();
ImGui::DestroyContext();
glfwDestroyWindow(window);
+33
View File
@@ -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<float*>(cfg.sizes);
if (cfg.colors) spec.MarkerFillColors = const_cast<ImU32*>(cfg.colors);
ImPlot3D::PlotScatter("##points", cfg.xs, cfg.ys, cfg.zs, cfg.n, spec);
ImPlot3D::EndPlot();
}
}
} // namespace fn
+29
View File
@@ -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
+77
View File
@@ -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 <vector>
#include <random>
const int N = 500;
std::vector<float> xs(N), ys(N), zs(N);
std::vector<ImU32> colors(N);
std::mt19937 rng(42);
std::normal_distribution<float> 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);
```
+47 -9
View File
@@ -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 <vector>
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<float> xs(total);
std::vector<float> 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
+35 -6
View File
@@ -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
+38 -30
View File
@@ -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 <vector>
#include <cmath>
constexpr int N = 64;
std::vector<float> 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.
Vendored Submodule
+1
Submodule cpp/vendor/implot3d added at 41ae3e447c
+41
View File
@@ -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 <new-commit>
cd ../../..
git add cpp/vendor/implot3d
# Update the pinned commit in this file.
git commit -m "chore(vendor): bump implot3d to <new-commit>"
```
## 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.