diff options
| author | Ruben Beltran del Rio <jj@r.bdr.sh> | 2025-12-16 11:09:49 +0100 |
|---|---|---|
| committer | Ruben Beltran del Rio <jj@r.bdr.sh> | 2025-12-16 11:15:32 +0100 |
| commit | 6deeee3848e291dee61e239f104dc29bb26f4608 (patch) | |
| tree | e97308fc347e2abef2272942a7e5d71e56ac0469 | |
| parent | f08648e4b5a9f7855a2ed9068a386e2f3c596322 (diff) | |
Benchmark with example, and raw rendering
| -rw-r--r-- | benches/example.wmap | 122 | ||||
| -rw-r--r-- | benches/rendering.rs | 21 | ||||
| -rw-r--r-- | src/lib.rs | 20 | ||||
| -rw-r--r-- | src/renderer.rs | 84 |
4 files changed, 204 insertions, 43 deletions
diff --git a/benches/example.wmap b/benches/example.wmap new file mode 100644 index 0000000..a238ab1 --- /dev/null +++ b/benches/example.wmap @@ -0,0 +1,122 @@ +Anchor (40, 0) +Tinker (4,30) [x] +Tailor (7.03, 49.21) [Square] +Soldier (28, 15) [Triangle] +AMnchor (28.22, 45.27) +Tonker (67.3, 4.4) [X] +Tailor1 (1,1) [Square] +Tailor2 (10,1) [Square] +Tailor3 (1,10) [Square] +Tailor4 (20,1) [Square] +Tailor5 (1,20) [Square] +Tailor7 (20,10) [Square] +Tailor8 (9.36, 16.97) [Square] +Tailor777(30,10) [Square] +Tailor10 (18.77, 26.25) [Square] +Tailor11 (10,10) [Square] +Tailor12 (30,20) +Tailor13 (31.10, 28.89) +Tailor14 (13.44, 21.48) [Square] +Tailor19 (45, 2) [triangle] +B (50, 50) +Hellele + +TailorX (20, 50) [Square] + +Hello World (90, 20) [x] + +Anchor -> Soldier… this is changing. +Anchor -> A +Masdlajksd -- Tailor13 + +Mabanchorn -- Anchornalsdjalks + +A +--B + +X (58.52, 27.07) + +Tailor11--Tailor12 +Tailor12--Tailor13 +Tailor777--Tailor8 +Tailor777--Tailor7 +Tailor777--Tailor6 +Tailor7-Tailor6 + +Tinker -- Tailor +Soldier -- Tinker + +[Evolution] Tinker +20 +[Inertia] Soldier + +[i] 15 +[ii] 35 +[iii] 80 + +this is kind of ok … a little bit better to be honest. (69.76, 89.84) + +OK. + +This map so far is fst. asdasd asda + +Holy shit, this is still indeed fast. But everything flashes as I type! This is now happening so fast + +Hello my old friend. + +OK so this map should eventually start getting slow too! + +let’s see how long after.asd asda sda + +asda sdasdasd asdasdasdasdasdasd +OK… Well we are typing. asd as asd as dasd a asdasda sdasd asd + + asd asd asd asdasd asdasd +Alright, the whole text is still fa asdlashing. Wow. This is pretty bad. +aas dasd adsa sd a asd asd asdjlkasjd laksdjasd basldjas!!! + +we have deleted this (20, 40) [triangle] + +a(12, 12) +Time to see if we get the same performance loss as withthis the other one. Well I will continue. Hello this is me. and i’m +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + +b(75,75) +c(9, 90) + +a -- b +b -- c + +d (45, 88) + +c -- d +d -- a +d -- b + +q (90, 60) +b -- q +q -- a +q -- c + +[Evolution] c +20 +[Evolution] d +10 +[Evolution] a -20 +[Evolution] q +5 + +m(50, 60) +n (65, 65) +o (45, 63) + +m -- o +o -- n +n -- m +o -- c +n -- q +m -- d + +testasd asdas +test2asdasa asdasd asd + +[Note] (5, 35) Notes also influence the placement of\n labels. + +[Group] Tailor2, Tailor7, Tailor11, Tailor12, Tailor10, Anchor, Tinker +[Group] Tailor3, Tailor5, Tailor14pr
\ No newline at end of file diff --git a/benches/rendering.rs b/benches/rendering.rs index 549ad21..51e1b13 100644 --- a/benches/rendering.rs +++ b/benches/rendering.rs @@ -1,23 +1,22 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; use std::fs; -use wmap_renderer::{render_to_png, Configuration, StageType}; +use wmap_renderer::{render_to_surface, Configuration, StageType}; fn benchmark_rendering(c: &mut Criterion) { - // Read the huge.wmap file for realistic benchmarking - let source = fs::read_to_string("benches/huge.wmap").expect("Failed to read benches/huge.wmap"); - let map = wmap_parser::parse(&source); let config = Configuration::default(); - c.bench_function("render huge map", |b| { - b.iter(|| render_to_png(black_box(&map), StageType::default(), &config)) + let example_source = fs::read_to_string("benches/example.wmap").expect("Failed to read benches/example.wmap"); + let example_map = wmap_parser::parse(&example_source); + + c.bench_function("render example map", |b| { + b.iter(|| render_to_surface(black_box(&example_map), StageType::default(), &config)) }); - // Also benchmark a simple map - let simple_source = "Tea (90, 50)\nCup (80, 40)\nTea -> Cup"; - let simple_map = wmap_parser::parse(simple_source); + let huge_source = fs::read_to_string("benches/huge.wmap").expect("Failed to read benches/huge.wmap"); + let huge_map = wmap_parser::parse(&huge_source); - c.bench_function("render simple map", |b| { - b.iter(|| render_to_png(black_box(&simple_map), StageType::default(), &config)) + c.bench_function("render huge map", |b| { + b.iter(|| render_to_surface(black_box(&huge_map), StageType::default(), &config)) }); } @@ -19,6 +19,7 @@ pub use configuration::{ pub use stage_type::StageType; use thiserror::Error; +use cairo::ImageSurface; /// Error types for rendering #[derive(Debug, Error)] @@ -86,6 +87,25 @@ pub fn render_to_svg( renderer::render_to_svg(map, stage_type, config) } +/// Renders a Wardley map to a cairo context. +/// +/// # Arguments +/// +/// * `map` - The parsed Wardley map +/// * `stage_type` - The stage type to use for axis labels +/// * `config` - Configuration options +/// +/// # Returns +/// +/// A string containing the SVG data +pub fn render_to_surface( + map: &Map, + stage_type: StageType, + config: &Configuration, +) -> Result<ImageSurface, RenderError> { + renderer::render_to_surface(map, stage_type, config) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/renderer.rs b/src/renderer.rs index 9991d56..8ac025b 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -5,6 +5,7 @@ use crate::configuration::Configuration; use crate::stage_type::StageType; use crate::utils::{percent_to_pixel, set_color}; use crate::RenderError; +use cairo::{Context, Error, FontSlant, FontWeight, Format, ImageSurface, LineCap, LineJoin, Operator, SvgSurface}; /// Renders a map to PNG format pub fn render_to_png( @@ -17,8 +18,8 @@ pub fn render_to_png( + config.theme.sizes.padding + config.theme.sizes.bottom_padding) as i32; - let surface = cairo::ImageSurface::create(cairo::Format::ARgb32, total_width, total_height)?; - let ctx = cairo::Context::new(&surface)?; + let surface = ImageSurface::create(Format::ARgb32, total_width, total_height)?; + let ctx = Context::new(&surface)?; render(&ctx, map, stage_type, config)?; @@ -27,6 +28,25 @@ pub fn render_to_png( Ok(buffer.into_inner()) } +/// Renders a map to the context +pub fn render_to_surface( + map: &Map, + stage_type: StageType, + config: &Configuration, +) -> Result<ImageSurface, RenderError> { + let total_width = (config.theme.sizes.map_width + config.theme.sizes.padding * 2.0) as i32; + let total_height = (config.theme.sizes.map_height + + config.theme.sizes.padding + + config.theme.sizes.bottom_padding) as i32; + + let surface = ImageSurface::create(Format::ARgb32, total_width, total_height)?; + let ctx = Context::new(&surface)?; + + render(&ctx, map, stage_type, config)?; + + Ok(surface) +} + /// Renders a map to SVG format pub fn render_to_svg( map: &Map, @@ -47,8 +67,8 @@ pub fn render_to_svg( .to_str() .ok_or_else(|| RenderError::InvalidConfig("Failed to create temp file path".to_string()))?; - let surface = cairo::SvgSurface::new(total_width, total_height, Some(path))?; - let ctx = cairo::Context::new(&surface)?; + let surface = SvgSurface::new(total_width, total_height, Some(path))?; + let ctx = Context::new(&surface)?; render(&ctx, map, stage_type, config)?; @@ -61,7 +81,7 @@ pub fn render_to_svg( /// Core rendering function fn render( - ctx: &cairo::Context, + ctx: &Context, map: &Map, stage_type: StageType, config: &Configuration, @@ -91,7 +111,7 @@ fn render( Ok(()) } -fn draw_background(ctx: &cairo::Context, config: &Configuration) -> Result<(), RenderError> { +fn draw_background(ctx: &Context, config: &Configuration) -> Result<(), RenderError> { ctx.save()?; set_color(ctx, &config.theme.colors.background); ctx.paint()?; @@ -100,7 +120,7 @@ fn draw_background(ctx: &cairo::Context, config: &Configuration) -> Result<(), R } fn draw_stage_patterns( - ctx: &cairo::Context, + ctx: &Context, _stage_type: StageType, map: &Map, config: &Configuration, @@ -173,7 +193,7 @@ fn draw_stage_patterns( } fn draw_axes( - ctx: &cairo::Context, + ctx: &Context, stage_type: StageType, map: &Map, config: &Configuration, @@ -197,8 +217,8 @@ fn draw_axes( set_color(ctx, &config.theme.colors.label); ctx.select_font_face( "sans-serif", - cairo::FontSlant::Normal, - cairo::FontWeight::Normal, + FontSlant::Normal, + FontWeight::Normal, ); ctx.set_font_size(fonts.axis_label); ctx.set_line_width(1.0); @@ -267,7 +287,7 @@ fn draw_axes( } fn draw_stage_dividers( - ctx: &cairo::Context, + ctx: &Context, map: &Map, config: &Configuration, ) -> Result<(), RenderError> { @@ -289,7 +309,7 @@ fn draw_stage_dividers( } fn draw_dependencies( - ctx: &cairo::Context, + ctx: &Context, map: &Map, config: &Configuration, ) -> Result<(), RenderError> { @@ -365,7 +385,7 @@ fn draw_dependencies( Ok(()) } -fn draw_groups(ctx: &cairo::Context, map: &Map, config: &Configuration) -> Result<(), RenderError> { +fn draw_groups(ctx: &Context, map: &Map, config: &Configuration) -> Result<(), RenderError> { use geo::{ConcaveHull, LineString, Point}; use std::collections::HashMap; use std::f64::consts::PI; @@ -381,8 +401,8 @@ fn draw_groups(ctx: &cairo::Context, map: &Map, config: &Configuration) -> Resul // Create offscreen surface for opacity handling let width = (config.theme.sizes.map_width + config.theme.sizes.vertex_size) as i32; let height = (config.theme.sizes.map_height + config.theme.sizes.vertex_size) as i32; - let offscreen = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?; - let offscreen_ctx = cairo::Context::new(&offscreen)?; + let offscreen = ImageSurface::create(Format::ARgb32, width, height)?; + let offscreen_ctx = Context::new(&offscreen)?; // Translate to account for vertex width/height offscreen_ctx.translate( @@ -435,9 +455,9 @@ fn draw_groups(ctx: &cairo::Context, map: &Map, config: &Configuration) -> Resul } // Clear offscreen canvas - offscreen_ctx.set_operator(cairo::Operator::Clear); + offscreen_ctx.set_operator(Operator::Clear); offscreen_ctx.paint()?; - offscreen_ctx.set_operator(cairo::Operator::Over); + offscreen_ctx.set_operator(Operator::Over); // Draw path set_color(&offscreen_ctx, color); @@ -454,8 +474,8 @@ fn draw_groups(ctx: &cairo::Context, map: &Map, config: &Configuration) -> Resul // Draw stroke with round caps and joins for smoother appearance offscreen_ctx.set_line_width(config.theme.sizes.vertex_size * 1.75); - offscreen_ctx.set_line_cap(cairo::LineCap::Round); - offscreen_ctx.set_line_join(cairo::LineJoin::Round); + offscreen_ctx.set_line_cap(LineCap::Round); + offscreen_ctx.set_line_join(LineJoin::Round); offscreen_ctx.stroke()?; // Composite onto main context with opacity @@ -469,7 +489,7 @@ fn draw_groups(ctx: &cairo::Context, map: &Map, config: &Configuration) -> Resul } fn draw_vertices( - ctx: &cairo::Context, + ctx: &Context, map: &Map, config: &Configuration, ) -> Result<(), RenderError> { @@ -497,7 +517,7 @@ fn draw_vertices( } fn draw_vertex_labels( - ctx: &cairo::Context, + ctx: &Context, map: &Map, config: &Configuration, ) -> Result<(), RenderError> { @@ -505,8 +525,8 @@ fn draw_vertex_labels( set_color(ctx, &config.theme.colors.label); ctx.select_font_face( "sans-serif", - cairo::FontSlant::Normal, - cairo::FontWeight::Normal, + FontSlant::Normal, + FontWeight::Normal, ); ctx.set_font_size(config.theme.fonts.vertex_label); @@ -546,7 +566,7 @@ fn draw_vertex_labels( } fn draw_inertias( - ctx: &cairo::Context, + ctx: &Context, map: &Map, config: &Configuration, ) -> Result<(), RenderError> { @@ -584,13 +604,13 @@ fn draw_inertias( } fn draw_rounded_rect( - ctx: &cairo::Context, + ctx: &Context, x: f64, y: f64, width: f64, height: f64, radius: f64, -) -> Result<(), cairo::Error> { +) -> Result<(), Error> { let r = radius; ctx.new_path(); ctx.arc( @@ -627,10 +647,10 @@ fn draw_rounded_rect( /// Wraps text to fit within a given width, breaking at word boundaries fn wrap_text( - ctx: &cairo::Context, + ctx: &Context, text: &str, max_width: f64, -) -> Result<Vec<String>, cairo::Error> { +) -> Result<Vec<String>, Error> { let words: Vec<&str> = text.split_whitespace().collect(); let mut lines = Vec::new(); let mut current_line = String::new(); @@ -667,7 +687,7 @@ fn wrap_text( } fn draw_evolutions( - ctx: &cairo::Context, + ctx: &Context, map: &Map, config: &Configuration, ) -> Result<(), RenderError> { @@ -739,15 +759,15 @@ fn draw_evolutions( Ok(()) } -fn draw_notes(ctx: &cairo::Context, map: &Map, config: &Configuration) -> Result<(), RenderError> { +fn draw_notes(ctx: &Context, map: &Map, config: &Configuration) -> Result<(), RenderError> { let padding = 5.0; let max_width = 400.0; ctx.save()?; ctx.select_font_face( "sans-serif", - cairo::FontSlant::Normal, - cairo::FontWeight::Normal, + FontSlant::Normal, + FontWeight::Normal, ); ctx.set_font_size(config.theme.fonts.note); |