three-pde

Una aplicación web interactiva que transforma imágenes bidimensionales en visualizaciones tridimensionales dinámicas mediante la resolución numérica de ecuaciones diferenciales parciales (PDEs). Este proyecto combina técnicas avanzadas de procesamiento de imágenes, métodos numéricos de diferencias finitas, y renderizado 3D en tiempo real utilizando WebGL y Three.js.

Captura de pantalla 1

Captura de pantalla 2

Captura de pantalla 3

Ver el proyecto completo aquí: https://github.com/agarnung/threepde/tree/main.

Resumen Ejecutivo

three-pde es una herramienta educativa e interactiva que permite visualizar cómo evolucionan las imágenes cuando se les aplican diferentes ecuaciones diferenciales parciales. La aplicación convierte cada píxel de una imagen en un punto de altura en una malla 3D, donde la intensidad del píxel determina la altura inicial. A medida que la simulación avanza, la PDE modifica estos valores de altura según las leyes físicas y matemáticas que gobiernan cada ecuación, creando visualizaciones dinámicas y fascinantes.

Las imágenes de entrada deben estar en formato WebP, ya que el sistema lee los datos en formato RGBA (Rojo, Verde, Azul, Alfa). Es válido convertir una imagen monocromática a WebP y luego pasársela a la aplicación web. El procesamiento completo se realiza en el navegador del cliente, sin necesidad de servidor backend, aprovechando las capacidades de JavaScript moderno y WebGL para cálculos intensivos.

Arquitectura del Proyecto

Estructura de Carpetas

La estructura de carpetas del proyecto está diseñada de forma modular, optimizada y con separación clara de responsabilidades. Esto incluye la separación entre partes de web estática, archivos públicos o assets, y componentes de CI/CD:

threepde/
├── index.html                    # Punto de entrada único de la aplicación
├── assets/                       # Archivos estáticos optimizados para producción
│   ├── js/                       # Código JavaScript minificado (solo usado en producción)
│   │   └── main.min.js         
│   └── images/                   # Imágenes estáticas (siempre directamente disponibles desde el cliente)
├── src/                          # Archivos de código fuente que se modifican durante desarrollo
│   ├── main.js                   # Punto de entrada principal de la aplicación
│   ├── solver.js                 # Implementación del solver de PDEs
│   ├── helpers/
│   │   ├── image-preprocessor.js # Normalización y redimensionado de imágenes
│   │   ├── color-maps.js         # Implementación de mapas de colores y conversiones Lab↔RGB
│   │   └── image-mesh-converter.js # Conversión de imágenes a mallas 3D
│   └── ...
├── public/                       # Archivos públicos servidos directamente
│   ├── css/
│   │   └── styles.css            # Estilos de la aplicación
│   ├── images/                   # Imágenes de ejemplo predefinidas
│   │   ├── lena_gray.webp
│   │   └── lena_rgb.webp
│   └── favicon.ico
└── .github/workflows/            # Automatización de producción
    └── deploy.yml                # Workflow de GitHub Actions para despliegue

Flujo de Desarrollo vs. Producción

Durante el desarrollo, se modifican únicamente los archivos dentro de src/. Las imágenes nuevas (por defecto, que el usuario finalmente podrá elegir con un selector) se agregan en assets/images/ o public/images/ y serán accesibles inmediatamente por el cliente, en el formato original, sin procesamiento de transferencia adicional.

