oxedyne/fe2o3/fe2o3_austenite/src/bin/diagram_demo.rs
4.3 KiB, 28 runs
created by r1870400018:36159, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | //! `diagram_demo` -- draws the compile-loop flowchart and sets it in a real document. |
| 2 | //! |
| 3 | //! Builds the flowchart from the diagram DSL section of the Austenite language document -- Source |
| 4 | //! through Parse, a Types-OK? decision, Lay out and Write Pearl, with a Report-error box off the |
| 5 | //! decision and an orthogonal "fix" edge back to the parser -- wraps it as a captioned figure, and |
| 6 | //! authors a one-page document around it: a heading, a sentence of prose, then the figure. The whole |
| 7 | //! is run through the two-pass driver, decorated with a folio, and written as SVG pages and a PDF, so |
| 8 | //! the flowchart's boxes, routed edges and shaped labels land on a typeset page. |
| 9 | //! |
| 10 | //! Usage: `diagram_demo [OUTPUT_DIR]` (default `diagram-out`). |
| 11 | |
| 12 | use oxedyne_fe2o3_austenite::{ |
| 13 | diagram::{ |
| 14 | layout::Route, |
| 15 | shape::{ |
| 16 | Port, |
| 17 | Shape, |
| 18 | }, |
| 19 | Diagram, |
| 20 | DiagramStyle, |
| 21 | Endpoint, |
| 22 | }, |
| 23 | doc::{ |
| 24 | self, |
| 25 | Block, |
| 26 | }, |
| 27 | theme::Theme, |
| 28 | driver::{ |
| 29 | self, |
| 30 | Config, |
| 31 | }, |
| 32 | emit::Emitter, |
| 33 | font::FontMetrics, |
| 34 | ir::{ |
| 35 | Graphic, |
| 36 | Sp, |
| 37 | }, |
| 38 | page::PageGeometry, |
| 39 | }; |
| 40 | |
| 41 | use oxedyne_fe2o3_core::prelude::*; |
| 42 | use oxedyne_fe2o3_font::{ |
| 43 | face::Role, |
| 44 | set::FontSet, |
| 45 | shape::Dir, |
| 46 | }; |
| 47 | |
| 48 | use std::sync::Arc; |
| 49 | |
| 50 | fn main() -> Outcome<()> { |
| 51 | let args: Vec<String> = std::env::args().collect(); |
| 52 | let out_dir = match args.get(1) { |
| 53 | Some(s) => s.clone(), |
| 54 | None => "diagram-out".to_string(), |
| 55 | }; |
| 56 | |
| 57 | let fonts = Arc::new(res!(oxedyne_fe2o3_austenite::fonts::libertinus())); |
| 58 | let geom = PageGeometry::a4(); |
| 59 | let style = Theme::default(); |
| 60 | |
| 61 | let graphic = res!(compile_loop(fonts.clone())); |
| 62 | println!( |
| 63 | "diagram_demo: flowchart is {:.1} x {:.1} pt.", |
| 64 | graphic.dims.width.to_pt(), graphic.dims.height.to_pt()); |
| 65 | |
| 66 | let blocks = vec![ |
| 67 | Block::heading(1, "A Flowchart Set on the Page"), |
| 68 | Block::paragraph( |
| 69 | "The figure below is drawn in Austenite's diagram sub-language: its boxes and edges are the \ |
| 70 | same outline paths the body text is, and its labels are shaped in the document's own font. \ |
| 71 | It shows the engine's own compile loop, the parser fed again whenever the type check fails."), |
| 72 | Block::figure(graphic, Some("The compile loop".to_string()), None), |
| 73 | ]; |
| 74 | |
| 75 | let (document, heads) = res!(doc::author(fonts.clone(), geom, &style, &oxedyne_fe2o3_austenite::fonts::FaceResolver::default(), &blocks, None, None)); |
| 76 | |
| 77 | let metrics = FontMetrics::new(fonts.clone(), Role::Body, Dir::Ltr, style.text.body_size); |
| 78 | let mut out = res!(driver::run(&document, &metrics, Config::default())); |
| 79 | res!(doc::decorate(&mut out.pages, &out.ledger, &heads, &fonts, &style, geom, "", None)); |
| 80 | |
| 81 | res!(std::fs::create_dir_all(&out_dir)); |
| 82 | let emitter = Emitter::Svg; |
| 83 | for page in &out.pages { |
| 84 | let svg = res!(emitter.render(page)); |
| 85 | let path = fmt!("{}/page-{:03}.{}", out_dir, page.number, emitter.extension()); |
| 86 | res!(std::fs::write(&path, svg)); |
| 87 | } |
| 88 | |
| 89 | let pdf = res!(oxedyne_fe2o3_austenite::emit::pdf::render_document(&out.pages)); |
| 90 | res!(std::fs::write(fmt!("{}/document.pdf", out_dir), pdf)); |
| 91 | |
| 92 | println!( |
| 93 | "diagram_demo: composed {} page(s) in {} pass(es); wrote SVG page(s) and document.pdf to {}/", |
| 94 | out.pages.len(), out.passes, out_dir); |
| 95 | Ok(()) |
| 96 | } |
| 97 | |
| 98 | /// The compile-loop flowchart, exactly the graph the language document's DSL example draws. |
| 99 | fn compile_loop(fonts: Arc<FontSet>) -> Outcome<Graphic> { |
| 100 | let down = Sp::from_pt(30.0); |
| 101 | let down_lo = Sp::from_pt(34.0); // a touch more room beneath the diamond |
| 102 | let across = Sp::from_pt(70.0); |
| 103 | |
| 104 | let mut d = Diagram::new(); |
| 105 | d.node_at("src", "Source", Sp::ZERO, Sp::ZERO, Shape::Stadium) |
| 106 | .node_below("parse", "Parse", "src", down, Shape::Box) |
| 107 | .node_below("check", "Types OK?", "parse", down, Shape::Diamond) |
| 108 | .node_below("layout", "Lay out", "check", down_lo, Shape::Box) |
| 109 | .node_below("pearl", "Write Pearl", "layout", down, Shape::Stadium) |
| 110 | .node_right("err", "Report error", "check", across, Shape::Box); |
| 111 | |
| 112 | d.edge(Endpoint::node("src"), Endpoint::node("parse"), None, Route::Straight) |
| 113 | .edge(Endpoint::node("parse"), Endpoint::node("check"), None, Route::Straight) |
| 114 | .edge(Endpoint::port("check", Port::South), Endpoint::node("layout"), Some("yes"), Route::Straight) |
| 115 | .edge(Endpoint::port("check", Port::East), Endpoint::port("err", Port::West), Some("no"), Route::Straight) |
| 116 | .edge(Endpoint::port("err", Port::North), Endpoint::port("parse", Port::East), Some("fix"), Route::Orthogonal) |
| 117 | .edge(Endpoint::node("layout"), Endpoint::node("pearl"), None, Route::Straight); |
| 118 | |
| 119 | d.build(fonts, &DiagramStyle::default()) |
| 120 | } |