docs(issues): añadir 0025-0036 — features C++ para registry y primitives_gallery
12 issues nuevos para implementacion paralela: text_editor, file_watcher, gl_texture_load, gl_compute+pingpong+DAG Compute, ImPlot3D, mesh_viewer, audio reactivo, animation curves, sql_workbench, http+ws inspector, scientific viz (5 charts), map_tiles, image_canvas + webcam_texture. Cada issue añade funciones al registry y un demo propio en primitives_gallery/demos_<feature>.cpp para minimizar conflictos en paralelo. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
# 0025 — C++ text_editor + file_watcher
|
||||
|
||||
## APP Metadata
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| **ID** | 0025 |
|
||||
| **Estado** | pendiente |
|
||||
| **Prioridad** | alta |
|
||||
| **Tipo** | feature — C++ devx (cpp/functions/core) |
|
||||
|
||||
## Dependencias
|
||||
|
||||
Ninguna. Se apoya en lo que ya existe (`tokens`, `app_base`, ImGui vendoreado).
|
||||
|
||||
**Desbloquea:** ciclo de edicion completo dentro de `shaders_lab` (editar GLSL + reload automatico al guardar) sin alt-tab a un editor externo. Tambien util para un futuro `sql_workbench` (issue 0032).
|
||||
|
||||
---
|
||||
|
||||
## Objetivo
|
||||
|
||||
Añadir dos primitivos al registry C++:
|
||||
|
||||
1. **`text_editor_cpp_core`** — editor de codigo embebido en ImGui con syntax highlighting (GLSL, SQL, generic). Wrapper de [ImGuiColorTextEdit](https://github.com/BalazsJako/ImGuiColorTextEdit) (header + cpp, MIT) vendoreado en `cpp/vendor/imgui_text_edit/`.
|
||||
2. **`file_watcher_cpp_core`** — watcher de archivos cross-platform (inotify Linux / ReadDirectoryChangesW Win) puro en su API: registra paths + callback, expone `poll()` no bloqueante.
|
||||
|
||||
Mostrar ambos en `primitives_gallery` con una demo combinada: editor que carga un .glsl, file_watcher detecta cambios externos y el editor recarga.
|
||||
|
||||
## Contexto
|
||||
|
||||
`shaders_lab` actualmente compila un `gl_shader` desde texto en RAM. No hay editor in-app: el usuario edita en VSCode y copia/pega. Tampoco hay file_watcher: para iterar sobre `.glsl` externos hay que recompilar la app.
|
||||
|
||||
Otros usos del editor:
|
||||
- `sql_workbench` (0032) — editor SQL + ejecucion sobre `registry.db`/`operations.db`.
|
||||
- Edicion de prompts/configs en apps futuras.
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
cpp/
|
||||
├── vendor/imgui_text_edit/ # NEW — vendor (MIT, 1 .h + 1 .cpp)
|
||||
│ ├── TextEditor.h
|
||||
│ └── TextEditor.cpp
|
||||
├── functions/core/
|
||||
│ ├── text_editor.h # NEW — wrapper en namespace fn
|
||||
│ ├── text_editor.cpp # NEW
|
||||
│ ├── text_editor.md # NEW
|
||||
│ ├── file_watcher.h # NEW
|
||||
│ ├── file_watcher.cpp # NEW (impure)
|
||||
│ └── file_watcher.md # NEW
|
||||
└── apps/primitives_gallery/
|
||||
├── demos_text_editor.cpp # NEW — demo combinada (editor + watcher)
|
||||
├── demos.h # MOD — declarar demo_text_editor()
|
||||
├── main.cpp # MOD — registrar entrada en sidebar
|
||||
└── CMakeLists.txt # MOD — añadir fuentes
|
||||
cpp/CMakeLists.txt # MOD — añadir vendor/imgui_text_edit a sources
|
||||
```
|
||||
|
||||
### Pure core / impure shell
|
||||
|
||||
- **`text_editor_cpp_core`**: API es **pura** en el sentido de "sin I/O propio" — todo el estado vive en una struct `TextEditorState`. La `TextEditor::Render()` hace draw calls de ImGui (igual que el resto de componentes). Marcar como `purity: pure`, `kind: component`. Misma categoria que `button_cpp_core`.
|
||||
- **`file_watcher_cpp_core`**: hace I/O del sistema de ficheros. `purity: impure`, `kind: function`, `error_type: error_go_core` (usar el equivalente C++ — ver `tokens` para errores existentes; si no, retornar `bool` con `last_error()`).
|
||||
|
||||
### API propuesta
|
||||
|
||||
```cpp
|
||||
namespace fn {
|
||||
|
||||
// text_editor.h
|
||||
struct TextEditorState; // forward (PIMPL hacia TextEditor de vendor)
|
||||
|
||||
enum class CodeLang { Generic, GLSL, SQL, Cpp };
|
||||
|
||||
TextEditorState* text_editor_create(CodeLang lang = CodeLang::Generic);
|
||||
void text_editor_destroy(TextEditorState*);
|
||||
void text_editor_set_text(TextEditorState*, const char* text);
|
||||
const char* text_editor_get_text(TextEditorState*); // valido hasta el siguiente call
|
||||
bool text_editor_render(TextEditorState*, const char* label, ImVec2 size); // true si cambio
|
||||
bool text_editor_is_dirty(const TextEditorState*);
|
||||
void text_editor_clear_dirty(TextEditorState*);
|
||||
|
||||
// file_watcher.h
|
||||
struct FileWatcher;
|
||||
|
||||
FileWatcher* file_watcher_create();
|
||||
void file_watcher_destroy(FileWatcher*);
|
||||
bool file_watcher_add(FileWatcher*, const char* path); // file or dir
|
||||
struct FileEvent { std::string path; enum { Modified, Created, Deleted } kind; };
|
||||
std::vector<FileEvent> file_watcher_poll(FileWatcher*); // non-blocking, drain
|
||||
}
|
||||
```
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1 — Vendor
|
||||
|
||||
- 1.1 Descargar [ImGuiColorTextEdit](https://github.com/BalazsJako/ImGuiColorTextEdit) (commit estable, MIT) a `cpp/vendor/imgui_text_edit/`.
|
||||
- 1.2 Añadirlo al `cpp/CMakeLists.txt` global como source list reusable.
|
||||
|
||||
### Fase 2 — text_editor
|
||||
|
||||
- 2.1 Implementar `text_editor.h/.cpp` con la API de arriba (PIMPL). Aplicar `tokens` para colores de fondo y border consistentes.
|
||||
- 2.2 Configurar lenguajes: `LanguageDefinition::GLSL()` y `LanguageDefinition::SQL()` (vendor ya las trae).
|
||||
- 2.3 `text_editor.md` con frontmatter completo (`kind: component`, `purity: pure`, `params`, `output`, ejemplo).
|
||||
|
||||
### Fase 3 — file_watcher
|
||||
|
||||
- 3.1 Implementar `file_watcher.h/.cpp`. Linux: inotify. Windows: ReadDirectoryChangesW (solo dir-level, filtrar por path). Macros condicionales.
|
||||
- 3.2 `file_watcher.md` (`kind: function`, `purity: impure`).
|
||||
|
||||
### Fase 4 — Gallery demo
|
||||
|
||||
- 4.1 `demos_text_editor.cpp` con `demo_text_editor()`: split horizontal — izquierda editor con un GLSL de ejemplo, derecha info (dirty flag, eventos del watcher). Botón "Save to /tmp/demo.glsl" + watcher activo sobre `/tmp/demo.glsl` que muestra los eventos.
|
||||
- 4.2 Registrar en `demos.h`, `main.cpp`, `CMakeLists.txt`.
|
||||
|
||||
### Fase 5 — Integracion en shaders_lab (opcional)
|
||||
|
||||
- 5.1 (Stretch) Añadir flag `--watch <archivo.glsl>` a `shaders_lab` que abre el archivo con text_editor + file_watcher y recompila automaticamente.
|
||||
|
||||
### Fase 6 — Tests + docs
|
||||
|
||||
- 6.1 Tests para `file_watcher` (Linux): crear tmpfile, modify, verificar evento. Compilable solo en Linux con guard.
|
||||
- 6.2 `./fn index` y `./fn show text_editor_cpp_core` / `file_watcher_cpp_core` verifica frontmatter.
|
||||
|
||||
## Ejemplo de uso
|
||||
|
||||
```cpp
|
||||
auto* ed = fn::text_editor_create(fn::CodeLang::GLSL);
|
||||
auto* fw = fn::file_watcher_create();
|
||||
fn::file_watcher_add(fw, "/tmp/demo.glsl");
|
||||
|
||||
fn::run_app("editor demo", [&]{
|
||||
if (fn::text_editor_render(ed, "##ed", {600, 400}))
|
||||
std::printf("changed\n");
|
||||
for (auto& ev : fn::file_watcher_poll(fw))
|
||||
std::printf("FS: %s\n", ev.path.c_str());
|
||||
});
|
||||
```
|
||||
|
||||
## Decisiones de diseño
|
||||
|
||||
- **PIMPL** evita exponer `TextEditor` del vendor en headers publicos.
|
||||
- **Vendor in-tree** sigue la convencion de ImGui/ImPlot ya presentes en `cpp/vendor/`.
|
||||
- **Sin tokens custom** — heredamos los del color theme global.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **API del vendor cambia entre commits**: pinear a un commit concreto en un README en `cpp/vendor/imgui_text_edit/`.
|
||||
- **inotify watch limit en Linux**: documentar `fs.inotify.max_user_watches` en el .md.
|
||||
- **Windows ReadDirectoryChangesW** solo emite a nivel de directorio: filtrar el path en el poll.
|
||||
@@ -0,0 +1,130 @@
|
||||
# 0026 — C++ gl_texture_load
|
||||
|
||||
## APP Metadata
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| **ID** | 0026 |
|
||||
| **Estado** | pendiente |
|
||||
| **Prioridad** | alta |
|
||||
| **Tipo** | feature — C++ gfx (cpp/functions/gfx) |
|
||||
|
||||
## Dependencias
|
||||
|
||||
Ninguna. Se compone con `gl_loader_cpp_gfx`, `gl_shader_cpp_gfx`, `shader_canvas_cpp_gfx`.
|
||||
|
||||
**Desbloquea:** shaders que toman imagenes externas (textura de ruido, foto, lookup tables). Base para `webcam_texture` (0036) y para nodos DAG futuros que usen sampler2D.
|
||||
|
||||
---
|
||||
|
||||
## Objetivo
|
||||
|
||||
Añadir una funcion impura al registry C++ que carga PNG/JPG/HDR desde disco y devuelve un `GLuint` listo para `glBindTexture` + `glUniform1i`. Vendorea **stb_image** (header-only, dominio publico) en `cpp/vendor/stb/`.
|
||||
|
||||
Mostrar el resultado en `primitives_gallery` como demo: un shader fullscreen que samplea una imagen cargada, con sliders para tint y zoom UV.
|
||||
|
||||
## Contexto
|
||||
|
||||
Actualmente `gl_shader_cpp_gfx` solo expone uniforms `u_resolution`, `u_time`, `u_mouse`, y `shaders_lab` puede pasar `u_params[16]` via `dag_uniforms`. No hay forma de meter una imagen al pipeline. Esto bloquea casos comunes:
|
||||
- Texturas de ruido (perlin/bluenoise) precomputadas.
|
||||
- Lookup tables para color grading.
|
||||
- Imagenes de referencia para post-process.
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
cpp/
|
||||
├── vendor/stb/
|
||||
│ └── stb_image.h # NEW (~10k LOC, public domain)
|
||||
├── functions/gfx/
|
||||
│ ├── gl_texture_load.h # NEW
|
||||
│ ├── gl_texture_load.cpp # NEW
|
||||
│ └── gl_texture_load.md # NEW
|
||||
└── apps/primitives_gallery/
|
||||
├── demos_gl_texture.cpp # NEW
|
||||
├── demos.h # MOD
|
||||
├── main.cpp # MOD
|
||||
└── CMakeLists.txt # MOD
|
||||
cpp/CMakeLists.txt # MOD
|
||||
```
|
||||
|
||||
### Pure core / impure shell
|
||||
|
||||
Funcion **impura** (`purity: impure`, `kind: function`): hace I/O de disco + crea recurso GPU.
|
||||
|
||||
### API propuesta
|
||||
|
||||
```cpp
|
||||
namespace fn {
|
||||
|
||||
struct GlTexture {
|
||||
GLuint id = 0;
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
int channels = 0;
|
||||
bool ok() const { return id != 0; }
|
||||
};
|
||||
|
||||
// Carga desde disco (PNG/JPG/BMP/TGA via stb_image, HDR como float opcional).
|
||||
// flip_y por defecto true para coincidir con UV de OpenGL.
|
||||
// Devuelve GlTexture con id=0 si falla; usar gl_texture_last_error() para detalle.
|
||||
GlTexture gl_texture_load(const char* path, bool flip_y = true, bool srgb = false);
|
||||
GlTexture gl_texture_load_from_memory(const unsigned char* data, int size, bool flip_y = true, bool srgb = false);
|
||||
|
||||
void gl_texture_destroy(GlTexture& tex);
|
||||
const char* gl_texture_last_error();
|
||||
|
||||
// Helper: bind a una texture unit y subir uniform sampler.
|
||||
void gl_texture_bind_uniform(GLuint program, const char* name, const GlTexture& tex, int unit);
|
||||
}
|
||||
```
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1 — Vendor stb_image
|
||||
|
||||
- 1.1 Descargar `stb_image.h` (commit estable) a `cpp/vendor/stb/`. Crear `cpp/vendor/stb/stb_image_impl.cpp` con `#define STB_IMAGE_IMPLEMENTATION` antes del include, para evitar emitir el cuerpo en multiples TUs.
|
||||
- 1.2 Añadirlo a `cpp/CMakeLists.txt` global.
|
||||
|
||||
### Fase 2 — gl_texture_load
|
||||
|
||||
- 2.1 Implementar `gl_texture_load.h/.cpp`. Usar `glGenTextures` + `glTexImage2D`. Filtros LINEAR_MIPMAP_LINEAR + LINEAR. Generar mipmaps con `glGenerateMipmap`.
|
||||
- 2.2 Soporte para `srgb=true` (`GL_SRGB8_ALPHA8` como internal format).
|
||||
- 2.3 Soporte HDR: si extension es `.hdr`, usar `stbi_loadf` y `GL_RGBA16F`.
|
||||
- 2.4 `gl_texture_load.md` con frontmatter (`kind: function`, `purity: impure`, `error_type`, `uses_functions: [gl_loader_cpp_gfx]`).
|
||||
|
||||
### Fase 3 — Gallery demo
|
||||
|
||||
- 3.1 `demos_gl_texture.cpp` con `demo_gl_texture_load()`: carga `assets/sample.png` (un PNG de prueba, ej. patron damero). Renderiza un fullscreen quad con un shader que samplea la textura y aplica tint via uniforms. ImGui::Image con thumbnail al lado y info (w, h, channels).
|
||||
- 3.2 Añadir `assets/sample.png` (256x256, ~10KB).
|
||||
- 3.3 Registrar en `demos.h`, `main.cpp`, `CMakeLists.txt`.
|
||||
|
||||
### Fase 4 — Tests + docs
|
||||
|
||||
- 4.1 Test que carga un PNG embebido (in-memory) y verifica `tex.ok() && tex.w == ...`.
|
||||
- 4.2 `./fn index` y verificar via `fn show gl_texture_load_cpp_gfx`.
|
||||
|
||||
## Ejemplo de uso
|
||||
|
||||
```cpp
|
||||
auto tex = fn::gl_texture_load("assets/noise.png");
|
||||
if (!tex.ok()) {
|
||||
std::fprintf(stderr, "error: %s\n", fn::gl_texture_last_error());
|
||||
return 1;
|
||||
}
|
||||
glUseProgram(prog);
|
||||
fn::gl_texture_bind_uniform(prog, "u_noise", tex, 0); // unit 0
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
```
|
||||
|
||||
## Decisiones de diseño
|
||||
|
||||
- **Vendoreado en `cpp/vendor/stb/`** sigue el patron actual de ImGui/ImPlot.
|
||||
- **`flip_y=true` por defecto** porque la convencion en GLSL es V hacia arriba.
|
||||
- **`srgb` opcional** — para texturas LDR de color, `GL_SRGB8_ALPHA8` da un gamma correcto sin tener que `pow(color, 2.2)` en el shader.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **Tamaño de imagenes**: cargar PNGs de 4K consume RAM/VRAM. Documentar limite practico (<= 8192 lado).
|
||||
- **Driver no soporta sRGB**: documentar fallback a `GL_RGBA8`.
|
||||
- **Codigo de stb_image lento**: aceptable para uso interactivo (no es hot path de cada frame).
|
||||
@@ -0,0 +1,171 @@
|
||||
# 0027 — C++ gl_compute_shader + gl_pingpong_fbo + DAG node Compute
|
||||
|
||||
## APP Metadata
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| **ID** | 0027 |
|
||||
| **Estado** | pendiente |
|
||||
| **Prioridad** | alta |
|
||||
| **Tipo** | feature — C++ gfx (cpp/functions/gfx) |
|
||||
|
||||
## Dependencias
|
||||
|
||||
Recomendado leer primero `gl_loader_cpp_gfx`, `gl_shader_cpp_gfx`, `gl_framebuffer_cpp_gfx`, `dag_catalog_cpp_gfx`, `dag_compile_cpp_gfx`. Sin bloqueos.
|
||||
|
||||
**Desbloquea:** simulaciones GPGPU (particulas, fluidos), feedback loops (reaction-diffusion, blur multipass, bloom), cualquier shader que necesite estado entre frames. Triplica el alcance del DAG visual de `shaders_lab`.
|
||||
|
||||
---
|
||||
|
||||
## Objetivo
|
||||
|
||||
Tres adiciones complementarias:
|
||||
|
||||
1. **`gl_compute_shader_cpp_gfx`** — carga + dispatch de compute shaders (GLSL `#version 430 core` + `layout(local_size_*) in`). API parejo a `gl_shader`.
|
||||
2. **`gl_pingpong_fbo_cpp_gfx`** — wrapper de **dos** `gl_framebuffer` con swap A↔B en cada paso. Permite usar el output del frame N-1 como input del frame N (feedback).
|
||||
3. **DAG kind `Compute`** — nuevo `DagNodeKind::Compute` en `dag_catalog`/`dag_compile`. Nodo que ejecuta un compute shader sobre la imagen actual, con N iteraciones configurable.
|
||||
|
||||
Demo en `primitives_gallery`: reaction-diffusion (Gray-Scott) usando ping-pong FBO + compute shader.
|
||||
|
||||
## Contexto
|
||||
|
||||
`gl_shader` solo soporta fragment shaders. El DAG actual es estrictamente fragment-pipeline (gen → op → blend). No hay forma de:
|
||||
- Persistir estado entre frames (necesario para feedback effects).
|
||||
- Hacer GPGPU verdadero (compute con buffers / images).
|
||||
- Implementar efectos como fluid sim, particulas o blur multipass eficientes.
|
||||
|
||||
OpenGL compute shaders requieren `#version 430 core` (= GL 4.3). El proyecto ya pide GL 3.3+; subir el minimo solo para los nodos compute es aceptable (graceful degrade si la GPU no lo soporta).
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
cpp/functions/gfx/
|
||||
├── gl_compute_shader.h # NEW
|
||||
├── gl_compute_shader.cpp # NEW
|
||||
├── gl_compute_shader.md # NEW
|
||||
├── gl_pingpong_fbo.h # NEW
|
||||
├── gl_pingpong_fbo.cpp # NEW
|
||||
├── gl_pingpong_fbo.md # NEW
|
||||
├── dag_types.h # MOD: añadir DagNodeKind::Compute
|
||||
├── dag_catalog.cpp/.h # MOD: registrar nodos compute (ej: ReactionDiffusion)
|
||||
├── dag_compile.cpp/.h # MOD: emitir paso compute en pipeline
|
||||
└── dag_uniforms.cpp/.h # MOD si los compute tienen params
|
||||
cpp/apps/primitives_gallery/
|
||||
├── demos_compute.cpp # NEW
|
||||
├── demos.h # MOD
|
||||
├── main.cpp # MOD
|
||||
└── CMakeLists.txt # MOD
|
||||
```
|
||||
|
||||
### Pure core / impure shell
|
||||
|
||||
- `gl_compute_shader` y `gl_pingpong_fbo`: ambos `purity: impure`, `kind: function`.
|
||||
- `dag_catalog`/`dag_compile`: ya son `pure`, mantener.
|
||||
- `dag_node_editor` / `dag_panel`: ya son `impure`, no se tocan en este issue (los nodos compute aparecen automaticamente al añadirse al catalog).
|
||||
|
||||
### API propuesta
|
||||
|
||||
```cpp
|
||||
namespace fn {
|
||||
|
||||
// gl_compute_shader.h
|
||||
struct GlComputeProgram {
|
||||
GLuint program = 0;
|
||||
int local_x = 1, local_y = 1, local_z = 1;
|
||||
bool ok() const { return program != 0; }
|
||||
};
|
||||
|
||||
GlComputeProgram gl_compute_compile(const char* glsl_body); // body sin version
|
||||
void gl_compute_destroy(GlComputeProgram&);
|
||||
|
||||
// Dispatch: bind images, set uniforms (callback opcional), glDispatchCompute, barrier.
|
||||
void gl_compute_dispatch(const GlComputeProgram&, int groups_x, int groups_y, int groups_z = 1);
|
||||
void gl_compute_bind_image(int unit, GLuint texture, GLenum access /*GL_READ_ONLY|WRITE|READ_WRITE*/, GLenum format = GL_RGBA8);
|
||||
|
||||
const char* gl_compute_last_error();
|
||||
|
||||
// gl_pingpong_fbo.h — dos FBO RGBA8 del mismo tamaño con A/B swap.
|
||||
struct PingpongFbo {
|
||||
Framebuffer a, b;
|
||||
bool a_is_front = true; // front = ultimo escrito
|
||||
};
|
||||
|
||||
PingpongFbo pingpong_create(int w, int h);
|
||||
void pingpong_destroy(PingpongFbo&);
|
||||
void pingpong_resize(PingpongFbo&, int w, int h);
|
||||
void pingpong_swap(PingpongFbo&);
|
||||
const Framebuffer& pingpong_front(const PingpongFbo&); // ultima lectura
|
||||
const Framebuffer& pingpong_back (const PingpongFbo&); // siguiente write target
|
||||
}
|
||||
```
|
||||
|
||||
### DAG: nuevo kind Compute
|
||||
|
||||
`dag_catalog` registra al menos un nodo de ejemplo: `compute_blur_separable` o `compute_reaction_diffusion`. Tras este issue queda abierto añadir mas (cada uno = un nuevo `DagNode`).
|
||||
|
||||
`dag_compile`: cuando emite un paso `Compute`, decide bindings (image read = ping-front, image write = pong-back) y devuelve metadatos para que el host haga `dispatch + swap` correctamente.
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1 — gl_compute_shader
|
||||
|
||||
- 1.1 Implementar wrapper minimo. Detectar version GL >= 4.3 al inicializar; si no, retornar `program == 0` y guardar error "compute shaders require OpenGL 4.3+".
|
||||
- 1.2 Helper `gl_compute_bind_image` con `glBindImageTexture`.
|
||||
- 1.3 `.md` con frontmatter (`purity: impure`, `kind: function`, `error_type`).
|
||||
|
||||
### Fase 2 — gl_pingpong_fbo
|
||||
|
||||
- 2.1 Implementar wrapper que reusa el `gl_framebuffer` existente. Resize propaga a los dos.
|
||||
- 2.2 `.md` con frontmatter.
|
||||
|
||||
### Fase 3 — DAG Compute kind
|
||||
|
||||
- 3.1 Añadir `DagNodeKind::Compute` en `dag_types.h` + serializacion (json IO).
|
||||
- 3.2 `dag_catalog`: registrar nodo de ejemplo `compute_blur_2pass` (separable Gaussian blur, 2 dispatches via flag uniform `direction`).
|
||||
- 3.3 `dag_compile`: emitir step compute con suficiente metadata para que el host (un nuevo helper) haga el dispatch.
|
||||
- 3.4 Helper `dag_compute_run_step()` — recibe `DagStep` compute + `PingpongFbo` + uniforms y hace `dispatch + swap`.
|
||||
|
||||
### Fase 4 — Gallery demo
|
||||
|
||||
- 4.1 `demos_compute.cpp` con `demo_compute_reaction_diffusion()`: `PingpongFbo` 512×512 + compute shader Gray-Scott con sliders (feed, kill, dt). Usa `ImGui::Image` para mostrar el front buffer cada frame. Boton "reset" rellena con seed.
|
||||
- 4.2 Registrar en gallery.
|
||||
|
||||
### Fase 5 — Tests + docs
|
||||
|
||||
- 5.1 Test (Linux + GPU disponible, opcional via env var `FN_GPU_TESTS=1`): compila un compute simple que escribe `vec4(1,0,0,1)` y verifica via `glReadPixels`.
|
||||
- 5.2 Test puro de `dag_compile` con un pipeline que mezcla nodos `Gen` + `Compute`.
|
||||
- 5.3 `./fn index` + `./fn show *_cpp_gfx` para los nuevos.
|
||||
|
||||
## Ejemplo de uso
|
||||
|
||||
```cpp
|
||||
auto cs = fn::gl_compute_compile(R"glsl(
|
||||
layout(local_size_x=8, local_size_y=8) in;
|
||||
layout(rgba8, binding=0) uniform image2D src;
|
||||
layout(rgba8, binding=1) uniform image2D dst;
|
||||
void main() {
|
||||
ivec2 p = ivec2(gl_GlobalInvocationID.xy);
|
||||
vec4 c = imageLoad(src, p);
|
||||
imageStore(dst, p, vec4(1.0 - c.rgb, 1.0));
|
||||
}
|
||||
)glsl");
|
||||
|
||||
auto pp = fn::pingpong_create(512, 512);
|
||||
glUseProgram(cs.program);
|
||||
fn::gl_compute_bind_image(0, fn::pingpong_front(pp).color_tex, GL_READ_ONLY);
|
||||
fn::gl_compute_bind_image(1, fn::pingpong_back (pp).color_tex, GL_WRITE_ONLY);
|
||||
fn::gl_compute_dispatch(cs, 64, 64);
|
||||
fn::pingpong_swap(pp);
|
||||
```
|
||||
|
||||
## Decisiones de diseño
|
||||
|
||||
- **Subir minimo a GL 4.3 solo para nodos Compute** — el resto del pipeline sigue compilando con 3.3. `gl_compute_compile` falla con error claro en GPUs viejas.
|
||||
- **Ping-pong fuera de FBO** — wrapper aparte, no fusionado con `gl_framebuffer` (mantenemos primitivos atomicos).
|
||||
- **Catalog del DAG abierto** — un solo nodo compute en este issue. Otros (fluid, particles) se añaden en issues posteriores reusando los wrappers.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **macOS no soporta compute shaders en GL 4.1** — documentar limitacion. En macOS el path Compute queda inactivo.
|
||||
- **Memory barriers**: olvidar `glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT)` produce datos basura. Encapsular en `gl_compute_dispatch`.
|
||||
- **Dispatch grande bloquea UI**: documentar que dispatches > 1M pixels deben dividirse o bajar local_size.
|
||||
@@ -0,0 +1,138 @@
|
||||
# 0028 — C++ ImPlot3D + surface_plot_3d real + scatter_3d
|
||||
|
||||
## APP Metadata
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| **ID** | 0028 |
|
||||
| **Estado** | pendiente |
|
||||
| **Prioridad** | media |
|
||||
| **Tipo** | feature — C++ viz (cpp/functions/viz) |
|
||||
|
||||
## Dependencias
|
||||
|
||||
`surface_plot_3d_cpp_viz` ya existe como **STUB**. Este issue quita el stub.
|
||||
|
||||
**Desbloquea:** dashboards 3D (terreno, FFT 2D, height maps, scatter 3D para PCA/clustering visualizations).
|
||||
|
||||
---
|
||||
|
||||
## Objetivo
|
||||
|
||||
1. Vendorear [ImPlot3D](https://github.com/brenocq/implot3d) (MIT, header + cpp) en `cpp/vendor/implot3d/`.
|
||||
2. Reescribir **`surface_plot_3d_cpp_viz`** (hoy STUB) con implementacion real.
|
||||
3. Añadir **`scatter_3d_cpp_viz`** — scatter 3D con color/size por punto.
|
||||
4. Demos en `primitives_gallery`.
|
||||
|
||||
## Contexto
|
||||
|
||||
`surface_plot_3d_cpp_viz` aparece en el registry pero su `code` es `[STUB] requires ImPlot3D not vendored yet`. Bloquea apps de visualizacion cientifica (heightfield, terreno, superficies parametricas).
|
||||
|
||||
ImPlot ya esta vendoreado y se usa para 2D. ImPlot3D es del mismo autor (brenocq), API parejo a ImPlot, con tres-axis y rotacion drag-to-orbit.
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
cpp/
|
||||
├── vendor/implot3d/ # NEW
|
||||
│ ├── implot3d.h
|
||||
│ ├── implot3d.cpp
|
||||
│ ├── implot3d_internal.h
|
||||
│ ├── implot3d_items.cpp
|
||||
│ └── implot3d_demo.cpp # opcional, util como referencia
|
||||
├── functions/viz/
|
||||
│ ├── surface_plot_3d.cpp/.h # MOD — quitar STUB
|
||||
│ ├── surface_plot_3d.md # MOD — actualizar tags/notes/code
|
||||
│ ├── scatter_3d.h # NEW
|
||||
│ ├── scatter_3d.cpp # NEW
|
||||
│ └── scatter_3d.md # NEW
|
||||
└── apps/primitives_gallery/
|
||||
├── demos_3d.cpp # NEW
|
||||
├── demos.h # MOD
|
||||
├── main.cpp # MOD
|
||||
└── CMakeLists.txt # MOD
|
||||
cpp/CMakeLists.txt # MOD — añadir sources de implot3d
|
||||
```
|
||||
|
||||
### API propuesta
|
||||
|
||||
```cpp
|
||||
namespace fn {
|
||||
|
||||
struct SurfacePlot3DConfig {
|
||||
const float* z; // size = nx * ny, row-major
|
||||
int nx, ny;
|
||||
float x_min = 0.f, x_max = 1.f;
|
||||
float y_min = 0.f, y_max = 1.f;
|
||||
const char* x_label = "x";
|
||||
const char* y_label = "y";
|
||||
const char* z_label = "z";
|
||||
ImVec2 size = {-1, 400};
|
||||
bool show_colormap = true;
|
||||
};
|
||||
void surface_plot_3d(const char* title, const SurfacePlot3DConfig& cfg);
|
||||
|
||||
struct Scatter3DConfig {
|
||||
const float* xs; // size = n
|
||||
const float* ys;
|
||||
const float* zs;
|
||||
const float* sizes = nullptr; // opcional: size = n
|
||||
const ImU32* colors = nullptr; // opcional: size = n
|
||||
int n;
|
||||
ImVec2 size = {-1, 400};
|
||||
};
|
||||
void scatter_3d(const char* title, const Scatter3DConfig& cfg);
|
||||
}
|
||||
```
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1 — Vendor
|
||||
|
||||
- 1.1 Clonar ImPlot3D, copiar fuentes a `cpp/vendor/implot3d/`. Pinear a tag/commit conocido.
|
||||
- 1.2 Inicializar contexto en `app_base` (similar a como ya inicializa ImPlot): `ImPlot3D::CreateContext()` / `DestroyContext()`.
|
||||
- 1.3 Añadir sources al `cpp/CMakeLists.txt`.
|
||||
|
||||
### Fase 2 — surface_plot_3d real
|
||||
|
||||
- 2.1 Reescribir `surface_plot_3d.cpp/.h` con la API de arriba usando `ImPlot3D::PlotSurface`.
|
||||
- 2.2 Actualizar `surface_plot_3d.md`: quitar tag `stub`, actualizar `code`, añadir `uses_functions: []` y `tags: ["3d", "viz"]`.
|
||||
|
||||
### Fase 3 — scatter_3d
|
||||
|
||||
- 3.1 Implementar `scatter_3d.cpp/.h` con `ImPlot3D::PlotScatter`. Soportar size/color por punto.
|
||||
- 3.2 `scatter_3d.md` con frontmatter (`kind: component`, `purity: pure`).
|
||||
|
||||
### Fase 4 — Gallery demos
|
||||
|
||||
- 4.1 `demos_3d.cpp` con dos demos:
|
||||
- `demo_surface_plot_3d()`: malla 64×64 con `z = sin(x)*cos(y)`. Sliders para frecuencia.
|
||||
- `demo_scatter_3d()`: 500 puntos con clusters sinteticos (3 clusters), colores por cluster.
|
||||
- 4.2 Registrar en gallery.
|
||||
|
||||
### Fase 5 — Tests + docs
|
||||
|
||||
- 5.1 Test puro de generacion de datos (la rendering depende del contexto GL, asumir que el smoke-test es la propia gallery).
|
||||
- 5.2 `./fn index` + verificar `surface_plot_3d_cpp_viz` ya no es STUB.
|
||||
|
||||
## Ejemplo de uso
|
||||
|
||||
```cpp
|
||||
std::vector<float> z(64*64);
|
||||
for (int j=0;j<64;j++) for (int i=0;i<64;i++)
|
||||
z[j*64+i] = std::sin(0.2f*i) * std::cos(0.2f*j);
|
||||
|
||||
fn::SurfacePlot3DConfig s{};
|
||||
s.z = z.data(); s.nx = 64; s.ny = 64;
|
||||
fn::surface_plot_3d("terreno", s);
|
||||
```
|
||||
|
||||
## Decisiones de diseño
|
||||
|
||||
- **ImPlot3D vs Plotly export**: ImPlot3D mantiene la coherencia con ImPlot 2D que ya usamos. Sin dependencias extra.
|
||||
- **Render-only por ahora**: pan/zoom/orbit las aporta ImPlot3D nativo.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **ImPlot3D inestable**: pinear commit; documentar version exacta en `cpp/vendor/implot3d/README.md`.
|
||||
- **Performance con malla grande**: documentar limite practico (~256×256 antes de notar drops).
|
||||
@@ -0,0 +1,178 @@
|
||||
# 0029 — C++ mesh_viewer + obj loader + orbit_camera
|
||||
|
||||
## APP Metadata
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| **ID** | 0029 |
|
||||
| **Estado** | pendiente |
|
||||
| **Prioridad** | media |
|
||||
| **Tipo** | feature — C++ viz/gfx (cpp/functions/viz, cpp/functions/gfx) |
|
||||
|
||||
## Dependencias
|
||||
|
||||
`gl_loader_cpp_gfx`, `gl_shader_cpp_gfx`, `gl_framebuffer_cpp_gfx`. Independiente de los demas issues.
|
||||
|
||||
**Desbloquea:** visualizacion 3D real (no solo plots): inspeccionar modelos, point clouds, debugging de geometria.
|
||||
|
||||
---
|
||||
|
||||
## Objetivo
|
||||
|
||||
Tres primitivos:
|
||||
|
||||
1. **`mesh_obj_load_cpp_gfx`** — parser minimo de Wavefront `.obj` (vertices + normales + indices). Sin materiales ni texturas en este issue.
|
||||
2. **`orbit_camera_cpp_core`** — camara orbital con drag (azimuth/elevation/distance), uniforms `view`/`proj`. Estado puro + helpers.
|
||||
3. **`mesh_viewer_cpp_viz`** — componente ImGui que rendea una malla `MeshGpu` con orbit camera dentro de un FBO + `ImGui::Image`.
|
||||
|
||||
Demo en `primitives_gallery` con un cubo procedural y opcion de cargar un `.obj` desde disco.
|
||||
|
||||
## Contexto
|
||||
|
||||
El stack actual hace 2D plotting (ImPlot) y 2D fragment shaders (`shader_canvas`). No hay forma de visualizar geometria 3D. ImPlot3D (issue 0028) cubre plots cientificos pero no meshes generales. Este issue añade el camino "raster 3D" autonomo.
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
cpp/functions/gfx/
|
||||
├── mesh_obj_load.h # NEW
|
||||
├── mesh_obj_load.cpp # NEW (parser puro)
|
||||
├── mesh_obj_load.md # NEW (kind: function, purity: pure)
|
||||
├── mesh_gpu.h # NEW (VAO/VBO/IBO de un Mesh)
|
||||
├── mesh_gpu.cpp # NEW
|
||||
└── mesh_gpu.md # NEW (kind: function, purity: impure)
|
||||
cpp/functions/core/
|
||||
├── orbit_camera.h # NEW
|
||||
├── orbit_camera.cpp # NEW
|
||||
└── orbit_camera.md # NEW (kind: function, purity: pure)
|
||||
cpp/functions/viz/
|
||||
├── mesh_viewer.h # NEW
|
||||
├── mesh_viewer.cpp # NEW
|
||||
└── mesh_viewer.md # NEW (kind: component, purity: impure)
|
||||
cpp/apps/primitives_gallery/
|
||||
├── demos_mesh.cpp # NEW
|
||||
├── demos.h # MOD
|
||||
├── main.cpp # MOD
|
||||
└── CMakeLists.txt # MOD
|
||||
cpp/CMakeLists.txt # MOD
|
||||
```
|
||||
|
||||
### API propuesta
|
||||
|
||||
```cpp
|
||||
namespace fn {
|
||||
|
||||
// --- mesh_obj_load (puro) ---
|
||||
struct Mesh {
|
||||
std::vector<float> positions; // x,y,z stride=3
|
||||
std::vector<float> normals; // optional, stride=3
|
||||
std::vector<uint32_t> indices;
|
||||
};
|
||||
Mesh mesh_obj_parse(const char* obj_text, size_t len); // pure
|
||||
Mesh mesh_obj_load(const char* path); // impure (lee fichero) — vive en mesh_gpu.cpp o aparte
|
||||
|
||||
// --- mesh_gpu (impure) ---
|
||||
struct MeshGpu {
|
||||
GLuint vao = 0, vbo = 0, ebo = 0;
|
||||
int index_count = 0;
|
||||
bool ok() const { return vao != 0; }
|
||||
};
|
||||
MeshGpu mesh_gpu_upload(const Mesh&);
|
||||
void mesh_gpu_destroy(MeshGpu&);
|
||||
|
||||
// --- orbit_camera (puro) ---
|
||||
struct OrbitCamera {
|
||||
float azimuth = 0.7f; // rad
|
||||
float elevation = 0.4f; // rad
|
||||
float distance = 3.0f;
|
||||
float fov = 45.0f; // deg
|
||||
float aspect = 1.0f;
|
||||
float near_plane = 0.05f;
|
||||
float far_plane = 100.0f;
|
||||
};
|
||||
struct CameraMatrices { float view[16]; float proj[16]; };
|
||||
CameraMatrices orbit_camera_matrices(const OrbitCamera&);
|
||||
void orbit_camera_handle_drag(OrbitCamera&, ImVec2 drag_delta, float wheel);
|
||||
|
||||
// --- mesh_viewer (impure) ---
|
||||
struct MeshViewerConfig {
|
||||
const MeshGpu* mesh;
|
||||
OrbitCamera* cam; // se modifica con drag
|
||||
ImVec2 size = {-1, 400};
|
||||
ImU32 color = IM_COL32(180,180,200,255);
|
||||
bool wireframe = false;
|
||||
};
|
||||
void mesh_viewer(const char* id, const MeshViewerConfig&);
|
||||
}
|
||||
```
|
||||
|
||||
`mesh_viewer` internamente:
|
||||
1. Compila/cachea un shader de Lambert minimo (vertex + fragment).
|
||||
2. Tiene un `Framebuffer` propio (cache por `id` + tamaño).
|
||||
3. Cada frame: bind FBO, draw mesh, `ImGui::Image(framebuffer.color_tex)`.
|
||||
4. Si `IsItemActive()` y mouse drag → `orbit_camera_handle_drag`.
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1 — mesh_obj_load (puro)
|
||||
|
||||
- 1.1 Implementar parser que cubre `v`, `vn`, `f` (tris y quads). Ignora `vt`, `mtllib`, materiales en este issue.
|
||||
- 1.2 Generar normales por face si faltan.
|
||||
- 1.3 Tests unitarios con .obj inline (cubo).
|
||||
- 1.4 `.md` con frontmatter.
|
||||
|
||||
### Fase 2 — mesh_gpu (impuro)
|
||||
|
||||
- 2.1 `mesh_gpu_upload`: crea VAO + VBO interleaved (pos+normal) + EBO.
|
||||
- 2.2 `.md` con frontmatter.
|
||||
|
||||
### Fase 3 — orbit_camera (puro)
|
||||
|
||||
- 3.1 Calcular `view = lookAt(eye, target=0, up=Y)` + `proj = perspective`.
|
||||
- 3.2 `handle_drag`: drag.x → azimuth, drag.y → elevation (clamp ±π/2 - eps), wheel → distance (clamp >0).
|
||||
- 3.3 Tests unitarios para matrices (idempotencia drag=0, rango de elevation).
|
||||
- 3.4 `.md`.
|
||||
|
||||
### Fase 4 — mesh_viewer
|
||||
|
||||
- 4.1 Implementar el componente con FBO interno cacheado por `id`.
|
||||
- 4.2 Shader de iluminacion Lambert con luz fija desde la camara.
|
||||
- 4.3 `.md`.
|
||||
|
||||
### Fase 5 — Gallery demo
|
||||
|
||||
- 5.1 `demos_mesh.cpp`: dos sub-demos: cubo procedural (generado in-line) + boton "Load .obj…" con path absoluto en text input.
|
||||
- 5.2 Registrar en gallery.
|
||||
|
||||
### Fase 6 — Tests + docs
|
||||
|
||||
- 6.1 Test parser obj (cubo: 8 vertices, 12 tris).
|
||||
- 6.2 Test orbit_camera (matrices conocidas).
|
||||
- 6.3 `./fn index` + `./fn show` para los nuevos.
|
||||
|
||||
## Ejemplo de uso
|
||||
|
||||
```cpp
|
||||
auto mesh = fn::mesh_obj_load("assets/teapot.obj");
|
||||
auto gpu = fn::mesh_gpu_upload(mesh);
|
||||
fn::OrbitCamera cam;
|
||||
cam.aspect = 4.0f/3.0f;
|
||||
|
||||
fn::run_app("mesh demo", [&]{
|
||||
fn::MeshViewerConfig cfg{};
|
||||
cfg.mesh = &gpu; cfg.cam = &cam;
|
||||
fn::mesh_viewer("##mv", cfg);
|
||||
});
|
||||
```
|
||||
|
||||
## Decisiones de diseño
|
||||
|
||||
- **Sin glm**: matrices a mano (4×4 row-major). Evita dependencia extra; el codigo es ~50 LOC.
|
||||
- **Sin gltf por ahora**: .obj cubre el 80% de casos de inspeccion rapida. gltf en issue futuro si se necesita.
|
||||
- **Iluminacion fija**: Lambert con luz=camara (headlight). Suficiente para inspeccion de geometria.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **Obj con quads o n-gons**: cubrir tris y quads (tris-fan), advertir en .md.
|
||||
- **Modelos enormes**: limite practico 1M tris. Documentar.
|
||||
- **Cache de FBO por id**: si la app cambia el id dinamicamente, fugas. Documentar para reusar `id` estable.
|
||||
@@ -0,0 +1,170 @@
|
||||
# 0030 — C++ audio reactivo (capture + FFT + uniform feed + viz)
|
||||
|
||||
## APP Metadata
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| **ID** | 0030 |
|
||||
| **Estado** | pendiente |
|
||||
| **Prioridad** | media |
|
||||
| **Tipo** | feature — C++ multi-domain (core, gfx, viz) |
|
||||
|
||||
## Dependencias
|
||||
|
||||
`gl_loader_cpp_gfx`, `time_series_buffer_cpp_core`. Independiente de los demas issues.
|
||||
|
||||
**Desbloquea:** shaders audio-reactivos en `shaders_lab`, dashboards en vivo con waveform/spectrum, debugging visual de pipelines de audio.
|
||||
|
||||
---
|
||||
|
||||
## Objetivo
|
||||
|
||||
Cinco primitivos coherentes:
|
||||
|
||||
1. **`audio_capture_cpp_core`** — input mic/loopback con [miniaudio](https://github.com/mackron/miniaudio) (single-header, dominio publico). Lock-free ring buffer.
|
||||
2. **`audio_fft_cpp_core`** — FFT real → magnitudes + smoothing (puro). Tamaño configurable (256/512/1024/2048).
|
||||
3. **`audio_uniform_feed_cpp_gfx`** — sube espectro como `sampler1D` o `texture2D 1×N` al shader activo.
|
||||
4. **`waveform_view_cpp_viz`** — dibuja muestras crudas en un line plot ImPlot.
|
||||
5. **`spectrum_view_cpp_viz`** — dibuja magnitudes FFT en bar plot logaritmico.
|
||||
|
||||
Demo en `primitives_gallery`: input live → fft → vis dual + opcion de bind a un shader fullscreen reactivo.
|
||||
|
||||
## Contexto
|
||||
|
||||
`shaders_lab` no tiene input externo dinamico. Los visuals reaccionan a `u_time` y `u_mouse` solamente. Audio reactivo es uno de los casos mas comunes de uso creativo de shaders y abre demos espectaculares con muy poco codigo añadido.
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
cpp/
|
||||
├── vendor/miniaudio/
|
||||
│ └── miniaudio.h # NEW (~80k LOC, public domain)
|
||||
├── functions/core/
|
||||
│ ├── audio_capture.h/.cpp/.md # NEW (impure)
|
||||
│ └── audio_fft.h/.cpp/.md # NEW (pure)
|
||||
├── functions/gfx/
|
||||
│ └── audio_uniform_feed.h/.cpp/.md # NEW (impure)
|
||||
└── functions/viz/
|
||||
├── waveform_view.h/.cpp/.md # NEW (pure component)
|
||||
└── spectrum_view.h/.cpp/.md # NEW (pure component)
|
||||
cpp/apps/primitives_gallery/
|
||||
├── demos_audio.cpp # NEW
|
||||
├── demos.h # MOD
|
||||
├── main.cpp # MOD
|
||||
└── CMakeLists.txt # MOD
|
||||
cpp/CMakeLists.txt # MOD
|
||||
```
|
||||
|
||||
### API propuesta
|
||||
|
||||
```cpp
|
||||
namespace fn {
|
||||
|
||||
// audio_capture (impure)
|
||||
struct AudioCapture;
|
||||
struct AudioCaptureConfig {
|
||||
int sample_rate = 48000;
|
||||
int channels = 1; // mixdown a mono
|
||||
int buffer_samples = 8192; // ring buffer
|
||||
bool loopback = false; // Win/Linux pulse: capturar output del sistema
|
||||
};
|
||||
AudioCapture* audio_capture_start(const AudioCaptureConfig&);
|
||||
void audio_capture_stop(AudioCapture*);
|
||||
// Drena hasta `out_capacity` muestras float [-1,1]. Retorna count efectivo.
|
||||
int audio_capture_read(AudioCapture*, float* out, int out_capacity);
|
||||
const char* audio_capture_last_error();
|
||||
|
||||
// audio_fft (pure)
|
||||
struct FftResult {
|
||||
std::vector<float> magnitudes; // size = fft_size/2
|
||||
float dc = 0.f;
|
||||
float peak_freq_hz = 0.f;
|
||||
};
|
||||
FftResult audio_fft_compute(const float* samples, int n, int sample_rate, float smoothing = 0.7f, FftResult* prev = nullptr);
|
||||
|
||||
// audio_uniform_feed (impure)
|
||||
struct AudioTexture { GLuint tex = 0; int n = 0; };
|
||||
AudioTexture audio_texture_create(int n);
|
||||
void audio_texture_destroy(AudioTexture&);
|
||||
void audio_texture_upload(AudioTexture&, const float* magnitudes, int n);
|
||||
void audio_texture_bind_uniform(GLuint program, const char* name, const AudioTexture&, int unit);
|
||||
|
||||
// waveform_view (pure component)
|
||||
void waveform_view(const char* id, const float* samples, int n, ImVec2 size = {-1, 100});
|
||||
// spectrum_view (pure component)
|
||||
void spectrum_view(const char* id, const float* magnitudes, int n, int sample_rate, ImVec2 size = {-1, 150});
|
||||
}
|
||||
```
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1 — Vendor
|
||||
|
||||
- 1.1 `cpp/vendor/miniaudio/miniaudio.h` (pin commit). Crear `cpp/vendor/miniaudio/miniaudio_impl.cpp` con `#define MINIAUDIO_IMPLEMENTATION`.
|
||||
- 1.2 Añadir source al CMakeLists.
|
||||
|
||||
### Fase 2 — audio_capture
|
||||
|
||||
- 2.1 Implementar wrapper. Backend default por OS (alsa/pulse en Linux, wasapi en Win). Lock-free ring buffer (single-producer/single-consumer) accumulando muestras del callback.
|
||||
- 2.2 `loopback`: Linux pulse loopback monitor source; Windows wasapi loopback. Documentar limitaciones.
|
||||
- 2.3 `.md` (`kind: function`, `purity: impure`, `error_type`).
|
||||
|
||||
### Fase 3 — audio_fft (puro)
|
||||
|
||||
- 3.1 Usar implementacion FFT minima (Cooley-Tukey radix-2) o vendorear [pffft](https://github.com/marton78/pffft) (BSD). Decidir y documentar.
|
||||
- 3.2 Aplicar Hann window antes de FFT.
|
||||
- 3.3 Smoothing exponencial frame-a-frame con `prev`.
|
||||
- 3.4 Tests con seno puro a frecuencia conocida; `peak_freq_hz` debe coincidir.
|
||||
- 3.5 `.md`.
|
||||
|
||||
### Fase 4 — audio_uniform_feed
|
||||
|
||||
- 4.1 Crear textura 1D (o 2D 1×N) `GL_R32F`. Subida con `glTexSubImage1D`/`2D`.
|
||||
- 4.2 `.md`.
|
||||
|
||||
### Fase 5 — waveform_view + spectrum_view
|
||||
|
||||
- 5.1 `waveform_view`: linea con `ImPlot::PlotLine`. Eje y fijado [-1, 1]. Eje x = sample index.
|
||||
- 5.2 `spectrum_view`: bar plot con eje x logaritmico (Hz), eje y dB (20*log10(magnitude+eps)).
|
||||
- 5.3 `.md` para cada uno.
|
||||
|
||||
### Fase 6 — Gallery demo
|
||||
|
||||
- 6.1 `demos_audio.cpp` con `demo_audio_reactive()`: start capture, fft cada frame, dos vistas (waveform + spectrum), shader fullscreen 256×256 al lado que samplea la `audio_texture` y desplaza colores en funcion del bin medio.
|
||||
- 6.2 Boton start/stop. Mostrar device en uso.
|
||||
|
||||
### Fase 7 — Tests + docs
|
||||
|
||||
- 7.1 Test FFT con seno conocido.
|
||||
- 7.2 Test ring buffer (productor escribe N, consumidor lee, sin perdidas).
|
||||
- 7.3 Smoke test: capture 100ms, asserts `read > 0`.
|
||||
- 7.4 `./fn index` + `./fn show` de los 5.
|
||||
|
||||
## Ejemplo de uso
|
||||
|
||||
```cpp
|
||||
auto* cap = fn::audio_capture_start({});
|
||||
fn::FftResult prev{};
|
||||
|
||||
fn::run_app("audio", [&]{
|
||||
float buf[2048];
|
||||
int n = fn::audio_capture_read(cap, buf, 2048);
|
||||
if (n >= 1024) {
|
||||
prev = fn::audio_fft_compute(buf, 1024, 48000, 0.7f, &prev);
|
||||
fn::waveform_view("##wav", buf, n);
|
||||
fn::spectrum_view("##spec", prev.magnitudes.data(), prev.magnitudes.size(), 48000);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Decisiones de diseño
|
||||
|
||||
- **miniaudio** vs portaudio: miniaudio es header-only, sin build extra; gana por simplicidad de integracion.
|
||||
- **FFT minimo propio o pffft**: empezar con propio (50 LOC) — performance suficiente para 1024 samples a 60Hz. Si hace falta velocidad, swap por pffft.
|
||||
- **`spectrum_view` y `waveform_view` puros**: no tocan el audio, solo dibujan.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **Permisos mic en macOS**: documentar Info.plist NSMicrophoneUsageDescription si se distribuye.
|
||||
- **Loopback no estandar entre OSes**: documentar plataformas soportadas y degradar grácilmente si no esta disponible.
|
||||
- **Latencia**: ring buffer vs callback puede acumular. Documentar tamaño y politica de drop.
|
||||
@@ -0,0 +1,162 @@
|
||||
# 0031 — C++ animation curves (timeline + bezier_editor + tween_curves)
|
||||
|
||||
## APP Metadata
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| **ID** | 0031 |
|
||||
| **Estado** | pendiente |
|
||||
| **Prioridad** | media |
|
||||
| **Tipo** | feature — C++ core (cpp/functions/core) |
|
||||
|
||||
## Dependencias
|
||||
|
||||
`tokens_cpp_core`. Independiente de los demas issues.
|
||||
|
||||
**Desbloquea:** animar uniforms en `shaders_lab` con keyframes; easing reusable en transiciones de UI; control fino sobre animaciones procedurales.
|
||||
|
||||
---
|
||||
|
||||
## Objetivo
|
||||
|
||||
Tres primitivos:
|
||||
|
||||
1. **`tween_curves_cpp_core`** — set de funciones de easing puras (Penner): linear, ease_in/out_quad/cubic/expo, elastic, bounce. Header-only.
|
||||
2. **`bezier_editor_cpp_core`** — editor visual de una curva cubica Bezier (4 puntos de control), evaluacion `f(t)` para t∈[0,1]. Estado puro + render en ImGui canvas.
|
||||
3. **`timeline_cpp_core`** — widget tipo DAW: tracks horizontales con keyframes draggable, scrub, play/pause, evaluacion `track_value_at(time)` con interp lineal o curve por keyframe.
|
||||
|
||||
Demo en `primitives_gallery` con un slider animado por timeline + curva bezier para ease.
|
||||
|
||||
## Contexto
|
||||
|
||||
`shaders_lab` tiene sliders manuales para uniforms. No hay forma de:
|
||||
- Animar un uniform con curva temporal.
|
||||
- Disenar transiciones suaves reusables.
|
||||
|
||||
Las funciones de easing son utiles tambien fuera de animacion (interpolacion de colores, rampas).
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
cpp/functions/core/
|
||||
├── tween_curves.h # NEW (header-only ok)
|
||||
├── tween_curves.cpp # NEW (si hace falta .cpp)
|
||||
├── tween_curves.md # NEW
|
||||
├── bezier_editor.h # NEW
|
||||
├── bezier_editor.cpp # NEW
|
||||
├── bezier_editor.md # NEW
|
||||
├── timeline.h # NEW
|
||||
├── timeline.cpp # NEW
|
||||
└── timeline.md # NEW
|
||||
cpp/apps/primitives_gallery/
|
||||
├── demos_animation.cpp # NEW
|
||||
├── demos.h # MOD
|
||||
├── main.cpp # MOD
|
||||
└── CMakeLists.txt # MOD
|
||||
```
|
||||
|
||||
### API propuesta
|
||||
|
||||
```cpp
|
||||
namespace fn {
|
||||
|
||||
// --- tween_curves (puro, header-only ok) ---
|
||||
namespace tween {
|
||||
inline float linear(float t) { return t; }
|
||||
inline float in_quad(float t) { return t*t; }
|
||||
inline float out_quad(float t) { return 1 - (1-t)*(1-t); }
|
||||
inline float in_out_cubic(float t) { /* Penner */ }
|
||||
inline float in_expo(float t);
|
||||
inline float out_expo(float t);
|
||||
inline float in_elastic(float t);
|
||||
inline float out_elastic(float t);
|
||||
inline float out_bounce(float t);
|
||||
// ... ~15 easing funcs
|
||||
|
||||
enum class Ease { Linear, InQuad, OutQuad, InOutQuad, InCubic, OutCubic, /*...*/ };
|
||||
float apply(Ease e, float t);
|
||||
}
|
||||
|
||||
// --- bezier_editor (puro estado) ---
|
||||
struct BezierCurve { ImVec2 p0{0,0}, p1{0.25f,0.0f}, p2{0.75f,1.0f}, p3{1,1}; };
|
||||
float bezier_eval(const BezierCurve&, float t); // y at x=t
|
||||
bool bezier_editor(const char* id, BezierCurve&, ImVec2 size = {200, 200}); // returns true if changed
|
||||
|
||||
// --- timeline ---
|
||||
struct Keyframe { float time; float value; tween::Ease ease = tween::Ease::Linear; };
|
||||
struct Track { std::string name; std::vector<Keyframe> keys; };
|
||||
struct TimelineState {
|
||||
std::vector<Track> tracks;
|
||||
float current_time = 0.0f;
|
||||
float duration = 5.0f;
|
||||
bool playing = false;
|
||||
};
|
||||
float track_value_at(const Track&, float t); // interp puro
|
||||
void timeline_update(TimelineState&, float dt); // avanza si playing
|
||||
bool timeline_widget(const char* id, TimelineState&, ImVec2 size = {-1, 200}); // returns true if changed
|
||||
}
|
||||
```
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1 — tween_curves
|
||||
|
||||
- 1.1 Implementar las funciones Penner (referencia: easings.net). Header-only inline para que el compilador inline en hot paths.
|
||||
- 1.2 `tween::apply(Ease, t)` con switch.
|
||||
- 1.3 Tests: cada curva en t=0 y t=1 da 0 y 1 respectivamente (excepto elastic/bounce).
|
||||
- 1.4 `.md`.
|
||||
|
||||
### Fase 2 — bezier_editor
|
||||
|
||||
- 2.1 `bezier_eval`: De Casteljau o forma polinomial. Implementacion puramente algebraica.
|
||||
- 2.2 `bezier_editor`: canvas ImGui con 4 puntos draggable (p0/p3 fijos en {0,0}/{1,1} para easing). Dibuja la curva con `AddBezierCubic`.
|
||||
- 2.3 Tests para `bezier_eval`.
|
||||
- 2.4 `.md`.
|
||||
|
||||
### Fase 3 — timeline
|
||||
|
||||
- 3.1 `track_value_at`: encuentra el segmento (k_i, k_{i+1}), normaliza t, aplica `tween::apply(ease)`.
|
||||
- 3.2 `timeline_update`: si `playing`, avanza `current_time += dt`; loop al llegar a duration.
|
||||
- 3.3 `timeline_widget`: barra con tracks horizontales, keyframes como diamantes draggable, scrub time con click en barra superior, botones play/pause. Estilo basado en `tokens`.
|
||||
- 3.4 Tests para `track_value_at` (linear).
|
||||
- 3.5 `.md`.
|
||||
|
||||
### Fase 4 — Gallery demo
|
||||
|
||||
- 4.1 `demos_animation.cpp`:
|
||||
- `demo_tween()`: dropdown de Ease + curva animandose contra el tiempo.
|
||||
- `demo_bezier_editor()`: editor + plot de la curva resultante.
|
||||
- `demo_timeline()`: timeline con 2 tracks ("hue", "amp") + slider que muestra `track_value_at(now)` para cada uno.
|
||||
- 4.2 Registrar en gallery (3 entradas).
|
||||
|
||||
### Fase 5 — Tests + docs
|
||||
|
||||
- 5.1 Tests de easing y track_value_at.
|
||||
- 5.2 `./fn index` + `./fn show` de los 3.
|
||||
|
||||
## Ejemplo de uso
|
||||
|
||||
```cpp
|
||||
fn::TimelineState tl{};
|
||||
tl.tracks.push_back({"hue", {{0, 0}, {2, 1}, {4, 0}}});
|
||||
tl.duration = 4.0f;
|
||||
tl.playing = true;
|
||||
|
||||
fn::run_app("anim", [&](float dt){
|
||||
fn::timeline_update(tl, dt);
|
||||
float h = fn::track_value_at(tl.tracks[0], tl.current_time);
|
||||
ImGui::Text("hue = %.3f", h);
|
||||
fn::timeline_widget("##tl", tl);
|
||||
});
|
||||
```
|
||||
|
||||
## Decisiones de diseño
|
||||
|
||||
- **Tween header-only**: codigo pequeño, alto uso → inline.
|
||||
- **Bezier solo para easing (p0=0, p3=1)** en MVP. Generalizable si hace falta.
|
||||
- **Timeline simple, no jerarquico**: tracks planos. Si hace falta layering en el futuro, otro issue.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **UX del bezier_editor**: precision de drag con mouse en canvas pequeño. Documentar tamaño minimo recomendado (180+).
|
||||
- **Timeline drag race**: keyframe drag debe respetar orden temporal. Reordenar al soltar.
|
||||
@@ -0,0 +1,130 @@
|
||||
# 0032 — C++ sql_workbench
|
||||
|
||||
## APP Metadata
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| **ID** | 0032 |
|
||||
| **Estado** | pendiente |
|
||||
| **Prioridad** | media |
|
||||
| **Tipo** | feature — C++ core (cpp/functions/core) |
|
||||
|
||||
## Dependencias
|
||||
|
||||
`text_editor_cpp_core` (issue 0025) — recomendado pero no bloqueante: si no esta listo, fallback a `ImGui::InputTextMultiline`. `table_view_cpp_viz`, `tokens_cpp_core`. SQLite ya esta en uso por `layout_storage_sqlite`.
|
||||
|
||||
**Desbloquea:** explorar `registry.db` y `operations.db` desde una app C++ sin salir al CLI; debugging visual de assertions/executions.
|
||||
|
||||
---
|
||||
|
||||
## Objetivo
|
||||
|
||||
Componente ImGui que combina:
|
||||
- Editor SQL (con highlight si `text_editor` esta disponible).
|
||||
- Botón "Run" que ejecuta sobre una `sqlite3*` proporcionada por el caller.
|
||||
- Tabla de resultados (`table_view`) con sorting y scrolling.
|
||||
- Lista de tablas/views del schema (sidebar) clickable que insertan `SELECT * FROM <name> LIMIT 100;`.
|
||||
- Historial de queries en memoria + boton "Save" a un archivo `.sql.history`.
|
||||
|
||||
Demo en `primitives_gallery` apuntando a `registry.db` (read-only).
|
||||
|
||||
## Contexto
|
||||
|
||||
Manualmente uso `sqlite3` CLI o DBeaver para inspeccionar `registry.db` mientras desarrollo. Tener un workbench in-app abre la posibilidad de apps C++ que sean dashboards-cum-SQL-explorers contra `operations.db` (entities, executions, assertions).
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
cpp/functions/core/
|
||||
├── sql_workbench.h # NEW
|
||||
├── sql_workbench.cpp # NEW
|
||||
└── sql_workbench.md # NEW
|
||||
cpp/apps/primitives_gallery/
|
||||
├── demos_sql.cpp # NEW
|
||||
├── demos.h # MOD
|
||||
├── main.cpp # MOD
|
||||
└── CMakeLists.txt # MOD
|
||||
```
|
||||
|
||||
### Pure core / impure shell
|
||||
|
||||
`sql_workbench_cpp_core`: `kind: component`, `purity: impure` (ejecuta SQL contra una DB).
|
||||
|
||||
### API propuesta
|
||||
|
||||
```cpp
|
||||
namespace fn {
|
||||
|
||||
struct SqlWorkbenchState {
|
||||
std::string query = "SELECT name FROM sqlite_master WHERE type='table';";
|
||||
std::string last_error;
|
||||
std::vector<std::string> columns;
|
||||
std::vector<std::vector<std::string>> rows;
|
||||
std::vector<std::string> history; // queries ejecutadas
|
||||
bool readonly = false;
|
||||
};
|
||||
|
||||
// db: caller-owned sqlite3*. NO se cierra desde el componente.
|
||||
void sql_workbench(const char* id, sqlite3* db, SqlWorkbenchState& state, ImVec2 size = {-1, -1});
|
||||
}
|
||||
```
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1 — Backend de ejecucion
|
||||
|
||||
- 1.1 Implementar `sql_workbench_run_query(sqlite3*, const char* sql, SqlWorkbenchState&)`: prepara, step, lee columnas y filas como strings. Limpia errores anteriores.
|
||||
- 1.2 Si `state.readonly=true`, rechazar comandos que no sean `SELECT`/`PRAGMA`/`EXPLAIN`.
|
||||
- 1.3 Cap rows a 10000 para no congelar UI; mostrar warning si se trunca.
|
||||
|
||||
### Fase 2 — Schema sidebar
|
||||
|
||||
- 2.1 `sql_workbench_list_schema(sqlite3*) -> {tables: [], views: [], indices: []}`. Query a `sqlite_master`.
|
||||
- 2.2 Sidebar collapsable con tablas. Click → insertar `SELECT * FROM <t> LIMIT 100;` en el editor.
|
||||
|
||||
### Fase 3 — UI
|
||||
|
||||
- 3.1 Layout: panel izquierdo schema (200px), panel derecho dividido vertical (editor arriba, tabla resultado abajo).
|
||||
- 3.2 Boton Run + atajo Ctrl+Enter.
|
||||
- 3.3 Barra inferior: status (`5 rows in 12.3 ms`), error en rojo si hubo.
|
||||
- 3.4 Editor: usar `text_editor` con `CodeLang::SQL` si disponible (compile-time `#ifdef FN_HAS_TEXT_EDITOR` o detectar via include guard).
|
||||
- 3.5 Resultado: usar `table_view_cpp_viz` con sorting por columnas.
|
||||
|
||||
### Fase 4 — Historial
|
||||
|
||||
- 4.1 Cada Run exitoso aparea la query a `state.history`.
|
||||
- 4.2 Popup "History" con las ultimas 50; click → recargar al editor.
|
||||
|
||||
### Fase 5 — Gallery demo
|
||||
|
||||
- 5.1 `demos_sql.cpp` con `demo_sql_workbench()`: abre `registry.db` (path resuelto via env var `FN_REGISTRY_ROOT` o cwd), modo readonly. La sidebar muestra `functions`, `types`, etc.
|
||||
- 5.2 Registrar.
|
||||
|
||||
### Fase 6 — Tests + docs
|
||||
|
||||
- 6.1 Test `sql_workbench_run_query` con DB en memoria: crea tabla, insert, select, verifica columns/rows.
|
||||
- 6.2 Test rechazo de DML en modo readonly.
|
||||
- 6.3 `./fn index` + `./fn show sql_workbench_cpp_core`.
|
||||
|
||||
## Ejemplo de uso
|
||||
|
||||
```cpp
|
||||
sqlite3* db; sqlite3_open_v2("registry.db", &db, SQLITE_OPEN_READONLY, nullptr);
|
||||
fn::SqlWorkbenchState st; st.readonly = true;
|
||||
|
||||
fn::run_app("sql", [&]{
|
||||
fn::sql_workbench("##sql", db, st);
|
||||
});
|
||||
sqlite3_close(db);
|
||||
```
|
||||
|
||||
## Decisiones de diseño
|
||||
|
||||
- **Caller-owned DB**: el workbench no abre/cierra. Permite reusar conexiones del host (un app que ya tiene `operations.db` abierto la pasa).
|
||||
- **Rows como strings**: SQLite ya las da convertibles. La `table_view` espera strings, sin conversion extra.
|
||||
- **Readonly opt-in**: por defecto permite escribir; el caller decide.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **Queries que cuelgan UI**: documentar; en MVP, ejecucion sincrona en main thread. Si bloquea, considerar `std::thread` (ya disponible via `process_runner_cpp_core` patrón).
|
||||
- **Memoria con resultados grandes**: cap a 10000 filas. Documentar.
|
||||
@@ -0,0 +1,144 @@
|
||||
# 0033 — C++ http_inspector + websocket_client
|
||||
|
||||
## APP Metadata
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| **ID** | 0033 |
|
||||
| **Estado** | pendiente |
|
||||
| **Prioridad** | baja |
|
||||
| **Tipo** | feature — C++ core (cpp/functions/core) |
|
||||
|
||||
## Dependencias
|
||||
|
||||
`text_editor_cpp_core` (issue 0025) recomendado para editar bodies; fallback a `InputTextMultiline`. `time_series_buffer_cpp_core`.
|
||||
|
||||
**Desbloquea:** debugging visual de APIs internas y feeds en tiempo real desde C++ apps. Util para apps de monitoring que se conectan a servicios propios.
|
||||
|
||||
---
|
||||
|
||||
## Objetivo
|
||||
|
||||
Dos componentes complementarios:
|
||||
|
||||
1. **`http_inspector_cpp_core`** — panel ImGui tipo Postman minimo: input URL, dropdown method (GET/POST/PUT/DELETE/PATCH), headers (key/value list), body, boton Send. Muestra status, headers de respuesta, body con pretty-print JSON.
|
||||
2. **`websocket_client_cpp_core`** — panel WebSocket: input ws://… , botones Connect/Disconnect, area de mensajes (timeline), input para enviar texto, contador msg/s y plot rate.
|
||||
|
||||
Vendorea [cpp-httplib](https://github.com/yhirose/cpp-httplib) (MIT, header-only) para HTTP. Para WS usa la API client de cpp-httplib (>=0.20 trae soporte ws basico) o vendorea [websocketpp](https://github.com/zaphoyd/websocketpp). Decidir en Fase 1.
|
||||
|
||||
Demo en `primitives_gallery` con un endpoint HTTP de prueba (httpbin.org) y un WS echo (`wss://echo.websocket.events`).
|
||||
|
||||
## Contexto
|
||||
|
||||
Apps C++ que consumen APIs internas (registry_api, deploy_server) suelen requerir scripts curl/Postman aparte para debugging. Tener un inspector embebido reduce friccion.
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
cpp/
|
||||
├── vendor/cpp-httplib/
|
||||
│ └── httplib.h # NEW (header-only, MIT)
|
||||
├── functions/core/
|
||||
│ ├── http_inspector.h/.cpp/.md # NEW (impure component)
|
||||
│ └── websocket_client.h/.cpp/.md # NEW (impure component)
|
||||
└── apps/primitives_gallery/
|
||||
├── demos_net.cpp # NEW
|
||||
├── demos.h # MOD
|
||||
├── main.cpp # MOD
|
||||
└── CMakeLists.txt # MOD
|
||||
cpp/CMakeLists.txt # MOD
|
||||
```
|
||||
|
||||
### API propuesta
|
||||
|
||||
```cpp
|
||||
namespace fn {
|
||||
|
||||
struct HttpHeader { std::string key, value; };
|
||||
struct HttpInspectorState {
|
||||
std::string url = "https://httpbin.org/get";
|
||||
std::string method = "GET";
|
||||
std::vector<HttpHeader> headers;
|
||||
std::string body;
|
||||
int last_status = 0;
|
||||
std::vector<HttpHeader> last_headers;
|
||||
std::string last_body;
|
||||
double last_ms = 0.0;
|
||||
bool in_flight = false; // request en background
|
||||
std::string error;
|
||||
};
|
||||
void http_inspector(const char* id, HttpInspectorState&, ImVec2 size = {-1, -1});
|
||||
|
||||
struct WsMessage { double t; bool incoming; std::string text; };
|
||||
struct WebSocketClientState {
|
||||
std::string url = "wss://echo.websocket.events";
|
||||
bool connected = false;
|
||||
std::string send_buf;
|
||||
std::vector<WsMessage> messages; // capped at 500
|
||||
float rate_msgs_s = 0.f;
|
||||
std::string error;
|
||||
};
|
||||
void websocket_client(const char* id, WebSocketClientState&, ImVec2 size = {-1, -1});
|
||||
}
|
||||
```
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1 — Vendor + decision WS
|
||||
|
||||
- 1.1 Vendorear `httplib.h` (pinear version reciente con TLS opcional).
|
||||
- 1.2 Decidir cliente WS:
|
||||
- Opcion A: cpp-httplib `WebSocketClient` si la version vendoreada lo trae.
|
||||
- Opcion B: vendorear `websocketpp` (header-only-ish, depende de Asio header-only).
|
||||
- 1.3 Documentar la decision en `cpp/vendor/<lib>/README.md`.
|
||||
|
||||
### Fase 2 — http_inspector
|
||||
|
||||
- 2.1 Implementar el componente. Request en background con `std::thread` (ver `process_runner_cpp_core` como patrón).
|
||||
- 2.2 Pretty-print: si `Content-Type: application/json`, formatear con un mini parser/printer JSON (o copiar uno minimal; no necesitamos validacion exhaustiva).
|
||||
- 2.3 `.md` con frontmatter (`kind: component`, `purity: impure`, `error_type`).
|
||||
|
||||
### Fase 3 — websocket_client
|
||||
|
||||
- 3.1 Implementar `connect/disconnect` en thread; lockear cola de mensajes con mutex; copiar a `state.messages` en `websocket_client()` (main thread).
|
||||
- 3.2 Calcular `rate_msgs_s` como contador por segundo con ventana deslizante (puede usar `time_series_buffer_cpp_core`).
|
||||
- 3.3 `.md`.
|
||||
|
||||
### Fase 4 — Gallery demo
|
||||
|
||||
- 4.1 `demos_net.cpp` con `demo_http_inspector()` y `demo_websocket_client()`.
|
||||
- 4.2 Registrar.
|
||||
|
||||
### Fase 5 — Tests + docs
|
||||
|
||||
- 5.1 Test pretty-print JSON sobre payload conocido.
|
||||
- 5.2 Test ratelimit del rate_msgs_s.
|
||||
- 5.3 Smoke test (opcional, requires net): GET a 127.0.0.1 contra un servidor levantado en el test.
|
||||
- 5.4 `./fn index` + `./fn show`.
|
||||
|
||||
## Ejemplo de uso
|
||||
|
||||
```cpp
|
||||
fn::HttpInspectorState http;
|
||||
fn::WebSocketClientState ws;
|
||||
|
||||
fn::run_app("net", [&]{
|
||||
if (ImGui::BeginTabBar("net")) {
|
||||
if (ImGui::BeginTabItem("HTTP")) { fn::http_inspector("##h", http); ImGui::EndTabItem(); }
|
||||
if (ImGui::BeginTabItem("WS")) { fn::websocket_client("##w", ws); ImGui::EndTabItem(); }
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Decisiones de diseño
|
||||
|
||||
- **cpp-httplib** vs libcurl: header-only gana en complejidad de build. TLS via OpenSSL si esta disponible; degradar a HTTP si no.
|
||||
- **Mensajes WS capped**: 500 maximo en buffer para no crecer indefinidamente; descartar oldest.
|
||||
- **Sin authentication helpers**: el header `Authorization` se mete a mano. UX simple.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **TLS**: cpp-httplib requiere OpenSSL para `https://`. Documentar en CMake como opt-in.
|
||||
- **WS reconnect**: no implementar auto-reconnect en MVP. El usuario reconecta manualmente.
|
||||
- **Threading**: cuidado con marshalling de strings entre thread y UI. Mutex obligatorio en mensajes.
|
||||
@@ -0,0 +1,156 @@
|
||||
# 0034 — C++ scientific viz (treemap, sankey, chord, contour, voronoi)
|
||||
|
||||
## APP Metadata
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| **ID** | 0034 |
|
||||
| **Estado** | pendiente |
|
||||
| **Prioridad** | media |
|
||||
| **Tipo** | feature — C++ viz (cpp/functions/viz) |
|
||||
|
||||
## Dependencias
|
||||
|
||||
`tokens_cpp_core`, `plot_theme_cpp_core`. ImPlot ya vendoreado. Sin bloqueos por otros issues.
|
||||
|
||||
**Desbloquea:** dashboards con tipos de visualizacion mas alla de bar/line/scatter — utiles para jerarquias, flujos, relaciones, distribuciones 2D continuas.
|
||||
|
||||
---
|
||||
|
||||
## Objetivo
|
||||
|
||||
Cinco componentes nuevos en `cpp/functions/viz/`, cada uno con su demo en gallery:
|
||||
|
||||
1. **`treemap_cpp_viz`** — squarified treemap (Bruls, Huijbrechts, van Wijk) para jerarquias planas con valores. Click hace zoom (opcional MVP: solo render).
|
||||
2. **`sankey_cpp_viz`** — sankey diagram para flujos `source → target` con magnitudes.
|
||||
3. **`chord_cpp_viz`** — chord diagram para matrices de relaciones N×N (ej: flujos entre N entidades).
|
||||
4. **`contour_cpp_viz`** — contour plot de un grid 2D escalar (marching squares con N niveles).
|
||||
5. **`voronoi_cpp_viz`** — diagrama de Voronoi (Fortune o aproximacion via ImGui draw): regiones coloreadas a partir de N seeds.
|
||||
|
||||
Todos los layouts/algoritmos deben ser **puros**; el render usa primitivas ImGui DrawList (`AddTriangleFilled`, `AddBezierQuadratic`, etc.).
|
||||
|
||||
## Contexto
|
||||
|
||||
`cpp/functions/viz/` cubre charts standard (bar/line/scatter/heatmap/histogram/pie/candlestick/sparkline + table + graph). Faltan las visualizaciones "narrativas" tipicas de dashboards mas avanzados.
|
||||
|
||||
Implementar 5 a la vez es viable porque comparten la misma estructura: `compute_layout` puro + `render` con DrawList.
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
cpp/functions/viz/
|
||||
├── treemap.h/.cpp/.md # NEW
|
||||
├── sankey.h/.cpp/.md # NEW
|
||||
├── chord.h/.cpp/.md # NEW
|
||||
├── contour.h/.cpp/.md # NEW
|
||||
└── voronoi.h/.cpp/.md # NEW
|
||||
cpp/apps/primitives_gallery/
|
||||
├── demos_scientific.cpp # NEW (5 demos en un archivo)
|
||||
├── demos.h # MOD
|
||||
├── main.cpp # MOD
|
||||
└── CMakeLists.txt # MOD
|
||||
```
|
||||
|
||||
### API propuesta
|
||||
|
||||
```cpp
|
||||
namespace fn {
|
||||
|
||||
// --- treemap ---
|
||||
struct TreemapItem { std::string label; float value; ImU32 color; };
|
||||
struct TreemapRect { ImVec2 min, max; const TreemapItem* item; };
|
||||
std::vector<TreemapRect> treemap_layout(const std::vector<TreemapItem>&, ImVec2 region); // pure
|
||||
void treemap(const char* id, const std::vector<TreemapItem>&, ImVec2 size = {-1, 300});
|
||||
|
||||
// --- sankey ---
|
||||
struct SankeyNode { std::string label; };
|
||||
struct SankeyLink { int src, dst; float value; };
|
||||
void sankey(const char* id,
|
||||
const std::vector<SankeyNode>&, const std::vector<SankeyLink>&,
|
||||
ImVec2 size = {-1, 400});
|
||||
|
||||
// --- chord ---
|
||||
void chord(const char* id,
|
||||
const float* matrix, int n, const char* const* labels,
|
||||
ImVec2 size = {400, 400});
|
||||
|
||||
// --- contour ---
|
||||
struct ContourLine { std::vector<ImVec2> pts; float level; };
|
||||
std::vector<ContourLine> contour_compute(const float* grid, int nx, int ny, const float* levels, int n_levels); // pure (marching squares)
|
||||
void contour(const char* id, const float* grid, int nx, int ny, const float* levels, int n_levels,
|
||||
ImVec2 size = {-1, 300});
|
||||
|
||||
// --- voronoi ---
|
||||
struct VoronoiCell { std::vector<ImVec2> polygon; ImVec2 seed; ImU32 color; };
|
||||
std::vector<VoronoiCell> voronoi_layout(const ImVec2* seeds, int n, ImVec2 region); // pure
|
||||
void voronoi(const char* id, const ImVec2* seeds, int n, const ImU32* colors, ImVec2 size = {-1, 300});
|
||||
}
|
||||
```
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1 — treemap
|
||||
|
||||
- 1.1 Implementar squarified algorithm (recursivo, O(n log n)).
|
||||
- 1.2 Render con `AddRectFilled` + `AddText` (label si cabe).
|
||||
- 1.3 Tests con 5 items conocidos.
|
||||
- 1.4 `.md`.
|
||||
|
||||
### Fase 2 — sankey
|
||||
|
||||
- 2.1 Asignar nodos a columnas via topological levels (BFS desde sources).
|
||||
- 2.2 Stack vertical de cada columna por valor; calcular Y offset por link.
|
||||
- 2.3 Render: rectangulos para nodos + bezier cubic para links.
|
||||
- 2.4 `.md`.
|
||||
|
||||
### Fase 3 — chord
|
||||
|
||||
- 3.1 Calcular angulos por nodo proporcionales a `sum(matrix[i,*])`.
|
||||
- 3.2 Arcos en el borde del circulo + bezier interno entre arcos.
|
||||
- 3.3 `.md`.
|
||||
|
||||
### Fase 4 — contour
|
||||
|
||||
- 4.1 `contour_compute` con marching squares clasico (16 casos), interp lineal entre celdas.
|
||||
- 4.2 Render: `AddPolyline` por cada level, color por level (gradiente).
|
||||
- 4.3 Tests con grid conocido (gaussiana → contornos circulares).
|
||||
- 4.4 `.md`.
|
||||
|
||||
### Fase 5 — voronoi
|
||||
|
||||
- 5.1 MVP: implementacion brute-force (para cada pixel, encontrar seed mas cercana). Para N pequeño (<200) y region <500², es < 1ms.
|
||||
- 5.2 Para layout poligonal: usar half-plane intersections (mas complejo). MVP puede renderizar como raster a una textura cacheada.
|
||||
- 5.3 `.md`.
|
||||
|
||||
### Fase 6 — Gallery demos
|
||||
|
||||
- 6.1 `demos_scientific.cpp` con 5 funciones, una por chart, datos sinteticos coherentes (treemap: gastos por categoria; sankey: flujos cliente→producto; chord: relaciones entre paises; contour: gaussiana mezcla; voronoi: 30 seeds aleatorios).
|
||||
- 6.2 Registrar en gallery.
|
||||
|
||||
### Fase 7 — Tests + docs
|
||||
|
||||
- 7.1 Tests por componente (algorithm puro, no render).
|
||||
- 7.2 `./fn index` + `./fn show` para los 5.
|
||||
|
||||
## Ejemplo de uso
|
||||
|
||||
```cpp
|
||||
std::vector<fn::TreemapItem> items = {
|
||||
{"comida", 320, IM_COL32(120,180,200,255)},
|
||||
{"vivienda", 950, IM_COL32(180,120,200,255)},
|
||||
{"transporte", 180, IM_COL32(200,180,120,255)},
|
||||
};
|
||||
fn::treemap("##gastos", items);
|
||||
```
|
||||
|
||||
## Decisiones de diseño
|
||||
|
||||
- **Render con DrawList** (no ImPlot) para chart types que ImPlot no soporta nativo.
|
||||
- **Layout puro separado del render**: facilita tests y reuso (treemap → exportar a SVG futuro).
|
||||
- **Voronoi raster en MVP**: simple, suficiente. Si hace falta poligonos, otro issue.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **Tamaño del issue**: 5 charts es mucho. Mitigacion: comparten patron y un archivo `demos_scientific.cpp`. Si un agente no termina, sub-issue por chart.
|
||||
- **Voronoi performance**: documentar limite practico N <= 200.
|
||||
- **Sankey con grafo ciclico**: assumir DAG; documentar.
|
||||
@@ -0,0 +1,122 @@
|
||||
# 0035 — C++ map_tiles (slippy map OSM)
|
||||
|
||||
## APP Metadata
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| **ID** | 0035 |
|
||||
| **Estado** | pendiente |
|
||||
| **Prioridad** | baja |
|
||||
| **Tipo** | feature — C++ viz (cpp/functions/viz) |
|
||||
|
||||
## Dependencias
|
||||
|
||||
`gl_texture_load_cpp_gfx` (issue 0026), `gl_loader_cpp_gfx`. Vendor: `cpp-httplib` (si ya esta vendoreado por 0033) o reusar.
|
||||
|
||||
**Desbloquea:** apps GIS minimas, dashboards con localizacion (entities con lat/lon), debugging de scrapers geograficos.
|
||||
|
||||
---
|
||||
|
||||
## Objetivo
|
||||
|
||||
Componente ImGui que renderiza un slippy map estilo Leaflet sobre OpenStreetMap tiles, con pan/zoom y soporte para markers + polylines.
|
||||
|
||||
## Contexto
|
||||
|
||||
Hay datos geograficos en varios proyectos (registry de proveedores, viajes, infrastructura). No hay forma de visualizarlos en C++; se exporta a una notebook con folium o se monta un frontend React.
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
cpp/functions/viz/
|
||||
├── map_tiles.h # NEW
|
||||
├── map_tiles.cpp # NEW
|
||||
└── map_tiles.md # NEW (impure)
|
||||
cpp/apps/primitives_gallery/
|
||||
├── demos_map.cpp # NEW
|
||||
├── demos.h # MOD
|
||||
├── main.cpp # MOD
|
||||
└── CMakeLists.txt # MOD
|
||||
```
|
||||
|
||||
### API propuesta
|
||||
|
||||
```cpp
|
||||
namespace fn {
|
||||
|
||||
struct MapMarker { double lat, lon; ImU32 color; const char* label; };
|
||||
struct MapPolyline { std::vector<ImVec2> latlon; ImU32 color; float thickness = 2.f; }; // ImVec2 = (lat, lon)
|
||||
struct MapState {
|
||||
double lat = 40.4168; // Madrid default
|
||||
double lon = -3.7038;
|
||||
int zoom = 6; // 0..18
|
||||
std::string tile_url = "https://tile.openstreetmap.org/{z}/{x}/{y}.png";
|
||||
std::string user_agent = "fn-registry-map/0.1 (egutierrez@aurgi.com)";
|
||||
bool show_attribution = true; // OSM atribucion obligatoria
|
||||
};
|
||||
|
||||
void map_tiles(const char* id, MapState&,
|
||||
const MapMarker* markers, int n_markers,
|
||||
const MapPolyline* polylines, int n_polylines,
|
||||
ImVec2 size = {-1, -1});
|
||||
}
|
||||
```
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1 — Tile cache
|
||||
|
||||
- 1.1 Cache en disco bajo `~/.cache/fn-registry/tiles/{z}/{x}/{y}.png`. Crear directorio si no existe.
|
||||
- 1.2 Cache en RAM (LRU) de `GlTexture` ya subidas, max 256 entradas.
|
||||
- 1.3 Funcion `tile_get_or_fetch(z, x, y)`: si en RAM → return; si en disco → load (gl_texture_load); si no → spawn thread que descarga via cpp-httplib y guarda en disco. Mientras tanto, devolver placeholder.
|
||||
|
||||
### Fase 2 — Proyeccion + interaction
|
||||
|
||||
- 2.1 Funciones puras `lonlat_to_xy(lat, lon, zoom) -> (px, py)` y `xy_to_lonlat`.
|
||||
- 2.2 `map_tiles_handle_input`: drag pan, wheel zoom (clamp 0-18), mantener punto bajo cursor durante zoom.
|
||||
|
||||
### Fase 3 — Render
|
||||
|
||||
- 3.1 Calcular tiles visibles dado el viewport y zoom.
|
||||
- 3.2 Para cada tile visible: `ImGui::Image(texture, screen_rect)` o `AddImage` en el DrawList.
|
||||
- 3.3 Markers: `AddCircleFilled` + texto.
|
||||
- 3.4 Polylines: convertir lat/lon a screen y `AddPolyline`.
|
||||
- 3.5 Atribucion en esquina inferior derecha: `"© OpenStreetMap contributors"`.
|
||||
|
||||
### Fase 4 — User-Agent + ToS
|
||||
|
||||
- 4.1 Enviar `User-Agent` custom (OSM lo exige). Documentar en `.md` que el usuario debe poner su contacto.
|
||||
- 4.2 Cap a 2 requests/segundo simultaneos para no abusar del tile server.
|
||||
|
||||
### Fase 5 — Gallery demo
|
||||
|
||||
- 5.1 `demos_map.cpp` con `demo_map_tiles()`: mapa centrado en Espana, 5 markers en ciudades, polyline conectandolas.
|
||||
- 5.2 Registrar.
|
||||
|
||||
### Fase 6 — Tests + docs
|
||||
|
||||
- 6.1 Tests puros de proyeccion (Madrid → conocidos pixels a zoom 10).
|
||||
- 6.2 `./fn index` + `./fn show map_tiles_cpp_viz`.
|
||||
|
||||
## Ejemplo de uso
|
||||
|
||||
```cpp
|
||||
fn::MapState m;
|
||||
fn::MapMarker markers[] = {
|
||||
{40.4168, -3.7038, IM_COL32(255,80,80,255), "Madrid"},
|
||||
{41.3851, 2.1734, IM_COL32(80,150,255,255), "Barcelona"},
|
||||
};
|
||||
fn::map_tiles("##m", m, markers, 2, nullptr, 0);
|
||||
```
|
||||
|
||||
## Decisiones de diseño
|
||||
|
||||
- **OSM tile server por defecto**: gratis, sin API key. Documentar limite y alternativa (carto, stamen) cambiando `tile_url`.
|
||||
- **Cache en `~/.cache/fn-registry/tiles/`**: respeta XDG; persistente entre runs.
|
||||
- **Sin GeoJSON parsing en MVP**: solo markers/polylines via API. GeoJSON en issue futuro.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **Bloqueo de IP por OSM**: documentar terminos de uso (max 1 req/s recomendado, User-Agent unico).
|
||||
- **Tiles a alta latitud**: documentar que la proyeccion Web Mercator distorsiona >85°.
|
||||
- **Threading de descargas**: cuidado con marshalling de texturas GL (deben crearse en hilo con contexto). Patron: descargar PNG bytes en thread, crear GL texture en main thread.
|
||||
@@ -0,0 +1,144 @@
|
||||
# 0036 — C++ image_canvas + webcam_texture
|
||||
|
||||
## APP Metadata
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| **ID** | 0036 |
|
||||
| **Estado** | pendiente |
|
||||
| **Prioridad** | baja |
|
||||
| **Tipo** | feature — C++ core/gfx (cpp/functions/core, cpp/functions/gfx) |
|
||||
|
||||
## Dependencias
|
||||
|
||||
`gl_texture_load_cpp_gfx` (issue 0026) — recomendado. `tokens_cpp_core`. Independiente de los demas.
|
||||
|
||||
**Desbloquea:** UIs para etiquetado/anotacion de imagenes (CV, OCR debugging) y entrada de webcam para `shaders_lab` reactivo a video.
|
||||
|
||||
---
|
||||
|
||||
## Objetivo
|
||||
|
||||
Dos primitivos:
|
||||
|
||||
1. **`image_canvas_cpp_core`** — viewer de imagen con pan/zoom + capa de anotaciones (rectangulos, puntos, polilineas, texto). Drag para crear nuevas anotaciones; click para seleccionar; tecla Del para borrar.
|
||||
2. **`webcam_texture_cpp_gfx`** — captura de webcam (V4L2 en Linux, Media Foundation en Windows) → GL texture actualizada cada frame. API parejo a `audio_capture`.
|
||||
|
||||
## Contexto
|
||||
|
||||
Casos de uso identificados:
|
||||
- Inspeccionar resultados de un detector (rectangulos overlay).
|
||||
- Etiquetar manualmente datasets pequeños in-app.
|
||||
- Pasar webcam a shaders en `shaders_lab` (filtros video en tiempo real).
|
||||
|
||||
Ningun primitivo del registry actual cubre estos casos.
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
cpp/functions/core/
|
||||
├── image_canvas.h # NEW
|
||||
├── image_canvas.cpp # NEW
|
||||
└── image_canvas.md # NEW (impure component)
|
||||
cpp/functions/gfx/
|
||||
├── webcam_texture.h # NEW
|
||||
├── webcam_texture.cpp # NEW
|
||||
└── webcam_texture.md # NEW (impure)
|
||||
cpp/apps/primitives_gallery/
|
||||
├── demos_image_webcam.cpp # NEW
|
||||
├── demos.h # MOD
|
||||
├── main.cpp # MOD
|
||||
└── CMakeLists.txt # MOD
|
||||
```
|
||||
|
||||
### API propuesta
|
||||
|
||||
```cpp
|
||||
namespace fn {
|
||||
|
||||
// --- image_canvas ---
|
||||
struct Annotation {
|
||||
enum Kind { Rect, Point, Polyline, Text } kind;
|
||||
std::vector<ImVec2> points; // image-space coords (no screen)
|
||||
ImU32 color = IM_COL32(255, 80, 80, 255);
|
||||
std::string label;
|
||||
};
|
||||
struct ImageCanvasState {
|
||||
GLuint texture = 0; // caller-owned
|
||||
int img_w = 0, img_h = 0;
|
||||
float zoom = 1.f;
|
||||
ImVec2 pan = {0, 0};
|
||||
std::vector<Annotation> annotations;
|
||||
int selected = -1;
|
||||
enum Tool { ToolNone, ToolRect, ToolPoint, ToolPolyline } tool = ToolNone;
|
||||
};
|
||||
void image_canvas(const char* id, ImageCanvasState&, ImVec2 size = {-1, -1});
|
||||
|
||||
// --- webcam_texture ---
|
||||
struct WebcamTextureConfig { int width = 640; int height = 480; int fps = 30; int device = 0; };
|
||||
struct WebcamTexture { GLuint id = 0; int w = 0; int h = 0; bool ok() const { return id != 0; } };
|
||||
WebcamTexture webcam_texture_start(const WebcamTextureConfig&);
|
||||
void webcam_texture_stop(WebcamTexture&);
|
||||
bool webcam_texture_update(WebcamTexture&); // copia ultimo frame a GPU; true si hubo nuevo
|
||||
const char* webcam_texture_last_error();
|
||||
}
|
||||
```
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1 — image_canvas
|
||||
|
||||
- 1.1 Render: `ImGui::Image(texture, ...)` con transform por pan/zoom. Drag con MMB para pan, wheel para zoom.
|
||||
- 1.2 Tool == Rect: drag con LMB en image-space crea anotacion `Rect` con 2 puntos.
|
||||
- 1.3 Tool == Point: click LMB anade un Point.
|
||||
- 1.4 Tool == Polyline: click anade puntos; doble-click cierra.
|
||||
- 1.5 Hit-test para selection; Del key elimina la seleccionada.
|
||||
- 1.6 `.md`.
|
||||
|
||||
### Fase 2 — webcam_texture (Linux V4L2)
|
||||
|
||||
- 2.1 Abrir `/dev/video<device>`, MMAP buffers, VIDIOC_STREAMON.
|
||||
- 2.2 En `update`, dequeue buffer, convert YUYV → RGBA (CPU), upload a GL texture, requeue.
|
||||
- 2.3 `.md` (`kind: function`, `purity: impure`, `error_type`).
|
||||
|
||||
### Fase 3 — webcam_texture (Windows Media Foundation, opcional)
|
||||
|
||||
- 3.1 Si `_WIN32`, usar `IMFSourceReader`. Convert NV12 → RGBA.
|
||||
- 3.2 Documentar que macOS no se soporta en MVP.
|
||||
|
||||
### Fase 4 — Gallery demo
|
||||
|
||||
- 4.1 `demos_image_webcam.cpp`:
|
||||
- `demo_image_canvas()`: carga `assets/sample.png` (si existe — fallback a rendered placeholder), permite pintar rectangulos.
|
||||
- `demo_webcam_texture()`: start cam, mostrar preview + sliders RGB sobre un shader fullscreen que samplea la cam.
|
||||
- 4.2 Registrar.
|
||||
|
||||
### Fase 5 — Tests + docs
|
||||
|
||||
- 5.1 Tests puros de transform image-space ↔ screen-space.
|
||||
- 5.2 Smoke test webcam (skip si no hay /dev/video0).
|
||||
- 5.3 `./fn index`.
|
||||
|
||||
## Ejemplo de uso
|
||||
|
||||
```cpp
|
||||
auto cam = fn::webcam_texture_start({});
|
||||
if (!cam.ok()) std::fprintf(stderr, "%s\n", fn::webcam_texture_last_error());
|
||||
|
||||
fn::run_app("cam", [&]{
|
||||
fn::webcam_texture_update(cam);
|
||||
ImGui::Image((ImTextureID)(intptr_t)cam.id, {cam.w*1.f, cam.h*1.f});
|
||||
});
|
||||
```
|
||||
|
||||
## Decisiones de diseño
|
||||
|
||||
- **Anotaciones en image-space**: pan/zoom no afectan coordenadas guardadas. Permite serializar a JSON (issue futuro).
|
||||
- **YUYV → RGBA en CPU**: simple, ~5ms/frame a 640×480. Si hace falta, hacer un compute shader (issue 0027).
|
||||
- **macOS sin webcam por ahora**: AVFoundation requiere ObjC++; no compensa en MVP.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **Permisos webcam**: documentar troubleshooting (`/dev/video0` permisos en Linux).
|
||||
- **Driver V4L2 raro**: algunos formatos de pixel no son YUYV. Documentar fallback a MJPEG (con stb_image decode) o error claro.
|
||||
- **`ImTextureID` semantics** entre backends: documentar.
|
||||
@@ -30,3 +30,15 @@
|
||||
| [0022](completed/0022-init-pipelines.md) | Init Pipelines (scaffolding) | completado | alta | feature | — |
|
||||
| [0023](completed/0023-testing-utils.md) | Testing Utilities Go | completado | media | feature | — |
|
||||
| [0024](completed/0024-dashboard-yaml-split-por-tab.md) | auto_metabase: split dashboard YAMLs por tab | completado | alta | mejora | — |
|
||||
| [0025](0025-cpp-text-editor-file-watcher.md) | C++ text_editor + file_watcher | pendiente | alta | feature | — |
|
||||
| [0026](0026-cpp-gl-texture-load.md) | C++ gl_texture_load (stb_image → sampler2D) | pendiente | alta | feature | 0035, 0036 |
|
||||
| [0027](0027-cpp-gl-compute-pingpong.md) | C++ gl_compute_shader + gl_pingpong_fbo + DAG Compute | pendiente | alta | feature | — |
|
||||
| [0028](0028-cpp-implot3d-3d-viz.md) | C++ ImPlot3D + surface_plot_3d + scatter_3d | pendiente | media | feature | — |
|
||||
| [0029](0029-cpp-mesh-viewer.md) | C++ mesh_viewer + obj loader + orbit_camera | pendiente | media | feature | — |
|
||||
| [0030](0030-cpp-audio-reactive.md) | C++ audio reactivo (capture + fft + uniform feed + viz) | pendiente | media | feature | — |
|
||||
| [0031](0031-cpp-animation-curves.md) | C++ animation curves (timeline + bezier_editor + tween) | pendiente | media | feature | — |
|
||||
| [0032](0032-cpp-sql-workbench.md) | C++ sql_workbench | pendiente | media | feature | — |
|
||||
| [0033](0033-cpp-http-ws-inspector.md) | C++ http_inspector + websocket_client | pendiente | baja | feature | — |
|
||||
| [0034](0034-cpp-scientific-viz.md) | C++ scientific viz (treemap, sankey, chord, contour, voronoi) | pendiente | media | feature | — |
|
||||
| [0035](0035-cpp-map-tiles.md) | C++ map_tiles (slippy map OSM) | pendiente | baja | feature | — |
|
||||
| [0036](0036-cpp-image-canvas-webcam.md) | C++ image_canvas + webcam_texture | pendiente | baja | feature | — |
|
||||
|
||||
Reference in New Issue
Block a user