feat: add C++ support with ImGui/ImPlot framework and vendor submodules
Añade soporte C++ al registry: vendor submodules (glfw, imgui, implot, tracy), sistema de build con CMake y toolchains cross-platform, runner C++ en fn CLI, parser de tests Google Test, y funciones bash para build Linux/Windows. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+13
@@ -0,0 +1,13 @@
|
|||||||
|
[submodule "cpp/vendor/imgui"]
|
||||||
|
path = cpp/vendor/imgui
|
||||||
|
url = https://github.com/ocornut/imgui.git
|
||||||
|
branch = docking
|
||||||
|
[submodule "cpp/vendor/implot"]
|
||||||
|
path = cpp/vendor/implot
|
||||||
|
url = https://github.com/epezent/implot.git
|
||||||
|
[submodule "cpp/vendor/tracy"]
|
||||||
|
path = cpp/vendor/tracy
|
||||||
|
url = https://github.com/wolfpld/tracy.git
|
||||||
|
[submodule "/home/lucas/fn_registry/cpp/vendor/glfw"]
|
||||||
|
path = /home/lucas/fn_registry/cpp/vendor/glfw
|
||||||
|
url = https://github.com/glfw/glfw.git
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
name: build_cpp_linux
|
||||||
|
kind: function
|
||||||
|
lang: bash
|
||||||
|
domain: infra
|
||||||
|
version: "1.0.0"
|
||||||
|
purity: impure
|
||||||
|
signature: "build_cpp_linux(target?: string) -> void"
|
||||||
|
description: "Compila las funciones y apps C++ del registry para Linux nativo usando cmake"
|
||||||
|
tags: [cpp, build, cmake, linux, imgui]
|
||||||
|
uses_functions: []
|
||||||
|
uses_types: []
|
||||||
|
returns: []
|
||||||
|
returns_optional: false
|
||||||
|
error_type: "error_go_core"
|
||||||
|
imports: []
|
||||||
|
tested: false
|
||||||
|
tests: []
|
||||||
|
test_file_path: ""
|
||||||
|
file_path: "bash/functions/infra/build_cpp_linux.sh"
|
||||||
|
params:
|
||||||
|
- name: target
|
||||||
|
desc: "Nombre del target cmake a compilar (opcional, sin argumento compila todo)"
|
||||||
|
output: "Compila los binarios en cpp/build/linux/"
|
||||||
|
---
|
||||||
|
|
||||||
|
# build_cpp_linux
|
||||||
|
|
||||||
|
Configura y compila el proyecto C++ (ImGui/ImPlot) para Linux nativo.
|
||||||
|
|
||||||
|
Usa cmake con compilacion paralela (`-j$(nproc)`). Si no se ha configurado antes, ejecuta `cmake -B` automaticamente.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
fn run build_cpp_linux # Compilar todo
|
||||||
|
fn run build_cpp_linux chart_demo # Compilar solo chart_demo
|
||||||
|
```
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REGISTRY_ROOT="${FN_REGISTRY_ROOT:-$(cd "$(dirname "$0")/../../.." && pwd)}"
|
||||||
|
CPP_ROOT="$REGISTRY_ROOT/cpp"
|
||||||
|
BUILD_DIR="$CPP_ROOT/build/linux"
|
||||||
|
TARGET="${1:-}"
|
||||||
|
|
||||||
|
# Configure if needed
|
||||||
|
if [ ! -f "$BUILD_DIR/CMakeCache.txt" ]; then
|
||||||
|
echo "[build_cpp_linux] Configuring cmake..."
|
||||||
|
cmake -B "$BUILD_DIR" -S "$CPP_ROOT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build
|
||||||
|
if [ -n "$TARGET" ]; then
|
||||||
|
echo "[build_cpp_linux] Building target: $TARGET"
|
||||||
|
cmake --build "$BUILD_DIR" --target "$TARGET" -- -j"$(nproc)"
|
||||||
|
else
|
||||||
|
echo "[build_cpp_linux] Building all targets..."
|
||||||
|
cmake --build "$BUILD_DIR" -- -j"$(nproc)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[build_cpp_linux] Done. Binaries in $BUILD_DIR"
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
name: build_cpp_windows
|
||||||
|
kind: function
|
||||||
|
lang: bash
|
||||||
|
domain: infra
|
||||||
|
version: "1.0.0"
|
||||||
|
purity: impure
|
||||||
|
signature: "build_cpp_windows(target?: string) -> void"
|
||||||
|
description: "Cross-compila las funciones y apps C++ del registry para Windows usando mingw-w64"
|
||||||
|
tags: [cpp, build, cmake, windows, cross-compile, mingw, imgui]
|
||||||
|
uses_functions: []
|
||||||
|
uses_types: []
|
||||||
|
returns: []
|
||||||
|
returns_optional: false
|
||||||
|
error_type: "error_go_core"
|
||||||
|
imports: []
|
||||||
|
tested: false
|
||||||
|
tests: []
|
||||||
|
test_file_path: ""
|
||||||
|
file_path: "bash/functions/infra/build_cpp_windows.sh"
|
||||||
|
params:
|
||||||
|
- name: target
|
||||||
|
desc: "Nombre del target cmake a compilar (opcional, sin argumento compila todo)"
|
||||||
|
output: "Produce binarios .exe de Windows en cpp/build/windows/"
|
||||||
|
---
|
||||||
|
|
||||||
|
# build_cpp_windows
|
||||||
|
|
||||||
|
Cross-compila el proyecto C++ para Windows desde Linux usando el toolchain mingw-w64.
|
||||||
|
|
||||||
|
Los .exe resultantes incluyen runtime linkado estaticamente (self-contained).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
fn run build_cpp_windows # Compilar todo
|
||||||
|
fn run build_cpp_windows chart_demo # Compilar solo chart_demo
|
||||||
|
```
|
||||||
|
|
||||||
|
Requiere `mingw-w64`: `sudo apt install mingw-w64`
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REGISTRY_ROOT="${FN_REGISTRY_ROOT:-$(cd "$(dirname "$0")/../../.." && pwd)}"
|
||||||
|
CPP_ROOT="$REGISTRY_ROOT/cpp"
|
||||||
|
BUILD_DIR="$CPP_ROOT/build/windows"
|
||||||
|
TOOLCHAIN="$CPP_ROOT/toolchains/mingw-w64.cmake"
|
||||||
|
TARGET="${1:-}"
|
||||||
|
|
||||||
|
# Check mingw is available
|
||||||
|
if ! command -v x86_64-w64-mingw32-g++ &>/dev/null; then
|
||||||
|
echo "[build_cpp_windows] Error: mingw-w64 not found. Install with: sudo apt install mingw-w64"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Configure if needed
|
||||||
|
if [ ! -f "$BUILD_DIR/CMakeCache.txt" ]; then
|
||||||
|
echo "[build_cpp_windows] Configuring cmake with mingw-w64 toolchain..."
|
||||||
|
cmake -B "$BUILD_DIR" -S "$CPP_ROOT" -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build
|
||||||
|
if [ -n "$TARGET" ]; then
|
||||||
|
echo "[build_cpp_windows] Cross-compiling target: $TARGET"
|
||||||
|
cmake --build "$BUILD_DIR" --target "$TARGET" -- -j"$(nproc)"
|
||||||
|
else
|
||||||
|
echo "[build_cpp_windows] Cross-compiling all targets..."
|
||||||
|
cmake --build "$BUILD_DIR" -- -j"$(nproc)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[build_cpp_windows] Done. Windows binaries in $BUILD_DIR"
|
||||||
|
if [ -n "$TARGET" ]; then
|
||||||
|
file "$BUILD_DIR"/**/"$TARGET".exe 2>/dev/null || file "$BUILD_DIR/$TARGET".exe 2>/dev/null || true
|
||||||
|
fi
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
name: install_cpp_deps
|
||||||
|
kind: function
|
||||||
|
lang: bash
|
||||||
|
domain: infra
|
||||||
|
version: "1.0.0"
|
||||||
|
purity: impure
|
||||||
|
signature: "install_cpp_deps() -> void"
|
||||||
|
description: "Verifica e instala las dependencias de sistema necesarias para compilar C++ con ImGui (cmake, g++, glfw, mesa)"
|
||||||
|
tags: [cpp, dependencies, setup, cmake, imgui]
|
||||||
|
uses_functions: []
|
||||||
|
uses_types: []
|
||||||
|
returns: []
|
||||||
|
returns_optional: false
|
||||||
|
error_type: "error_go_core"
|
||||||
|
imports: []
|
||||||
|
tested: false
|
||||||
|
tests: []
|
||||||
|
test_file_path: ""
|
||||||
|
file_path: "bash/functions/infra/install_cpp_deps.sh"
|
||||||
|
params: []
|
||||||
|
output: "Instala paquetes faltantes via apt o confirma que todo esta instalado"
|
||||||
|
---
|
||||||
|
|
||||||
|
# install_cpp_deps
|
||||||
|
|
||||||
|
Verifica las dependencias necesarias para el build C++:
|
||||||
|
- `cmake` — sistema de build
|
||||||
|
- `g++` / `build-essential` — compilador
|
||||||
|
- `libglfw3-dev` — windowing (GLFW)
|
||||||
|
- `libgl1-mesa-dev` — OpenGL headers
|
||||||
|
|
||||||
|
Tambien reporta si `mingw-w64` esta disponible para cross-compile a Windows.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
fn run install_cpp_deps
|
||||||
|
```
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "[install_cpp_deps] Checking C++ build dependencies..."
|
||||||
|
|
||||||
|
MISSING=()
|
||||||
|
|
||||||
|
if ! command -v cmake &>/dev/null; then
|
||||||
|
MISSING+=(cmake)
|
||||||
|
else
|
||||||
|
echo " cmake: $(cmake --version | head -1)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v g++ &>/dev/null; then
|
||||||
|
MISSING+=(g++ build-essential)
|
||||||
|
else
|
||||||
|
echo " g++: $(g++ --version | head -1)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! dpkg -s libglfw3-dev &>/dev/null 2>&1; then
|
||||||
|
MISSING+=(libglfw3-dev)
|
||||||
|
else
|
||||||
|
echo " libglfw3-dev: installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! dpkg -s libgl1-mesa-dev &>/dev/null 2>&1; then
|
||||||
|
MISSING+=(libgl1-mesa-dev)
|
||||||
|
else
|
||||||
|
echo " libgl1-mesa-dev: installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Optional: mingw for cross-compile
|
||||||
|
if command -v x86_64-w64-mingw32-g++ &>/dev/null; then
|
||||||
|
echo " mingw-w64: $(x86_64-w64-mingw32-g++ --version | head -1)"
|
||||||
|
else
|
||||||
|
echo " mingw-w64: not installed (optional, for Windows cross-compile)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${#MISSING[@]} -eq 0 ]; then
|
||||||
|
echo "[install_cpp_deps] All dependencies satisfied."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "[install_cpp_deps] Missing packages: ${MISSING[*]}"
|
||||||
|
echo "[install_cpp_deps] Installing..."
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -y -qq "${MISSING[@]}"
|
||||||
|
echo "[install_cpp_deps] Done."
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"fn-registry/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func buildCppCommand(fn *registry.Function, registryRoot, absPath string, args []string) (*exec.Cmd, error) {
|
||||||
|
cppRoot := filepath.Join(registryRoot, "cpp")
|
||||||
|
buildDir := filepath.Join(cppRoot, "build", "linux")
|
||||||
|
|
||||||
|
// Ensure build directory exists and cmake is configured
|
||||||
|
if _, err := os.Stat(filepath.Join(buildDir, "CMakeCache.txt")); os.IsNotExist(err) {
|
||||||
|
fmt.Fprintf(os.Stderr, "[fn run] configuring cmake for cpp...\n")
|
||||||
|
configure := exec.Command("cmake", "-B", buildDir, "-S", cppRoot)
|
||||||
|
configure.Dir = registryRoot
|
||||||
|
configure.Stdout = os.Stderr
|
||||||
|
configure.Stderr = os.Stderr
|
||||||
|
if err := configure.Run(); err != nil {
|
||||||
|
return nil, fmt.Errorf("cmake configure failed: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dir := filepath.Dir(absPath)
|
||||||
|
|
||||||
|
// Check if the function's directory has its own CMakeLists.txt (app with main)
|
||||||
|
localCMake := filepath.Join(dir, "CMakeLists.txt")
|
||||||
|
hasMain := false
|
||||||
|
if _, err := os.Stat(localCMake); err == nil {
|
||||||
|
hasMain = true
|
||||||
|
}
|
||||||
|
// Also check for main.cpp in the same directory
|
||||||
|
mainCpp := filepath.Join(dir, "main.cpp")
|
||||||
|
if _, err := os.Stat(mainCpp); err == nil {
|
||||||
|
hasMain = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasMain {
|
||||||
|
// Build and run the app binary
|
||||||
|
targetName := filepath.Base(dir)
|
||||||
|
build := exec.Command("cmake", "--build", buildDir, "--target", targetName)
|
||||||
|
build.Dir = registryRoot
|
||||||
|
build.Stdout = os.Stderr
|
||||||
|
build.Stderr = os.Stderr
|
||||||
|
fmt.Fprintf(os.Stderr, "[fn run] building target %s...\n", targetName)
|
||||||
|
if err := build.Run(); err != nil {
|
||||||
|
return nil, fmt.Errorf("cmake build failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the built binary
|
||||||
|
binaryPath := findBinary(buildDir, targetName)
|
||||||
|
if binaryPath == "" {
|
||||||
|
return nil, fmt.Errorf("built binary %q not found in %s", targetName, buildDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command(binaryPath, args...)
|
||||||
|
cmd.Dir = dir
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Library code: compile-check only (like go vet)
|
||||||
|
build := exec.Command("cmake", "--build", buildDir)
|
||||||
|
build.Dir = registryRoot
|
||||||
|
build.Stdout = os.Stderr
|
||||||
|
build.Stderr = os.Stderr
|
||||||
|
fmt.Fprintf(os.Stderr, "[fn run] %s is library code — running compile check\n", fn.ID)
|
||||||
|
|
||||||
|
if err := build.Run(); err != nil {
|
||||||
|
return nil, fmt.Errorf("compile check failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a no-op command that just prints success
|
||||||
|
cmd := exec.Command("echo", fmt.Sprintf("[fn run] %s compiled successfully", fn.ID))
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// findBinary searches for an executable in the build tree.
|
||||||
|
func findBinary(buildDir, name string) string {
|
||||||
|
// Common locations cmake puts binaries
|
||||||
|
candidates := []string{
|
||||||
|
filepath.Join(buildDir, name),
|
||||||
|
filepath.Join(buildDir, "apps", name, name),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range candidates {
|
||||||
|
if info, err := os.Stat(c); err == nil && !info.IsDir() {
|
||||||
|
// Check if executable
|
||||||
|
if info.Mode()&0111 != 0 {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk the build directory as fallback
|
||||||
|
var found string
|
||||||
|
filepath.Walk(buildDir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil || info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if info.Name() == name && info.Mode()&0111 != 0 {
|
||||||
|
found = path
|
||||||
|
return filepath.SkipAll
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// Also try without extension match for paths with subdirectories
|
||||||
|
if found == "" {
|
||||||
|
filepath.Walk(buildDir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil || info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
base := strings.TrimSuffix(info.Name(), filepath.Ext(info.Name()))
|
||||||
|
if base == name && info.Mode()&0111 != 0 {
|
||||||
|
found = path
|
||||||
|
return filepath.SkipAll
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return found
|
||||||
|
}
|
||||||
@@ -103,6 +103,8 @@ func buildCommand(fn *registry.Function, db *registry.DB, registryRoot, absPath
|
|||||||
return buildBashCommand(absPath, args)
|
return buildBashCommand(absPath, args)
|
||||||
case "ts":
|
case "ts":
|
||||||
return buildTsCommand(registryRoot, absPath, args)
|
return buildTsCommand(registryRoot, absPath, args)
|
||||||
|
case "cpp":
|
||||||
|
return buildCppCommand(fn, registryRoot, absPath, args)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported lang %q for execution", fn.Lang)
|
return nil, fmt.Errorf("unsupported lang %q for execution", fn.Lang)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
project(fn_registry_cpp LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
# --- Options ---
|
||||||
|
option(TRACY_ENABLE "Enable Tracy profiling" OFF)
|
||||||
|
|
||||||
|
# --- Vendor: Dear ImGui ---
|
||||||
|
set(IMGUI_DIR ${CMAKE_CURRENT_SOURCE_DIR}/vendor/imgui)
|
||||||
|
add_library(imgui STATIC
|
||||||
|
${IMGUI_DIR}/imgui.cpp
|
||||||
|
${IMGUI_DIR}/imgui_draw.cpp
|
||||||
|
${IMGUI_DIR}/imgui_tables.cpp
|
||||||
|
${IMGUI_DIR}/imgui_widgets.cpp
|
||||||
|
${IMGUI_DIR}/imgui_demo.cpp
|
||||||
|
${IMGUI_DIR}/backends/imgui_impl_glfw.cpp
|
||||||
|
${IMGUI_DIR}/backends/imgui_impl_opengl3.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(imgui PUBLIC
|
||||||
|
${IMGUI_DIR}
|
||||||
|
${IMGUI_DIR}/backends
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Vendor: ImPlot ---
|
||||||
|
set(IMPLOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/vendor/implot)
|
||||||
|
add_library(implot STATIC
|
||||||
|
${IMPLOT_DIR}/implot.cpp
|
||||||
|
${IMPLOT_DIR}/implot_items.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(implot PUBLIC ${IMPLOT_DIR})
|
||||||
|
target_link_libraries(implot PUBLIC imgui)
|
||||||
|
|
||||||
|
# --- Vendor: Tracy (optional) ---
|
||||||
|
if(TRACY_ENABLE)
|
||||||
|
set(TRACY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/vendor/tracy)
|
||||||
|
add_library(tracy STATIC
|
||||||
|
${TRACY_DIR}/public/TracyClient.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(tracy PUBLIC ${TRACY_DIR}/public)
|
||||||
|
target_compile_definitions(tracy PUBLIC TRACY_ENABLE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# --- Platform dependencies ---
|
||||||
|
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||||
|
# Cross-compile: use vendored or system GLFW, link opengl32/gdi32
|
||||||
|
find_package(glfw3 QUIET)
|
||||||
|
if(NOT glfw3_FOUND)
|
||||||
|
# Build GLFW from source if available
|
||||||
|
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/vendor/glfw/CMakeLists.txt)
|
||||||
|
set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE)
|
||||||
|
set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||||
|
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||||
|
add_subdirectory(vendor/glfw)
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "GLFW not found. For Windows cross-compile, add GLFW source to cpp/vendor/glfw/")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
set(PLATFORM_LIBS glfw opengl32 gdi32 imm32)
|
||||||
|
else()
|
||||||
|
# Linux native
|
||||||
|
find_package(glfw3 REQUIRED)
|
||||||
|
find_package(OpenGL REQUIRED)
|
||||||
|
set(PLATFORM_LIBS glfw OpenGL::GL ${CMAKE_DL_LIBS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(imgui PUBLIC ${PLATFORM_LIBS})
|
||||||
|
|
||||||
|
# --- Framework ---
|
||||||
|
add_library(fn_framework STATIC
|
||||||
|
framework/app_base.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(fn_framework PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/framework
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/functions
|
||||||
|
)
|
||||||
|
target_link_libraries(fn_framework PUBLIC imgui implot)
|
||||||
|
if(TRACY_ENABLE)
|
||||||
|
target_link_libraries(fn_framework PUBLIC tracy)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# --- Macro for creating ImGui apps ---
|
||||||
|
function(add_imgui_app target)
|
||||||
|
add_executable(${target} ${ARGN})
|
||||||
|
target_link_libraries(${target} PRIVATE fn_framework)
|
||||||
|
target_include_directories(${target} PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/functions
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# --- Function libraries (headers for composition) ---
|
||||||
|
# Functions are compiled as part of apps that use them via add_imgui_app.
|
||||||
|
# Each function is a .h/.cpp pair included by the app's CMakeLists.txt.
|
||||||
|
|
||||||
|
# --- Demo app ---
|
||||||
|
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/apps/chart_demo/CMakeLists.txt)
|
||||||
|
add_subdirectory(apps/chart_demo)
|
||||||
|
endif()
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
add_imgui_app(chart_demo
|
||||||
|
main.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/functions/viz/line_plot.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/functions/viz/scatter_plot.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/functions/viz/bar_chart.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/functions/viz/heatmap.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/functions/core/fps_overlay.cpp
|
||||||
|
)
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
#include "app_base.h"
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "implot.h"
|
||||||
|
|
||||||
|
#include "viz/line_plot.h"
|
||||||
|
#include "viz/scatter_plot.h"
|
||||||
|
#include "viz/bar_chart.h"
|
||||||
|
#include "viz/heatmap.h"
|
||||||
|
#include "core/fps_overlay.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// Generate sample data
|
||||||
|
static constexpr int N = 500;
|
||||||
|
static float xs[N], ys_sin[N], ys_cos[N];
|
||||||
|
static float scatter_x[200], scatter_y[200];
|
||||||
|
static const char* bar_labels[] = {"Go", "Python", "Bash", "TypeScript", "C++"};
|
||||||
|
static float bar_values[] = {201.0f, 202.0f, 38.0f, 80.0f, 5.0f};
|
||||||
|
static float heat_data[10 * 10];
|
||||||
|
|
||||||
|
static bool data_initialized = false;
|
||||||
|
|
||||||
|
static void init_data() {
|
||||||
|
if (data_initialized) return;
|
||||||
|
for (int i = 0; i < N; i++) {
|
||||||
|
xs[i] = static_cast<float>(i) * 0.02f;
|
||||||
|
ys_sin[i] = sinf(xs[i]);
|
||||||
|
ys_cos[i] = cosf(xs[i]);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 200; i++) {
|
||||||
|
scatter_x[i] = static_cast<float>(rand()) / RAND_MAX * 10.0f;
|
||||||
|
scatter_y[i] = scatter_x[i] * 0.5f + (static_cast<float>(rand()) / RAND_MAX - 0.5f) * 3.0f;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
int r = i / 10, c = i % 10;
|
||||||
|
heat_data[i] = sinf(r * 0.5f) * cosf(c * 0.5f);
|
||||||
|
}
|
||||||
|
data_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void render() {
|
||||||
|
init_data();
|
||||||
|
fps_overlay();
|
||||||
|
|
||||||
|
// Full-window dockspace
|
||||||
|
ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport());
|
||||||
|
|
||||||
|
if (ImGui::Begin("fn_registry — Chart Demo")) {
|
||||||
|
if (ImGui::BeginTabBar("##charts")) {
|
||||||
|
if (ImGui::BeginTabItem("Line Plot")) {
|
||||||
|
ImGui::Text("sin(x) — %d points", N);
|
||||||
|
line_plot("Sine Wave", xs, ys_sin, N);
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
if (ImGui::BeginTabItem("Scatter Plot")) {
|
||||||
|
ImGui::Text("y = 0.5x + noise — 200 points");
|
||||||
|
scatter_plot("Scatter Data", scatter_x, scatter_y, 200);
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
if (ImGui::BeginTabItem("Bar Chart")) {
|
||||||
|
ImGui::Text("Functions per language in fn_registry");
|
||||||
|
bar_chart("Registry Languages", bar_labels, bar_values, 5);
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
if (ImGui::BeginTabItem("Heatmap")) {
|
||||||
|
ImGui::Text("sin(r) * cos(c) — 10x10 matrix");
|
||||||
|
heatmap("Correlation Matrix", heat_data, 10, 10, -1.0f, 1.0f);
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
ImGui::EndTabBar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
return fn::run_app({.title = "fn_registry — Chart Demo", .width = 1400, .height = 900}, render);
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
#include "app_base.h"
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "imgui_impl_glfw.h"
|
||||||
|
#include "imgui_impl_opengl3.h"
|
||||||
|
#include "implot.h"
|
||||||
|
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#ifdef TRACY_ENABLE
|
||||||
|
#include "tracy/Tracy.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void glfw_error_callback(int error, const char* description) {
|
||||||
|
fprintf(stderr, "GLFW Error %d: %s\n", error, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace fn {
|
||||||
|
|
||||||
|
int run_app(AppConfig config, std::function<void()> render_fn) {
|
||||||
|
glfwSetErrorCallback(glfw_error_callback);
|
||||||
|
if (!glfwInit()) {
|
||||||
|
fprintf(stderr, "Failed to initialize GLFW\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenGL 3.3 core
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||||
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||||
|
#ifdef __APPLE__
|
||||||
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
GLFWwindow* window = glfwCreateWindow(config.width, config.height, config.title, nullptr, nullptr);
|
||||||
|
if (!window) {
|
||||||
|
fprintf(stderr, "Failed to create GLFW window\n");
|
||||||
|
glfwTerminate();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
glfwMakeContextCurrent(window);
|
||||||
|
glfwSwapInterval(config.vsync ? 1 : 0);
|
||||||
|
|
||||||
|
// Setup ImGui
|
||||||
|
IMGUI_CHECKVERSION();
|
||||||
|
ImGui::CreateContext();
|
||||||
|
ImPlot::CreateContext();
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||||||
|
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||||
|
|
||||||
|
ImGui::StyleColorsDark();
|
||||||
|
|
||||||
|
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
||||||
|
ImGui_ImplOpenGL3_Init("#version 330");
|
||||||
|
|
||||||
|
// Main loop
|
||||||
|
while (!glfwWindowShouldClose(window)) {
|
||||||
|
glfwPollEvents();
|
||||||
|
|
||||||
|
if (glfwGetWindowAttrib(window, GLFW_ICONIFIED)) {
|
||||||
|
glfwWaitEvents();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui_ImplOpenGL3_NewFrame();
|
||||||
|
ImGui_ImplGlfw_NewFrame();
|
||||||
|
ImGui::NewFrame();
|
||||||
|
|
||||||
|
render_fn();
|
||||||
|
|
||||||
|
ImGui::Render();
|
||||||
|
int display_w, display_h;
|
||||||
|
glfwGetFramebufferSize(window, &display_w, &display_h);
|
||||||
|
glViewport(0, 0, display_w, display_h);
|
||||||
|
glClearColor(config.bg_r, config.bg_g, config.bg_b, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||||
|
|
||||||
|
glfwSwapBuffers(window);
|
||||||
|
|
||||||
|
#ifdef TRACY_ENABLE
|
||||||
|
FrameMark;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
ImGui_ImplOpenGL3_Shutdown();
|
||||||
|
ImGui_ImplGlfw_Shutdown();
|
||||||
|
ImPlot::DestroyContext();
|
||||||
|
ImGui::DestroyContext();
|
||||||
|
glfwDestroyWindow(window);
|
||||||
|
glfwTerminate();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int run_app(std::function<void()> render_fn) {
|
||||||
|
return run_app(AppConfig{}, render_fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fn
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace fn {
|
||||||
|
|
||||||
|
struct AppConfig {
|
||||||
|
const char* title = "fn_registry";
|
||||||
|
int width = 1280;
|
||||||
|
int height = 720;
|
||||||
|
bool vsync = true;
|
||||||
|
float bg_r = 0.1f;
|
||||||
|
float bg_g = 0.1f;
|
||||||
|
float bg_b = 0.1f;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Run an ImGui application. The render_fn is called every frame
|
||||||
|
// between ImGui::NewFrame() and ImGui::Render().
|
||||||
|
// Returns 0 on clean exit, 1 on error.
|
||||||
|
int run_app(AppConfig config, std::function<void()> render_fn);
|
||||||
|
|
||||||
|
// Convenience: run with default config
|
||||||
|
int run_app(std::function<void()> render_fn);
|
||||||
|
|
||||||
|
} // namespace fn
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#include "core/fps_overlay.h"
|
||||||
|
#include "imgui.h"
|
||||||
|
|
||||||
|
#ifdef TRACY_ENABLE
|
||||||
|
#include "tracy/Tracy.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void fps_overlay() {
|
||||||
|
#ifdef TRACY_ENABLE
|
||||||
|
ZoneScoped;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration
|
||||||
|
| ImGuiWindowFlags_AlwaysAutoResize
|
||||||
|
| ImGuiWindowFlags_NoSavedSettings
|
||||||
|
| ImGuiWindowFlags_NoFocusOnAppearing
|
||||||
|
| ImGuiWindowFlags_NoNav
|
||||||
|
| ImGuiWindowFlags_NoMove;
|
||||||
|
|
||||||
|
const float pad = 10.0f;
|
||||||
|
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||||
|
ImVec2 pos(viewport->WorkPos.x + viewport->WorkSize.x - pad,
|
||||||
|
viewport->WorkPos.y + pad);
|
||||||
|
ImGui::SetNextWindowPos(pos, ImGuiCond_Always, ImVec2(1.0f, 0.0f));
|
||||||
|
ImGui::SetNextWindowBgAlpha(0.65f);
|
||||||
|
|
||||||
|
if (ImGui::Begin("##fps_overlay", nullptr, flags)) {
|
||||||
|
ImGui::Text("%.1f FPS", io.Framerate);
|
||||||
|
ImGui::Text("%.3f ms", 1000.0f / io.Framerate);
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Renders an FPS counter overlay in the top-right corner.
|
||||||
|
// Call within an ImGui frame.
|
||||||
|
void fps_overlay();
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
name: fps_overlay
|
||||||
|
kind: component
|
||||||
|
lang: cpp
|
||||||
|
domain: core
|
||||||
|
version: "1.0.0"
|
||||||
|
purity: pure
|
||||||
|
signature: "void fps_overlay()"
|
||||||
|
description: "Renderiza un overlay de FPS y frametime en la esquina superior derecha, con soporte opcional de Tracy"
|
||||||
|
tags: [imgui, fps, overlay, profiling, debug]
|
||||||
|
uses_functions: []
|
||||||
|
uses_types: []
|
||||||
|
returns: []
|
||||||
|
returns_optional: false
|
||||||
|
error_type: ""
|
||||||
|
imports: [imgui]
|
||||||
|
tested: false
|
||||||
|
tests: []
|
||||||
|
test_file_path: ""
|
||||||
|
file_path: "cpp/functions/core/fps_overlay.cpp"
|
||||||
|
framework: imgui
|
||||||
|
params: []
|
||||||
|
output: "Renderiza el overlay de FPS en el frame ImGui actual"
|
||||||
|
---
|
||||||
|
|
||||||
|
# fps_overlay
|
||||||
|
|
||||||
|
Muestra FPS y frametime (ms) en una ventana semi-transparente en la esquina superior derecha.
|
||||||
|
|
||||||
|
Si se compila con `TRACY_ENABLE`, incluye un `ZoneScoped` para profiling con Tracy.
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#include "viz/bar_chart.h"
|
||||||
|
#include "implot.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
void bar_chart(const char* title, const char* const* labels, const float* values, int count, float bar_width) {
|
||||||
|
if (ImPlot::BeginPlot(title, ImVec2(-1, 0))) {
|
||||||
|
std::vector<double> positions(count);
|
||||||
|
for (int i = 0; i < count; i++) positions[i] = i;
|
||||||
|
|
||||||
|
ImPlot::SetupAxisTicks(ImAxis_X1, positions.data(), count, labels);
|
||||||
|
ImPlot::PlotBars("##data", values, count, bar_width);
|
||||||
|
ImPlot::EndPlot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bar_chart(const char* title, const char* const* labels, const double* values, int count, double bar_width) {
|
||||||
|
if (ImPlot::BeginPlot(title, ImVec2(-1, 0))) {
|
||||||
|
std::vector<double> positions(count);
|
||||||
|
for (int i = 0; i < count; i++) positions[i] = i;
|
||||||
|
|
||||||
|
ImPlot::SetupAxisTicks(ImAxis_X1, positions.data(), count, labels);
|
||||||
|
ImPlot::PlotBars("##data", values, count, bar_width);
|
||||||
|
ImPlot::EndPlot();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Renders a vertical bar chart using ImPlot.
|
||||||
|
// Call within an ImGui frame.
|
||||||
|
void bar_chart(const char* title, const char* const* labels, const float* values, int count, float bar_width = 0.67f);
|
||||||
|
void bar_chart(const char* title, const char* const* labels, const double* values, int count, double bar_width = 0.67);
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
name: bar_chart
|
||||||
|
kind: component
|
||||||
|
lang: cpp
|
||||||
|
domain: viz
|
||||||
|
version: "1.0.0"
|
||||||
|
purity: pure
|
||||||
|
signature: "void bar_chart(const char* title, const char* const* labels, const float* values, int count, float bar_width)"
|
||||||
|
description: "Renderiza un grafico de barras verticales usando ImPlot dentro de un frame ImGui"
|
||||||
|
tags: [implot, chart, visualization, gpu, bar]
|
||||||
|
uses_functions: []
|
||||||
|
uses_types: []
|
||||||
|
returns: []
|
||||||
|
returns_optional: false
|
||||||
|
error_type: ""
|
||||||
|
imports: [implot]
|
||||||
|
tested: false
|
||||||
|
tests: []
|
||||||
|
test_file_path: ""
|
||||||
|
file_path: "cpp/functions/viz/bar_chart.cpp"
|
||||||
|
framework: imgui
|
||||||
|
params:
|
||||||
|
- name: title
|
||||||
|
desc: "Titulo del grafico de barras"
|
||||||
|
- name: labels
|
||||||
|
desc: "Array de etiquetas para el eje X, una por barra"
|
||||||
|
- name: values
|
||||||
|
desc: "Array de valores numericos para la altura de cada barra"
|
||||||
|
- name: count
|
||||||
|
desc: "Numero de barras (longitud de labels y values)"
|
||||||
|
- name: bar_width
|
||||||
|
desc: "Ancho de cada barra como fraccion del espacio disponible (default 0.67)"
|
||||||
|
output: "Renderiza el grafico de barras en el frame ImGui actual"
|
||||||
|
---
|
||||||
|
|
||||||
|
# bar_chart
|
||||||
|
|
||||||
|
Wrapper atomico sobre `ImPlot::PlotBars` con configuracion automatica de etiquetas en el eje X.
|
||||||
|
|
||||||
|
Debe llamarse dentro del render callback de `fn::run_app`.
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#include "viz/heatmap.h"
|
||||||
|
#include "implot.h"
|
||||||
|
|
||||||
|
void heatmap(const char* title, const float* values, int rows, int cols,
|
||||||
|
float scale_min, float scale_max) {
|
||||||
|
if (ImPlot::BeginPlot(title, ImVec2(-1, 0), ImPlotFlags_NoLegend)) {
|
||||||
|
ImPlot::SetupAxes(nullptr, nullptr,
|
||||||
|
ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations);
|
||||||
|
ImPlot::PlotHeatmap("##data", values, rows, cols,
|
||||||
|
scale_min, scale_max);
|
||||||
|
ImPlot::EndPlot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void heatmap(const char* title, const double* values, int rows, int cols,
|
||||||
|
double scale_min, double scale_max) {
|
||||||
|
if (ImPlot::BeginPlot(title, ImVec2(-1, 0), ImPlotFlags_NoLegend)) {
|
||||||
|
ImPlot::SetupAxes(nullptr, nullptr,
|
||||||
|
ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations);
|
||||||
|
ImPlot::PlotHeatmap("##data", values, rows, cols,
|
||||||
|
scale_min, scale_max);
|
||||||
|
ImPlot::EndPlot();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Renders a heatmap using ImPlot.
|
||||||
|
// Data is row-major: values[row * cols + col].
|
||||||
|
// Call within an ImGui frame.
|
||||||
|
void heatmap(const char* title, const float* values, int rows, int cols,
|
||||||
|
float scale_min = 0.0f, float scale_max = 0.0f);
|
||||||
|
void heatmap(const char* title, const double* values, int rows, int cols,
|
||||||
|
double scale_min = 0.0, double scale_max = 0.0);
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
name: heatmap
|
||||||
|
kind: component
|
||||||
|
lang: cpp
|
||||||
|
domain: viz
|
||||||
|
version: "1.0.0"
|
||||||
|
purity: pure
|
||||||
|
signature: "void heatmap(const char* title, const float* values, int rows, int cols, float scale_min, float scale_max)"
|
||||||
|
description: "Renderiza un mapa de calor 2D usando ImPlot dentro de un frame ImGui"
|
||||||
|
tags: [implot, chart, visualization, gpu, heatmap, matrix]
|
||||||
|
uses_functions: []
|
||||||
|
uses_types: []
|
||||||
|
returns: []
|
||||||
|
returns_optional: false
|
||||||
|
error_type: ""
|
||||||
|
imports: [implot]
|
||||||
|
tested: false
|
||||||
|
tests: []
|
||||||
|
test_file_path: ""
|
||||||
|
file_path: "cpp/functions/viz/heatmap.cpp"
|
||||||
|
framework: imgui
|
||||||
|
params:
|
||||||
|
- name: title
|
||||||
|
desc: "Titulo del heatmap"
|
||||||
|
- name: values
|
||||||
|
desc: "Array de valores en orden row-major (values[row * cols + col])"
|
||||||
|
- name: rows
|
||||||
|
desc: "Numero de filas de la matriz"
|
||||||
|
- name: cols
|
||||||
|
desc: "Numero de columnas de la matriz"
|
||||||
|
- name: scale_min
|
||||||
|
desc: "Valor minimo de la escala de color (0 para autodetectar)"
|
||||||
|
- name: scale_max
|
||||||
|
desc: "Valor maximo de la escala de color (0 para autodetectar)"
|
||||||
|
output: "Renderiza el heatmap en el frame ImGui actual"
|
||||||
|
---
|
||||||
|
|
||||||
|
# heatmap
|
||||||
|
|
||||||
|
Wrapper atomico sobre `ImPlot::PlotHeatmap`. Renderiza una matriz de valores como mapa de calor con escala de color.
|
||||||
|
|
||||||
|
Los datos deben estar en formato row-major. Si `scale_min` y `scale_max` son ambos 0, ImPlot autodetecta el rango.
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#include "viz/line_plot.h"
|
||||||
|
#include "implot.h"
|
||||||
|
|
||||||
|
void line_plot(const char* title, const float* xs, const float* ys, int count) {
|
||||||
|
if (ImPlot::BeginPlot(title, ImVec2(-1, 0))) {
|
||||||
|
ImPlot::PlotLine("##data", xs, ys, count);
|
||||||
|
ImPlot::EndPlot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void line_plot(const char* title, const double* xs, const double* ys, int count) {
|
||||||
|
if (ImPlot::BeginPlot(title, ImVec2(-1, 0))) {
|
||||||
|
ImPlot::PlotLine("##data", xs, ys, count);
|
||||||
|
ImPlot::EndPlot();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Renders a 2D line plot using ImPlot.
|
||||||
|
// Call within an ImGui frame (inside fn::run_app render callback).
|
||||||
|
void line_plot(const char* title, const float* xs, const float* ys, int count);
|
||||||
|
|
||||||
|
// Overload with double precision.
|
||||||
|
void line_plot(const char* title, const double* xs, const double* ys, int count);
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
name: line_plot
|
||||||
|
kind: component
|
||||||
|
lang: cpp
|
||||||
|
domain: viz
|
||||||
|
version: "1.0.0"
|
||||||
|
purity: pure
|
||||||
|
signature: "void line_plot(const char* title, const float* xs, const float* ys, int count)"
|
||||||
|
description: "Renderiza un grafico de lineas 2D usando ImPlot dentro de un frame ImGui"
|
||||||
|
tags: [implot, chart, visualization, gpu, line]
|
||||||
|
uses_functions: []
|
||||||
|
uses_types: []
|
||||||
|
returns: []
|
||||||
|
returns_optional: false
|
||||||
|
error_type: ""
|
||||||
|
imports: [implot]
|
||||||
|
tested: false
|
||||||
|
tests: []
|
||||||
|
test_file_path: ""
|
||||||
|
file_path: "cpp/functions/viz/line_plot.cpp"
|
||||||
|
framework: imgui
|
||||||
|
params:
|
||||||
|
- name: title
|
||||||
|
desc: "Titulo del grafico, se muestra como header del plot"
|
||||||
|
- name: xs
|
||||||
|
desc: "Array de coordenadas X"
|
||||||
|
- name: ys
|
||||||
|
desc: "Array de coordenadas Y"
|
||||||
|
- name: count
|
||||||
|
desc: "Numero de puntos en los arrays xs/ys"
|
||||||
|
output: "Renderiza el grafico de lineas en el frame ImGui actual"
|
||||||
|
---
|
||||||
|
|
||||||
|
# line_plot
|
||||||
|
|
||||||
|
Wrapper atomico sobre `ImPlot::PlotLine`. Renderiza un grafico de lineas 2D con los datos proporcionados.
|
||||||
|
|
||||||
|
Debe llamarse dentro del render callback de `fn::run_app` (o cualquier contexto con un frame ImGui activo).
|
||||||
|
|
||||||
|
Soporta `float` y `double` precision.
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#include "viz/scatter_plot.h"
|
||||||
|
#include "implot.h"
|
||||||
|
|
||||||
|
void scatter_plot(const char* title, const float* xs, const float* ys, int count) {
|
||||||
|
if (ImPlot::BeginPlot(title, ImVec2(-1, 0))) {
|
||||||
|
ImPlot::PlotScatter("##data", xs, ys, count);
|
||||||
|
ImPlot::EndPlot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scatter_plot(const char* title, const double* xs, const double* ys, int count) {
|
||||||
|
if (ImPlot::BeginPlot(title, ImVec2(-1, 0))) {
|
||||||
|
ImPlot::PlotScatter("##data", xs, ys, count);
|
||||||
|
ImPlot::EndPlot();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Renders a scatter plot using ImPlot.
|
||||||
|
// Call within an ImGui frame.
|
||||||
|
void scatter_plot(const char* title, const float* xs, const float* ys, int count);
|
||||||
|
void scatter_plot(const char* title, const double* xs, const double* ys, int count);
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
name: scatter_plot
|
||||||
|
kind: component
|
||||||
|
lang: cpp
|
||||||
|
domain: viz
|
||||||
|
version: "1.0.0"
|
||||||
|
purity: pure
|
||||||
|
signature: "void scatter_plot(const char* title, const float* xs, const float* ys, int count)"
|
||||||
|
description: "Renderiza un grafico de dispersion usando ImPlot dentro de un frame ImGui"
|
||||||
|
tags: [implot, chart, visualization, gpu, scatter]
|
||||||
|
uses_functions: []
|
||||||
|
uses_types: []
|
||||||
|
returns: []
|
||||||
|
returns_optional: false
|
||||||
|
error_type: ""
|
||||||
|
imports: [implot]
|
||||||
|
tested: false
|
||||||
|
tests: []
|
||||||
|
test_file_path: ""
|
||||||
|
file_path: "cpp/functions/viz/scatter_plot.cpp"
|
||||||
|
framework: imgui
|
||||||
|
params:
|
||||||
|
- name: title
|
||||||
|
desc: "Titulo del grafico scatter"
|
||||||
|
- name: xs
|
||||||
|
desc: "Array de coordenadas X"
|
||||||
|
- name: ys
|
||||||
|
desc: "Array de coordenadas Y"
|
||||||
|
- name: count
|
||||||
|
desc: "Numero de puntos en los arrays xs/ys"
|
||||||
|
output: "Renderiza el grafico de dispersion en el frame ImGui actual"
|
||||||
|
---
|
||||||
|
|
||||||
|
# scatter_plot
|
||||||
|
|
||||||
|
Wrapper atomico sobre `ImPlot::PlotScatter`. Renderiza un grafico de dispersion 2D.
|
||||||
|
|
||||||
|
Debe llamarse dentro del render callback de `fn::run_app`.
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
set(CMAKE_SYSTEM_NAME Linux)
|
||||||
|
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
||||||
|
|
||||||
|
set(CMAKE_C_COMPILER gcc)
|
||||||
|
set(CMAKE_CXX_COMPILER g++)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
set(CMAKE_SYSTEM_NAME Windows)
|
||||||
|
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
||||||
|
|
||||||
|
set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
|
||||||
|
set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
|
||||||
|
set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
|
||||||
|
|
||||||
|
set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
# Static link runtime so .exe is self-contained
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++")
|
||||||
+1
Submodule cpp/vendor/glfw added at b00e6a8a88
+1
Submodule cpp/vendor/imgui added at f5f6ca07be
+1
Submodule cpp/vendor/implot added at 524f9fcd48
+1
Submodule cpp/vendor/tracy added at 00a069d608
@@ -35,6 +35,8 @@ func parseTestFile(path, lang string) ([]testCase, error) {
|
|||||||
return parsePythonTests(content), nil
|
return parsePythonTests(content), nil
|
||||||
case "bash":
|
case "bash":
|
||||||
return parseBashTests(content), nil
|
return parseBashTests(content), nil
|
||||||
|
case "cpp":
|
||||||
|
return parseCppTests(content), nil
|
||||||
default:
|
default:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -115,6 +117,23 @@ func parseBashTests(content string) []testCase {
|
|||||||
return extractBlocks(lines, positions)
|
return extractBlocks(lines, positions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseCppTests extracts C++ test functions (Google Test TEST/TEST_F macros).
|
||||||
|
var cppTestRe = regexp.MustCompile(`(?m)^(?:TEST|TEST_F|TEST_P)\s*\(\s*(\w+)\s*,\s*(\w+)\s*\)`)
|
||||||
|
|
||||||
|
func parseCppTests(content string) []testCase {
|
||||||
|
lines := strings.Split(content, "\n")
|
||||||
|
var positions []testPos
|
||||||
|
|
||||||
|
for i, line := range lines {
|
||||||
|
if m := cppTestRe.FindStringSubmatch(line); m != nil {
|
||||||
|
name := m[1] + "." + m[2] // Suite.TestName
|
||||||
|
positions = append(positions, testPos{name: name, startLine: i})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return extractBlocks(lines, positions)
|
||||||
|
}
|
||||||
|
|
||||||
// extractBlocks splits lines into code blocks based on test positions.
|
// extractBlocks splits lines into code blocks based on test positions.
|
||||||
func extractBlocks(lines []string, positions []testPos) []testCase {
|
func extractBlocks(lines []string, positions []testPos) []testCase {
|
||||||
var tests []testCase
|
var tests []testCase
|
||||||
|
|||||||
Reference in New Issue
Block a user