refactor(monorepo): reorganización lógica + renames + SDDs + split CHANGELOG

Reorganización física de crates/:
- core/ (mezclaba 6 propósitos) se divide en protocol/, init/, runtime/, compat/
- shared/ (3 crates) se redistribuye en protocol/ e init/
- lapaloma (sub-módulo de ui_engine) se promueve a modules/pineal/

Renames de proyectos:
- shipote → shuma (runtime de sandboxes)
- nouser → akasha (explorador de Mónadas)
- yahweh → nahual (motor GPUI, antes ui_engine/)
- lapaloma → pineal (data-viz agnóstica)

Fraccionamiento UI → core agnóstico:
- vista-core (DeckState + snap, 175 LOC, 5 tests verdes)
- barra-core (Task + render_html + sanitize, 90 LOC, 5 tests verdes)
- vista-web y barra-web ahora son thin DOM bindings

Documentación nueva:
- 16 SDDs por subdirectorio (≤80 LOC c/u): protocol/init/runtime/compat
  + 10 módulos + apps/
- docs/STATUS.md con cifras reales por proyecto
- docs/ROADMAP.md con plan a finalización (6 hitos, ~6-8 semanas)
- CHANGELOG.md particionado en docs/changelog/<proyecto>.md (7 buckets)

Automatización:
- scripts/reorg.py — script idempotente que: git mv directorios, renombra
  package names, recomputa path = refs, reescribe imports rust, actualiza
  workspace Cargo.toml. Soporta --dry-run.
- scripts/split-changelog.py — particiona CHANGELOG por componente.

Validación:
- cargo check --workspace pasa (124 crates + 2 nuevos cores).
- 10 tests adicionales (5 en vista-core + 5 en barra-core) verdes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-19 14:48:34 +00:00
parent 86fb6ae20b
commit 550c98f275
375 changed files with 8512 additions and 7155 deletions
+153
View File
@@ -0,0 +1,153 @@
//! Escalas value→pixel para series cartesianas.
//!
//! La proyección no se aplica sobre los datos (eso rompería el
//! P2 zero-alloc — habría que reescribir todo el buffer por frame).
//! Las escalas devuelven el `(scale_x, scale_y, translate_x,
//! translate_y)` que el painter mete en un transform GPU. Los
//! datos quedan intactos.
/// Trait común a Linear / Log / Time. Cada implementación traduce
/// un valor de dominio a posición normalizada `[0, 1]` (que luego
/// el painter mapea al pixel range del plot).
pub trait Scale {
fn to_norm(&self, value: f64) -> f64;
fn from_norm(&self, norm: f64) -> f64;
fn domain(&self) -> (f64, f64);
}
#[derive(Debug, Clone, Copy)]
pub struct LinearScale {
min: f64,
max: f64,
}
impl LinearScale {
pub fn new(min: f64, max: f64) -> Self {
debug_assert!(max > min, "LinearScale: max debe ser > min");
Self { min, max }
}
}
impl Scale for LinearScale {
fn to_norm(&self, v: f64) -> f64 {
(v - self.min) / (self.max - self.min)
}
fn from_norm(&self, n: f64) -> f64 {
self.min + n * (self.max - self.min)
}
fn domain(&self) -> (f64, f64) {
(self.min, self.max)
}
}
/// Escala logarítmica base e. `min` y `max` deben ser positivos.
#[derive(Debug, Clone, Copy)]
pub struct LogScale {
log_min: f64,
log_max: f64,
min: f64,
max: f64,
}
impl LogScale {
pub fn new(min: f64, max: f64) -> Self {
debug_assert!(min > 0.0 && max > min, "LogScale: 0 < min < max");
Self {
log_min: min.ln(),
log_max: max.ln(),
min,
max,
}
}
}
impl Scale for LogScale {
fn to_norm(&self, v: f64) -> f64 {
(v.ln() - self.log_min) / (self.log_max - self.log_min)
}
fn from_norm(&self, n: f64) -> f64 {
(self.log_min + n * (self.log_max - self.log_min)).exp()
}
fn domain(&self) -> (f64, f64) {
(self.min, self.max)
}
}
/// Escala temporal sobre epoch ms. Internamente lineal.
#[derive(Debug, Clone, Copy)]
pub struct TimeScale {
inner: LinearScale,
}
impl TimeScale {
pub fn new(min_epoch_ms: f64, max_epoch_ms: f64) -> Self {
Self {
inner: LinearScale::new(min_epoch_ms, max_epoch_ms),
}
}
}
impl Scale for TimeScale {
fn to_norm(&self, v: f64) -> f64 {
self.inner.to_norm(v)
}
fn from_norm(&self, n: f64) -> f64 {
self.inner.from_norm(n)
}
fn domain(&self) -> (f64, f64) {
self.inner.domain()
}
}
/// Wilkinson "nice numbers" — devuelve el step ideal en `{1, 2, 5} × 10^k`
/// para que un rango `[min, max]` tenga ~`target_ticks` divisiones.
pub fn nice_step(min: f64, max: f64, target_ticks: usize) -> f64 {
debug_assert!(max > min && target_ticks > 0);
let raw = (max - min) / target_ticks as f64;
let mag = 10f64.powf(raw.log10().floor());
let norm = raw / mag;
let nice = if norm < 1.5 {
1.0
} else if norm < 3.0 {
2.0
} else if norm < 7.0 {
5.0
} else {
10.0
};
nice * mag
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn linear_roundtrip() {
let s = LinearScale::new(10.0, 20.0);
assert!((s.to_norm(15.0) - 0.5).abs() < 1e-9);
assert!((s.from_norm(0.5) - 15.0).abs() < 1e-9);
}
#[test]
fn log_roundtrip() {
let s = LogScale::new(1.0, 1000.0);
// 10 está a 1/3 del camino en log10. ln(10)/ln(1000) = 1/3.
assert!((s.to_norm(10.0) - 1.0 / 3.0).abs() < 1e-9);
assert!((s.from_norm(2.0 / 3.0) - 100.0).abs() < 1e-9);
}
#[test]
fn nice_step_es_potencia() {
// 100/5 = 20 — exact match para el branch nice=2.0 · mag=10.
assert!((nice_step(0.0, 100.0, 5) - 20.0).abs() < 1e-9);
// 1.0/10 = 0.1 — branch nice=1.0 · mag=0.1.
assert!((nice_step(0.0, 1.0, 10) - 0.1).abs() < 1e-9);
// 14/5 = 2.8 — branch nice=2.0 (1.5 ≤ norm < 3) · mag=1.
assert!((nice_step(0.0, 14.0, 5) - 2.0).abs() < 1e-9);
// 7/5 = 1.4 — cae bajo 1.5 → snap a 1.0 · mag=1 = 1.0.
assert!((nice_step(0.0, 7.0, 5) - 1.0).abs() < 1e-9);
// 50/5 = 10 — branch nice=10 · mag=1 = 10. (Equivalente a 1·10.)
assert!((nice_step(0.0, 50.0, 5) - 10.0).abs() < 1e-9);
}
}