FORK.md · generada al construir
El fork de ManimGX en Tórculo
Qué cambia el fork de ManimGX que estampa los pliegos y dónde engancha cada pieza del estándar.
Repositorio: ~/Developer/torculo/manimgx (remoto upstream = academa-labs/manimgx, base 9be4111).
Ramas: seams (enganches genéricos, candidatos a contribuir al original) y saorin (lo artístico).
seams es la rama del motor: el 14-10-2026 se le fusionaron seams-post, seams-materia y seams-plataforma
(06bba13), y saorin se adelantó hasta ella. Los worktrees wt/post, wt/materia y wt/plataforma se quitaron;
queda wt/materia-scratch/base (9be4111, con base_hashes.json y corpus_hashes.py) para comparar el corpus.
Compilar y probar: uv sync (unos 4 min la primera vez), uv run manimgx render x.py,
just test-rust, just test, just bench main (requiere que no haya regresión cuando lo nuevo está apagado).
Lee AGENTS.md del repo antes de tocar nada.
Reglas del fork
- La lógica nueva vive en ficheros nuevos (
post.rs,material.rs,vt.rs,pick.rs,*.wgsl.tmpl, paquete Pythonmanimgx/saorin/o similar). Enrender.rs,export.rs,player.rs,vector.wgsl,feed.pysolo unas pocas líneas de enganche. Upstream reescribe esos ficheros a menudo. - Las tomas del fork usan un rango propio de versión: hoy 0x8000_0002 (
take::VERSION; registros de 424 B, POST = 15, MATERIAL = 16). Se siguen leyendo las de 0x8000_0001 de seams-materia (MATERIAL era el 15). - Claves de canalización de la composición (vector.rs): LINEAR = 32, BLENDS = 64, MATERIALS = 128, KEYS = 256.
- Todo lo nuevo apagado por defecto no puede cambiar ni un píxel del corpus de referencia.
- Determinismo: el tiempo que ven los shaders es el de la escena (o el índice de fotograma), nunca el reloj.
- Commits en español, sin emojis, terminados en «Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com».
- Canon de la casa:
~/Developer/torculo/canon/gen/canon.{wgsl,py,rs}. Papel #F5F0E8, tinta #2C1810. - Materias WGSL compartidas:
~/Developer/torculo/materias(convención de pasada: entrada rgba16float lineal premultiplicada, sampler, uniformes {resolucion, fotograma, tiempo, semilla, params[4]}, salida).
Hechos del código (leídos con fichero:línea)
- GPU singleton:
static GPUrender.rs:1016,with_gpu:1020, wasm thread-local :1029. Requiere R32Float blending y Depth32FloatStencil8 (render.rs:1070-1086). - Composición 2D en 8 bits:
COLOR = Rgba8Unormrender.rs:44; solo 3D iluminado enRADIANCE = Rgba16Float:46; la composición escribetexture_storage_2d<rgba8unorm>vector_compute.wgsl:5 víastore()/seen(). Pintura en sRGB premultiplicado (paint.wgsl:49). - Pintura por píxel en la composición:
painted()vector.wgsl:1301 →paint():957 →paint_of():1324; capas conover():1094; modelo de dos regioneslay*()/split_by():1149-1233. - Ancho de trazo constante por objeto:
half = params.zvector.wgsl:803;body_slot/joint_slot/cap_slot:669/:727/:754; raster de trazo a atlas R32Float (vector.rs:722, :1207;fs_strokevector.wgsl:923). - Guiones en espacio de parámetro, no de longitud de arco (vector.wgsl:241-261).
- Registro de 336 B por objeto y fotograma: render.rs:113-135, take.rs:50, feed.py:79-109,
Instancerender.rs:91,vector::Objectvector.rs:21 (432 B), accesor en vector_buffers.wgsl:31-43. Claves de forma: take.rs:49 KEYS=7,slots():53; feed.py KEYS (:111). Desalojo KEEP=120, SWEEP=240, BUDGET=768 MB (feed.py:53-56). - Fotograma final:
encode_frame()render.rs:2061 (devuelve en :2506) aTargets.frame.color; lo leen export.rs:107 (convert), render.rs:1783 (copy_pixels) y player.rs:268 (Screen::composite). - Codificación por daño:
damage()export.rs:18,wholeexport.rs:83, NV12 BT.601 limitado nv12.wgsl:3, x264 shim.c:39, MP4 mp4.rs:399; fotogramas retenidos conrepeat(film.py:482_send); el reproductor cachea el fotograma (project.rs:434). - Peticiones reproductor→anfitrión:
asks()web.rs:141; browser/src/engine.ts. - LaTeX mal formado pasa:
mitex_mathtypeset.rs:752; avisos de Typst descartados typeset.rs:568-575;_balancedexiste en text.py:1180. - Voces:
Voice = Callable[[str], Speech]sound.py:404;Scene.speechcae enFal()de pago sin avisar (scene.py:1683).Worden manimgx.audio.sound. Estimación por letras sound.py:417-450; caché :513-549. - Pixels de
frames=en RGBA premultiplicado (153,80,28,153 para #FF862F a 0,6).
Plan por punto (resumen; los detalles están en el informe de investigación)
- Postproceso:
post.rs(modelobloom.rs), llamado al final deencode_frame;config.post = [Pass(...)]; mensaje de tomaPOST; en el reproductor sobre el lienzo cacheado (picture()player.rs ~946). Composición lineal opcional en Rgba16Float (config.compositing="linear"), OETF sRGB a nv12.wgsl y al COMPOSITE del reproductor. Daño: pasadas locales dilatan macrobloques; pasadas que esparcen →whole; temporales → expandir retenidos (push_framesexport.rs:45 y_sendfilm.py:482). - Materiales: registro +
material:u32,blend:u32,user_id:u64,user:[f32;16](336→416 B);register_material(nombre, wgsl)→ mensajeMATERIAL; switch generado inyectado en la composición; clave de caché de pipelines (render.rs:1000); texturas de material entexture_2d_array. Mezcla multiplicar:blend(mode, p, below)enlay,lay_line,split_by; rasters víalay_raster:1432. - Trazo variable: clave
width_rows(KEYS=8),half(u)enstroke_slot,body():461,joint_record:493,cap_record/end_cap:520/:533; huella con el ancho máximo (render.rs:836); longitud de arco acumulada enadd_path(render.rs:2625); textura de pincel enfs_stroke; canal extra de atlas Rg32Float. - Hervor: N poses subidas una vez, cambia
key1(sin fork); versión GPU desplazando puntos en el aplanado (curve()/at()vector.wgsl:187/:200) conhash(objeto, curva, floor(t·fps)). - Biblioteca: crate-type rlib+cdylib,
pythonfuera de default, fachadamanimgx-renderconGpu::from_device,Player,Replay,typeset(); reproductor npm de tomas sin Pyodide (<manimgx-film src="x.take">,Player.feedweb.rs:69 → project.rs:325);manimgx render --take. - Interactividad: textura de selección R32Uint en
composite(vector_compute.wgsl:60) conuser_id; eventos en player.rs 695-728 víaasks();await self.espera(Clic(pieza)). - VideoToolbox: features
export-x264/export-vt;vt.rscon objc2;p010.wgslBT.709; HEVC Main10, ProRes 422 HQ y 4444 con alfa (desmultiplicar); AVAssetWriter; IOSurface para el laboratorio de película. - Móvil: reproductor de tomas iOS (xcframework, uniffi, CAMetalLayer); Android con camino alternativo de cobertura por compute si falta la mezcla de float32.
- Voz:
sidecar(dir)que lee WAV +.tiempos.jsonde .mesa ({beats:{id:{dur,pause,palabras:[{w,t,d}]}}, wavs:{id:path}}); la voz por defecto da error en vez de llamar a Fal. - Errores estrictos:
_balanced,\left/\right,^/_colgantes,zwscomo argumento; avisos de Typst devueltos;--strict/MANIMGX_STRICT=1;inspectmarca glifos vacíos.
La emulsión: pasadas de clase «grafo» (rama emulsion, 15-10-2026)
rust/engine/src/grafo.rs(nuevo): un grafo de pasadas de cómputo de un mismo shader, con texturas propias a fracciones del tamaño; la pasada de la cadena trae el WGSL ensource, el grafo enentry(una pasada por línea:punto lee,lee escribe divisor p0 p1 p2 p3), los números enparams(búfer de almacenamiento) y la gelatina como volumen. Enganches enpost.rs(clase aceptada,Stage::Graph, suencode) y una línea enrender.rs.- Python:
manimgx/saorin/pelicula.py(m.Pelicula), que pide la ficha atorculo_emulsion(TORCULO/emulsion). - El mismo grafo lo ejecutan
materias/src/grafo.rs(materia revela) yemulsion/web/grafo.js(WebGPU).