Para previsualizar la web durante desarrollo se puede usar Live Server o Live Preview de VSCode (accesible desde http://localhost:5500), que sirve el index.html localmente y recarga automáticamente al guardar cambios. Esto elimina la necesidad de un backend (Flask, Django, etc.), ya que todo lo que servimos es contenido estático.

Uso de Three.js mediante CDN

Utilizamos Three.js mediante CDN tanto en desarrollo como en producción; lo especificamos directamente en el index.html. Esto tiene varias ventajas con respecto a usar Three.js en local o como librería npm:

  1. Evita duplicados en el repositorio: No necesitamos incluir archivos grandes de librerías en nuestro repositorio
  2. Aprovecha caché del navegador: Si el usuario ya visitó otro sitio que usa la misma versión de Three.js desde el mismo CDN, el navegador reutiliza el archivo en caché
  3. Versión minificada y optimizada: El CDN proporciona versiones minificadas que están optimizadas para producción
  4. Import maps modernos: Usamos la versión ES6 (three.module.js) con un import map, que es la versión más moderna y recomendada, en lugar de la UMD (three.min.js)
<!-- En index.html (SIEMPRE usa CDN) -->
<script type="importmap">
{
    "imports": {
        "three": "https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.module.js",
        "three/examples/jsm/": "https://cdn.jsdelivr.net/npm/three@0.132.2/examples/jsm/"
    }
}
</script>

Automatización de Despliegue con GitHub Actions

Usamos GitHub Actions para detectar pushes a la rama main y automáticamente minificar todos los archivos JavaScript de src/ y llevarlos a assets/js/main.min.js, es decir, para minificar y desplegar la web. Esto mantiene el código de src/ intacto y solo sube el resultado final optimizado a GitHub Pages, que es donde se sirve luego la versión optimizada de la web.

El workflow también minifica el CSS usando clean-css-cli para reducir aún más el tamaño de los archivos en producción. El proceso completo incluye:

  1. Checkout del código: Obtiene el código fuente del repositorio
  2. Instalación de dependencias: Instala herramientas de minificación (terser, clean-css-cli)
  3. Construcción y minificación:
    • Copia archivos estáticos necesarios
    • Minifica todos los archivos JavaScript de src/ en un solo bundle
    • Minifica el CSS
    • Organiza todo en un directorio dist/ listo para producción
  4. Despliegue a GitHub Pages: Usa la acción peaceiris/actions-gh-pages@v3 para desplegar el contenido
name: Deploy to GitHub Pages

on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Install dependencies
        run: npm install -g terser clean-css-cli

      - name: Build and Minify
        run: |
          mkdir -p dist/assets/js
          mkdir -p dist/assets/css
          cp index.html dist/
          cp -r public/images dist/images
          cp public/favicon.ico dist/
          
          # Minify JS
          npx terser "src/**/*.js" -c -m -o dist/assets/js/main.min.js

          # Minify CSS
          npx cleancss -o dist/assets/css/styles.min.css public/css/styles.css

      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: $
          publish_dir: dist

Carga Condicional de Scripts

Para decidir qué scripts JavaScript cargar desde el index.html, se crea un bloque de carga condicional basado en la URL actual, para diferenciar si cargar nuestro código fuente en desarrollo o el código minificado en producción:

<!-- Carga condicional basada en URL -->
<script type="module">
  if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') {
    // Desarrollo: carga módulos desagregados
    import('./src/main.js');
  } else {
    // Producción: carga bundle minificado
    const script = document.createElement('script');
    script.type = 'module';
    script.src = 'assets/js/main.min.js';
    document.head.appendChild(script);
  }
</script>

Así, en localhost desarrollamos con archivos fuente individuales y en nuestro dominio (o, aquí, en github.io), se usa el bundle minificado automáticamente. Esto permite un desarrollo más cómodo con debugging más fácil, mientras que en producción se sirve código optimizado.

Comparación: GitHub Pages vs. PythonAnywhere/Flask

Es importante destacar la diferencia fundamental entre usar GitHub Pages y usar una solución como PythonAnywhere con Flask:

GitHub Pages:

PythonAnywhere/Flask:

En nuestro caso, three-pde no necesita backend porque:

  1. Todo el procesamiento de imágenes se hace en el navegador (FileReader API, Canvas API)
  2. El solver de PDEs se ejecuta en JavaScript/WebGL
  3. No hay necesidad de almacenar datos del usuario
  4. No hay autenticación ni sesiones

Por lo tanto, GitHub Pages es la solución perfecta y más simple para este proyecto.

