Antes los hijos del mismo anchor se distribuian en un anillo de 360 grados alrededor del padre. Cuando un enricher producia 10+ hijos, se llenaban todas las direcciones y se pisaban nodos preexistentes. Ahora los hijos se reparten en un abanico de 45 grados (pi/4) saliendo del anchor en la direccion outward (vector anchor - centroide del resto del grafo). Si solo hay 1 nodo placed o coincide con el anchor, default a la derecha (0 rad). Capacidad por anillo restringida al arco (arc_span * r / min_dist), con fallback de subida de radio en mismo angulo si el slot ideal colisiona con un nodo no-orphan. Solo afecta la pasada 2 (orphans con anchor). Pasadas 1 y 3 intactas. build limpio, 102 pytest passed (WSL) + 91 passed/11 skipped (Windows). Refs: issues/0037-directional-orphan-placement.md
3.4 KiB
id, title, status, priority, created, completed
| id | title | status | priority | created | completed |
|---|---|---|---|---|---|
| 0037 | Placement direccional de orphans — abanico de 45 grados en una sola direccion | done | high | 2026-05-04 | 2026-05-03 |
Contexto
Hoy place_orphans_near_neighbors (en main.cpp) reparte los hijos del
mismo anchor en un anillo de 360 grados alrededor del padre. Resultado:
con muchos hijos, el viewport se llena en todas las direcciones,
pisando nodos preexistentes y dificultando la lectura del grafo.
Issue 0035 ya cluster los hijos del mismo anchor (no los desperdiga por anillos crecientes), pero siguen ocupando 360 grados.
Objetivo
Que los nuevos hijos creados por un enricher (orphans con un anchor
detectado por layout_first_placed_neighbor) se desplieguen en un
abanico de 45 grados (no 360) en UNA sola direccion saliendo del
anchor. Asi el grafo "crece" en una direccion clara por cada
ejecucion de enricher, sin pisar lo existente.
Diseño
1. Direccion del abanico — outward por defecto
Para cada anchor con orphans:
- Calcular el centroide del resto de nodos placed del grafo (excluyendo los orphans propios y el anchor).
- La direccion outward es el vector
anchor - centroide, normalizado. - Si solo hay 1 nodo en el grafo (el propio anchor) o el centroide
coincide con el anchor: usar direccion
(1, 0)(derecha).
2. Reparto en abanico de 45 grados
arc_span = π / 4 (45 grados, configurable como constante).
Para los N orphans del anchor:
- Si
N == 1: lo plantamos exactamente en la direccion outward al radio base (~80 px). - Si
N <= 8(capacidad del primer anillo en 45 grados con min_dist): se reparten equiespaciados en el arco a un radio fijo. - Si
N > 8: se llenan anillos sucesivos del mismo arco con radios crecientes (80, 140, 200, 280, 400) y misma capacidad por anillo.
Cada slot dentro del arco tiene angulo:
angle = out_angle - arc_span/2 + slot * (arc_span / (slot_count - 1))
(ajusta para cuando slot_count == 1 para no dividir entre cero).
3. Colision con nodos preexistentes
Despues de calcular (px, py) para cada hijo, si colisiona con un
nodo no-orphan, incrementar el indice de anillo (mismo angulo,
radio mayor) hasta encontrar hueco o agotar anillos. En el ultimo
recurso aceptar solape — coherente con la logica actual.
4. Fallback sin anchor
Los orphans sin anchor (no tienen vecino placed) siguen el camino existente: ring placement alrededor de la camara o parking lot. NO se les aplica el abanico.
Acceptance criteria
- Lanzar
split_words(≥50 palabras unicas) sobre un nodotext: los 10 sueltos del preview + 1 Group caen en un abanico de 45 grados saliendo del nodotext, en la direccion alejada del resto del grafo. - Lanzar el mismo enricher otra vez sobre OTRO nodo en otra posicion: el abanico nuevo apunta hacia su propia direccion outward.
- Si el grafo solo tiene 1 nodo (sin existing centroide), el abanico sale a la derecha por default.
- Tests pytest siguen verdes (no requiere tests nuevos — el cambio es algoritmico y se valida visualmente; opcional anyadir un test C++ standalone que verifique angulos contra una fixture).
TBD
Branch issue/0037-directional-orphan-placement, merge --no-ff a
master.
Out of scope
- Animar la transicion del placement (poof-in suave). Fase 2.
- Layout interno del Group cuando se expande — sigue siendo todos los hijos del Group ocultos en colapsado.
- Grouping logic (eso es 0035, ya cerrado fase 1).