creation
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "lapaloma-render"
|
||||
version = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
license = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
publish = { workspace = true }
|
||||
description = "Lapaloma — abstracción de painter: trait Canvas + RenderPlan + color helpers. Habilita backend CPU (gpui hoy) y GPU (wgpu mañana) sin tocar a los painters."
|
||||
|
||||
[dependencies]
|
||||
lapaloma-core = { path = "../../libs/lapaloma-core" }
|
||||
@@ -0,0 +1,53 @@
|
||||
//! El trait `Canvas` que todos los painters consumen.
|
||||
//!
|
||||
//! Mantenemos el set mínimo: line / polyline / rect (fill+stroke) /
|
||||
//! triangle strip. Cualquier visualización compleja (curvas
|
||||
//! bezier, gradients) se descompone en estos primitivos por el
|
||||
//! painter — el backend no necesita entender la semántica.
|
||||
//!
|
||||
//! Convención: coordenadas en píxeles del viewport, origen
|
||||
//! arriba-izquierda, +Y hacia abajo. La proyección de datos→pixel
|
||||
//! la hace el painter via las escalas de `lapaloma-core`.
|
||||
|
||||
use crate::{Color, Point, Rect};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct StrokeStyle {
|
||||
pub width: f32,
|
||||
pub color: Color,
|
||||
}
|
||||
|
||||
impl StrokeStyle {
|
||||
pub const fn new(width: f32, color: Color) -> Self {
|
||||
Self { width, color }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Canvas {
|
||||
/// Clip subsiguiente al rect dado. Stack-discipline:
|
||||
/// `push_clip` + draw + `pop_clip`.
|
||||
fn push_clip(&mut self, rect: Rect);
|
||||
fn pop_clip(&mut self);
|
||||
|
||||
/// Rectángulo relleno (sin stroke).
|
||||
fn fill_rect(&mut self, rect: Rect, color: Color);
|
||||
|
||||
/// Rectángulo sólo stroke (sin fill).
|
||||
fn stroke_rect(&mut self, rect: Rect, stroke: StrokeStyle);
|
||||
|
||||
/// Línea de a→b.
|
||||
fn stroke_line(&mut self, a: Point, b: Point, stroke: StrokeStyle);
|
||||
|
||||
/// Polilínea sobre coords interleaved `[x0,y0,x1,y1,…]`.
|
||||
/// El backend la rendea como un solo draw call cuando puede.
|
||||
fn stroke_polyline(&mut self, coords: &[f32], stroke: StrokeStyle);
|
||||
|
||||
/// Triangle strip rellenado, con un color por vértice
|
||||
/// (longitudes deben coincidir: `coords.len()/2 == colors.len()`).
|
||||
/// Es lo que usa el phosphor trail y los ribbons Sankey.
|
||||
fn fill_triangle_strip(&mut self, coords: &[f32], colors: &[Color]);
|
||||
|
||||
/// Glyph de texto sencillo. El layout va a un text-cache
|
||||
/// dentro del backend; por ahora un trazo simple.
|
||||
fn draw_text(&mut self, p: Point, text: &str, color: Color, size_px: f32);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//! Color RGBA en f32, agnóstico de backend.
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Color {
|
||||
pub r: f32,
|
||||
pub g: f32,
|
||||
pub b: f32,
|
||||
pub a: f32,
|
||||
}
|
||||
|
||||
impl Color {
|
||||
pub const TRANSPARENT: Self = Self::rgba(0.0, 0.0, 0.0, 0.0);
|
||||
pub const BLACK: Self = Self::rgb(0.0, 0.0, 0.0);
|
||||
pub const WHITE: Self = Self::rgb(1.0, 1.0, 1.0);
|
||||
|
||||
pub const fn rgb(r: f32, g: f32, b: f32) -> Self {
|
||||
Self { r, g, b, a: 1.0 }
|
||||
}
|
||||
pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self {
|
||||
Self { r, g, b, a }
|
||||
}
|
||||
|
||||
/// Construye desde 0xRRGGBB hex literal.
|
||||
pub fn from_hex(rgb: u32) -> Self {
|
||||
let r = ((rgb >> 16) & 0xff) as f32 / 255.0;
|
||||
let g = ((rgb >> 8) & 0xff) as f32 / 255.0;
|
||||
let b = (rgb & 0xff) as f32 / 255.0;
|
||||
Self::rgb(r, g, b)
|
||||
}
|
||||
|
||||
/// Multiplica el canal alpha — útil para fade del phosphor trail.
|
||||
pub fn with_alpha(self, a: f32) -> Self {
|
||||
Self { a, ..self }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
//! Tipos geométricos mínimos en `f32`.
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Point {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
}
|
||||
|
||||
impl Point {
|
||||
pub const fn new(x: f32, y: f32) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Rect {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub w: f32,
|
||||
pub h: f32,
|
||||
}
|
||||
|
||||
impl Rect {
|
||||
pub const fn new(x: f32, y: f32, w: f32, h: f32) -> Self {
|
||||
Self { x, y, w, h }
|
||||
}
|
||||
pub fn right(&self) -> f32 {
|
||||
self.x + self.w
|
||||
}
|
||||
pub fn bottom(&self) -> f32 {
|
||||
self.y + self.h
|
||||
}
|
||||
pub fn contains(&self, p: Point) -> bool {
|
||||
p.x >= self.x && p.x <= self.right() && p.y >= self.y && p.y <= self.bottom()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
//! `lapaloma-render` — abstracción de painter.
|
||||
//!
|
||||
//! Los crates de visualización (cartesian, mesh, polar…) no
|
||||
//! conocen `gpui` ni `wgpu`. Hablan contra el trait [`Canvas`]
|
||||
//! definido acá. Eso permite:
|
||||
//!
|
||||
//! - **Backend CPU sobre gpui** — implementación por defecto;
|
||||
//! sirve para series de hasta ~50 k vértices a 60 FPS sin
|
||||
//! sudar.
|
||||
//! - **Backend GPU sobre wgpu** — placeholder hoy; cuando un
|
||||
//! módulo le pegue al wall (millones de puntos, force-sim
|
||||
//! pesada), se enchufa sin tocar la lógica de los painters.
|
||||
//! - **Backend SVG** — `lapaloma-export` implementa el mismo
|
||||
//! trait emitiendo elementos `<path>`, `<polyline>`, etc.
|
||||
//!
|
||||
//! Tipos primitivos (`Color`, `Point`, `Rect`) viven acá para
|
||||
//! no atarlos a `gpui::Rgba`/`gpui::Point` — los backends
|
||||
//! traducen al tipo nativo del runtime que les toca.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub mod color;
|
||||
pub mod geom;
|
||||
pub mod canvas;
|
||||
pub mod plan;
|
||||
|
||||
pub use color::Color;
|
||||
pub use geom::{Point, Rect};
|
||||
pub use canvas::{Canvas, StrokeStyle};
|
||||
pub use plan::{RenderCmd, RenderPlan};
|
||||
@@ -0,0 +1,35 @@
|
||||
//! `RenderPlan` — comandos materializados para backends que no
|
||||
//! reciben llamadas en vivo (SVG export, snapshot testing).
|
||||
//!
|
||||
//! Un painter que escribe contra [`crate::Canvas`] puede ser
|
||||
//! capturado en un `RenderPlan` usando un `Canvas` adapter que
|
||||
//! empuja `RenderCmd`s en lugar de dibujar. El exporter consume
|
||||
//! el plan y emite `<polyline>` / `<rect>` / etc.
|
||||
|
||||
use crate::{Color, Point, Rect, StrokeStyle};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum RenderCmd {
|
||||
PushClip(Rect),
|
||||
PopClip,
|
||||
FillRect { rect: Rect, color: Color },
|
||||
StrokeRect { rect: Rect, stroke: StrokeStyle },
|
||||
StrokeLine { a: Point, b: Point, stroke: StrokeStyle },
|
||||
StrokePolyline { coords: Vec<f32>, stroke: StrokeStyle },
|
||||
FillTriangleStrip { coords: Vec<f32>, colors: Vec<Color> },
|
||||
DrawText { p: Point, text: String, color: Color, size_px: f32 },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct RenderPlan {
|
||||
pub cmds: Vec<RenderCmd>,
|
||||
}
|
||||
|
||||
impl RenderPlan {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
pub fn push(&mut self, cmd: RenderCmd) {
|
||||
self.cmds.push(cmd);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user