Procesamiento de Imágenes

Tipos de Imágenes en el Proyecto

Hay dos tipos de imágenes en el proyecto, cada una con su propio flujo de procesamiento:

1. Imágenes Estáticas Predefinidas

Las imágenes estáticas, predefinidas por el desarrollador en public/images/, son parte del repositorio y deben estar optimizadas. Es decir, se debería usar herramientas como ImageMagick o squoosh-cli para convertirlas a un formato optimizado (e.g. WebP) antes de subirlas, a fin de reducir el tamaño del repositorio, mejorar el tiempo de carga inicial, reducir el ancho de banda, etc.

Ejemplo de optimización:

# Usando ImageMagick
convert lena.png -resize 512x512 -quality 99 public/images/lena.webp

# Usando squoosh-cli
npx @squoosh/cli --webp '{quality:99}' lena.png -d public/images/

2. Imágenes Subidas por el Usuario

Las imágenes subidas por el usuario, cuyo origen es su sistema local, se procesan inmediatamente y por completo en el navegador. El flujo completo es el siguiente:

sequenceDiagram
    Usuario->>Navegador: Selecciona imagen desde sistema de archivos
    Navegador->>JavaScript: FileReader API lee el archivo
    JavaScript->>Canvas: Dibuja imagen en canvas HTML5
    Canvas->>JavaScript: getImageData() obtiene datos de píxeles RGBA
    JavaScript->>Normalización: Convierte valores a rango [0,1]
    JavaScript->>Preprocesamiento: Extrae canal de luminancia (L de Lab)
    JavaScript->>Three.js: Crea heightmap a partir de datos normalizados
    Three.js->>WebGL: Renderiza malla 3D en tiempo real

Este procesamiento completamente en el cliente tiene varias ventajas:

Conversión a Escala de Grises y Espacio de Color Lab

Independientemente del tipo de imagen de entrada (RGB, escala de grises, etc.), las PDEs se resuelven utilizando únicamente el canal de luminancia. Esto se debe a que las ecuaciones diferenciales parciales que implementamos operan sobre funciones escalares (un valor por punto), no sobre funciones vectoriales (múltiples valores por punto).

El proceso de conversión utiliza el espacio de color Lab (CIE Lab*), que es perceptualmente uniforme y separa la luminancia (L) de la crominancia (a, b). Esto permite:

  1. Extraer la luminancia (L): Se usa para resolver la PDE
  2. Preservar la crominancia (a, b): Se almacena para reconstruir colores después

El algoritmo de conversión RGB a Lab utiliza el punto blanco D65 como referencia estándar, que es el estándar para monitores y pantallas modernas.

Redimensionado y Submuestreo

Para optimizar el rendimiento del solver, las imágenes se redimensionan típicamente a 512×512 píxeles antes de ser procesadas por la PDE. Esto reduce significativamente el número de operaciones necesarias:

El redimensionado se realiza usando el algoritmo de interpolación bilineal del Canvas API, que proporciona un buen balance entre calidad y rendimiento.

Nota importante sobre el modo “constant-color”: Este es el único modo en el que se puede visualizar en el canvas 2D la imagen con resolución original perfecta, puesto que la intensidad del heightmap del resto de modos es la calculada en cada paso por el solver, y típicamente submuestreamos a 512×512 la imagen para no sobrecargar al solver. Pero en el caso constant-color, el valor de intensidad de malla y canvas2d no cambia en ningún paso; solo la altura de la mesh evoluciona según la PDE.

Ecuaciones Diferenciales Parciales Implementadas

Ecuación de Calor (Heat Equation)

La ecuación de calor, también conocida como ecuación de difusión o ecuación de Fourier, es una de las PDEs más fundamentales en física y matemáticas:

\[\frac{\partial u}{\partial t} = \alpha \nabla^2 u\]

Donde:

Esta ecuación modela cómo se difunde el calor (o cualquier cantidad conservada) a través de un medio. En el contexto de procesamiento de imágenes, produce un efecto de “desenfoque” progresivo, donde los bordes se suavizan y los detalles de alta frecuencia se atenúan con el tiempo.

