feat(viz): surface_plot_3d real (ImPlot3D) + scatter_3d nuevo

surface_plot_3d (v2.0.0): quita el STUB. API basada en
SurfacePlot3DConfig (z[nx*ny] row-major + ranges X/Y) que delega en
ImPlot3D::PlotSurface. Las coordenadas X/Y por vertice se generan
internamente desde [x_min, x_max] x [y_min, y_max].

scatter_3d (v1.0.0): nuevo primitivo. Scatter 3D con tamano y color
opcionales por punto via ImPlot3DSpec::MarkerSizes / MarkerFillColors.
Util para PCA / clustering / nubes de puntos sinteticas.

Ambos namespace fn::, kind component, purity pure. Orbit / zoom / pan
los aporta ImPlot3D nativo.

Issue 0028.
This commit is contained in:
2026-04-25 21:48:43 +02:00
parent b3e55f7abe
commit cebf87cb4e
6 changed files with 259 additions and 45 deletions
+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.