Ecuación de Onda (Wave Equation)

La ecuación de onda describe la propagación de ondas en un medio:

\[\frac{\partial^2 u}{\partial t^2} = c^2 \nabla^2 u\]

Donde:

Esta ecuación produce efectos ondulatorios, donde las perturbaciones se propagan a través de la imagen creando patrones de interferencia y ondas que se reflejan en los bordes según las condiciones de contorno.

Decaimiento Exponencial (Exponential Decay)

Técnicamente, el decaimiento exponencial es una ecuación diferencial ordinaria (ODE), no una PDE, ya que no hay propagación espacial:

\[\frac{\partial u}{\partial t} = -\lambda u\]

Donde \(\lambda\) es la constante de decaimiento.

Esta ecuación no requiere condición CFL (Courant-Friedrichs-Lewy) porque no hay propagación espacial, pero sí tiene una condición de estabilidad numérica que debe cumplirse para evitar inestabilidades en el solver.

La solución analítica es \(u(t) = u_0 e^{-\lambda t}\), lo que produce un desvanecimiento uniforme de la imagen con el tiempo.

Métodos Numéricos y Discretización

Esquemas de Discretización Temporal

El proyecto implementa varios esquemas numéricos para resolver las PDEs:

Forward Euler (Explícito)

El método de Euler hacia adelante es un esquema explícito de primer orden:

\[u^{n+1} = u^n + \Delta t \cdot f(u^n, t^n)\]

Ventajas:

Desventajas:

Backward Euler (Implícito)

El método de Euler hacia atrás es un esquema implícito de primer orden:

\[u^{n+1} = u^n + \Delta t \cdot f(u^{n+1}, t^{n+1})\]

Ventajas:

Desventajas:

Crank-Nicholson (Comentado, no implementado aún)

El método de Crank-Nicholson es un esquema implícito de segundo orden que promedia las evaluaciones en \(t^n\) y \(t^{n+1}\):

\[u^{n+1} = u^n + \frac{\Delta t}{2} \left[ f(u^n, t^n) + f(u^{n+1}, t^{n+1}) \right]\]

Este método combina las ventajas de estabilidad de los métodos implícitos con mayor precisión, pero requiere resolver sistemas lineales más complejos.

Discretización Espacial: Diferencias Finitas

El laplaciano $\nabla^2 u$ se discretiza usando el esquema de diferencias finitas de cinco puntos (stencil de 5 puntos):

\[\nabla^2 u_{i,j} \approx \frac{u_{i+1,j} + u_{i-1,j} + u_{i,j+1} + u_{i,j-1} - 4u_{i,j}}{h^2}\]

Donde $h$ es el espaciado de la malla (típicamente \(h = 1\) píxel).

Este esquema es de segundo orden en espacio y es el estándar para problemas de difusión en 2D.

Condiciones de Contorno

Las condiciones de contorno son cruciales para determinar cómo se comporta la solución en los bordes del dominio. El proyecto implementa varias opciones:

Dirichlet (Valor Fijo)

Especifica el valor de la función en el contorno:

\[u|_{\partial \Omega} = g\]

Casos especiales:

Neumann (Derivada Fija)

Especifica el valor de la derivada normal en el contorno:

\[\frac{\partial u}{\partial n}|_{\partial \Omega} = h\]

Caso especial:

Periódica

Los bordes se conectan de forma que la imagen se “envuelve” sobre sí misma:

\[u(0, y) = u(W, y)\] \[u(x, 0) = u(x, H)\]

Donde \(W\) y \(H\) son el ancho y alto de la imagen. Esto crea un dominio toroidal donde no hay bordes reales.

Robin (Mixta)

Combina condiciones Dirichlet y Neumann:

\[\alpha u + \beta \frac{\partial u}{\partial n}|_{\partial \Omega} = \gamma\]

Esta es una condición más general que permite modelar fenómenos físicos más complejos, como transferencia de calor con convección.

Para más información sobre condiciones de contorno, consulta: Wikipedia.

Mapas de Colores (Colormaps)

El proyecto implementa varios mapas de colores para visualizar los valores de altura de la malla 3D. Los colormaps transforman valores escalares (altura) en colores RGB para facilitar la interpretación visual.

Modos de Coloreado

Constant (Constante)

Todos los vértices de la malla reciben el mismo color uniforme (gris medio, #888888). Este modo es útil para enfatizar la forma geométrica de la malla sin distracciones de color, pero para la imagen no tiene mucho sentido visualmente pues se ve completamente uniforme. Se mantiene principalmente para demostrar el funcionamiento de los colormaps y para casos de prueba.

Grayscale (Escala de Grises)

Mapea directamente los valores de altura a una escala de grises, donde valores bajos son negros y valores altos son blancos. Es el modo más intuitivo y corresponde directamente a la imagen procesada.

Constant Color (Color Constante / Modo Mantel)

En este modo, los colores RGB originales de la imagen se mantienen completamente intactos. La imagen se comporta como un “mantel” que se coloca sobre la malla 3D, donde la altura evoluciona según la PDE pero los colores permanecen fijos.

Características importantes:

Este modo realmente significa “mantener colores originales”, mientras que el modo “constant-chrominance” significa “mantener crominancia original”.

Constant Chrominance (Crominancia Constante)

Este modo combina la altura evolutiva (que se mapea al canal L de Lab) con los canales de crominancia originales (a, b) de la imagen. Esto produce una visualización donde:

Este modo proporciona una retroalimentación visual más físicamente informada de la evolución de la PDE, mientras mantiene parte de la identidad de color original de la imagen. Es especialmente útil para visualizar cómo la difusión o las ondas afectan la estructura de la imagen sin perder completamente la información de color.

Jet

Un colormap clásico que va del azul (valores bajos) al rojo (valores altos), pasando por cian, verde, amarillo y naranja. Es muy popular en visualización científica pero puede ser problemático para personas con daltonismo.

Viridis

Un colormap perceptualmente uniforme diseñado específicamente para visualización científica. Va del púrpura oscuro (bajos) al amarillo brillante (altos), y es accesible para personas con daltonismo.

Inferno

Similar a Viridis pero con una paleta más cálida, yendo del negro (bajos) al amarillo brillante (altos). También es perceptualmente uniforme y accesible.

Seismic

Un colormap divergente que va del azul oscuro (valores negativos/bajos) al rojo oscuro (valores positivos/altos), pasando por blanco en el centro. Ideal para visualizar datos que tienen un punto de referencia central.

RdYlBu (Red-Yellow-Blue)

Otro colormap divergente que va del rojo (bajos) al azul (altos), pasando por amarillo en el centro. Útil para visualizar desviaciones de un valor central.

Visualización 3D con Three.js

Creación de la Malla de Altura

La malla 3D se crea a partir del heightmap (mapa de alturas) generado a partir de los datos de la imagen. El proceso incluye:

  1. Generación de vértices: Cada píxel de la imagen se convierte en un vértice 3D
  2. Cálculo de índices: Se crean triángulos conectando vértices adyacentes
  3. Cálculo de normales: Se calculan normales de vértices para iluminación realista
  4. Asignación de colores: Se aplica el colormap seleccionado a cada vértice
  5. Creación de wireframe: Se genera una versión de alambre para visualización alternativa

Materiales y Iluminación

El proyecto utiliza MeshStandardMaterial de Three.js, que soporta iluminación física basada (PBR - Physically Based Rendering). Las propiedades configurables incluyen:

Actualmente, estos valores están fijos en el código, pero hay un TODO para permitir su control en tiempo real a través de la interfaz de usuario.

La escena utiliza iluminación direccional desde dos direcciones opuestas para proporcionar una iluminación equilibrada que revele los detalles de la superficie.

Normalización de Alturas

El proyecto incluye una opción de normalización de alturas que ajusta dinámicamente el rango de alturas visible en cada paso de la simulación. Esto es útil cuando:

La normalización se puede activar/desactivar con la tecla N durante la simulación.

Interfaz de Usuario y Controles

Controles Principales

Atajos de Teclado

Tecla Acción
E Alterna modo pseudo-pantalla completa 3D
R Inicia/pausa la simulación
S Guarda la imagen actual como PNG
N Alterna normalización de alturas
G Alterna solver GPU (cuando está disponible)
(Reset) Restaura las condiciones iniciales

Modo Pseudo-Pantalla Completa

Al presionar E, la visualización 3D se expande para ocupar toda la pantalla, ocultando los controles y el canvas 2D. Esto permite una experiencia de visualización inmersiva. Los ejes de referencia también se ocultan automáticamente en este modo.

Optimizaciones y Rendimiento

Control de Velocidad de Animación

El control de velocidad (slider) NO modifica el paso de tiempo (dt) del solver, sino que controla la frecuencia con la que se actualiza la visualización. Esto es importante porque:

El sistema implementa dos modos de velocidad:

  1. Velocidad baja (1-50): Ejecuta un paso de la simulación cada cierto intervalo de tiempo
  2. Velocidad alta (51-99): Ejecuta múltiples pasos por frame para acelerar la visualización

Solver en GPU (WebGL)

El proyecto incluye soporte experimental para ejecutar el solver en la GPU usando WebGL shaders. Esto puede proporcionar aceleración significativa para resoluciones altas, pero actualmente está limitado a:

El indicador “GPU solver ON” aparece cuando el solver está ejecutándose en la GPU.

Nota sobre Web Workers: Hay un TODO pendiente para implementar el solver en un Web Worker, lo que permitiría ejecutar el cómputo en un hilo separado sin bloquear el hilo principal de renderizado. Esto mejoraría significativamente la fluidez de la animación, especialmente para simulaciones intensivas.

Minificación y Optimización

En producción, todos los archivos JavaScript se minifican y se combinan en un solo bundle. El CSS también se minifica. Esto reduce:

Flujo de Trabajo Completo

El flujo de trabajo completo de desarrollo y producción se puede visualizar así:

graph LR
    A[Editar archivos en src/] --> B[Previsualizar con Live Server]
    B --> C{¿Funciona correctamente?}
    C -->|Sí| D[Commit y Push a GitHub]
    C -->|No| A
    D --> E[GitHub Actions detecta push]
    E --> F[Minifica JS y CSS]
    F --> G[Despliega a GitHub Pages]
    G --> H[Disponible en usuario.github.io/repo]

Uso de MathJax para Ecuaciones

Para escribir las ecuaciones matemáticas en la interfaz, usamos MathJax v3 mediante CDN. MathJax permite renderizar ecuaciones LaTeX directamente en HTML, proporcionando una visualización de alta calidad de las fórmulas matemáticas.

Alternativamente, se podría usar MathML, que es nativo de HTML5, pero es menos legible en el código fuente y no permite usar la sintaxis LaTeX, que es más familiar para la mayoría de los científicos y matemáticos.

Polyfills y Compatibilidad

Se emplea un polyfill para URL (url-polyfill) para garantizar que ciertas características de JavaScript correspondientes a ES6 estén disponibles en navegadores más antiguos que no las soportan de manera nativa. Esto mejora la compatibilidad con navegadores legacy, aunque el proyecto está optimizado para navegadores modernos con soporte WebGL2.

Stats.js para Monitoreo de Rendimiento

Utilizamos Stats.js (desde CDN) para mostrar los FPS (frames por segundo) en tiempo real. Esto es útil para:

El contador de FPS se muestra en la esquina inferior derecha de la pantalla.

Exportación de Imágenes

La función de exportación permite guardar el estado actual de la simulación como una imagen PNG. El proceso:

  1. Crea un canvas temporal con las dimensiones originales de la imagen
  2. Aplica el colormap actual
  3. Genera un blob con los datos de la imagen
  4. Descarga el archivo usando la API de descarga del navegador

Para el modo “constant-color”, se exporta la imagen original a resolución completa. Para otros modos, se escala la imagen procesada a la resolución original.

Referencias y Trabajos Relacionados

Este proyecto está inspirado por el trabajo de chrismars91/fdm, que implementa un solver de diferencias finitas para visualización. Sin embargo, three-pde extiende significativamente estas ideas al:

Para más información sobre PDEs aplicadas a imágenes, consulta:

Para opciones de uso de Three.js en proyectos, consulta: discourse.threejs.org.

TODOs y Mejoras Futuras

Implementaciones Pendientes

  1. Control de velocidad del solver: Actualmente el slider controla la velocidad de visualización, pero sería útil poder controlar también el paso de tiempo (dt) del solver para experimentar con diferentes condiciones de estabilidad.

  2. Exportar imagen mejorado: La funcionalidad de exportación está implementada, pero se podría mejorar con opciones para elegir formato (PNG, JPEG, WebP), calidad, y resolución.

  3. Minificación de CSS automatizada: Ya está implementada en el workflow de GitHub Actions usando clean-css-cli.

  4. Selector de resolucion de heightmap: Permitir al usuario elegir el tamaño del heightmap, ya sea especificando ancho y alto, o especificando uno y manteniendo la relación de aspecto (no forzar 512×512).

  5. Esquemas numéricos adicionales: Implementar Crank-Nicholson, Forward-Backward Euler, y otros esquemas para mayor flexibilidad y precisión.

  6. Solver en Web Worker: Implementar el solver en un Web Worker para evitar bloquear el hilo principal de renderizado. Esto mejoraría significativamente la fluidez, especialmente para simulaciones intensivas. La variable needToUpdate se usaría como un mutex compartido para notificar cuando el cómputo ha terminado.

  7. Control de propiedades de material: Permitir control en tiempo real de roughness y metalness del material de la malla, así como cambiar entre materiales básicos y estándar, todo en una ventana específica de opciones de Three.js.

  8. Modo Lab original: Dedicar un párrafo completo explicando el modo original Lab y cómo funciona en contraste con los otros modos.

  9. Solver GPU mejorado: Habilitar GPU ON/OFF para todas las PDEs, no solo algunas. Inspirarse en proyectos como vertexWaves para implementaciones más avanzadas.

  10. Interpolación perspective-correct: Para interpolar valores de color de la imagen a la malla, usar interpolación perspective-correct para mayor precisión visual.

  11. Optimización de transferencia CPU-GPU: Para el solver GPU, no debería convertirse a ImageData en cada paso, ya que esto es un cuello de botella (alta carga de transferencia CPU-GPU). En su lugar, si se está en modo GPU, se debería renderizar directamente la textura en pantalla usando un quad de pantalla completa (sceneView), por ejemplo.

  12. Más PDEs: Implementar ecuaciones adicionales como la ecuación de Laplace, ecuaciones de reacción-difusión, ecuaciones de Burgers, etc.

Conclusión

three-pde es un proyecto que demuestra la potencia de combinar procesamiento de imágenes, métodos numéricos y visualización 3D en tiempo real en el navegador. Al ejecutar todo en el cliente, proporciona una experiencia interactiva inmediata sin necesidad de infraestructura de servidor, mientras mantiene la privacidad del usuario al procesar las imágenes localmente.

El proyecto sirve tanto como herramienta educativa para entender cómo funcionan las PDEs en el contexto de procesamiento de imágenes, como plataforma de experimentación para investigadores que quieren visualizar la evolución de imágenes bajo diferentes modelos físicos y matemáticos.

La arquitectura modular y el uso de tecnologías web estándar hacen que el proyecto sea fácil de extender y modificar, permitiendo a otros desarrolladores agregar nuevas PDEs, esquemas numéricos, o funcionalidades de visualización.