Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_austenite/src/bin/mathgallery.rs

4.1 KiB, 21 runs

created by r1870400018:36348, 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//! `mathgallery` -- the maths-layout comparison harness.
2//!
3//! Sets each exemplar expression as a single centred display equation on its own page and writes it as
4//! a one-page PDF, so each can be turned into a tightly-cropped PNG and set beside the Typst oracle that
5//! uses the identical font. This is the living comparison harness for tuning the maths layout against
6//! New Computer Modern Math: every difference between our render and the oracle is a layout error to close.
7//!
8//! Usage: `mathgallery [OUTPUT_DIR]` (default `mathgallery-out`). Each expression `N` is written to
9//! `eqN.pdf`; convert to PNG in the shell with `pdftoppm -png -r 300 eqN.pdf eqN` then trim.
10
11use oxedyne_fe2o3_austenite::{
12 doc::{
13 self,
14 Block,
15 },
16 theme::Theme,
17 driver::{
18 self,
19 Config,
20 },
21 font::FontMetrics,
22 math::{
23 Atom,
24 Class,
25 },
26 page::PageGeometry,
27};
28
29use oxedyne_fe2o3_core::prelude::*;
30use oxedyne_fe2o3_font::{
31 face::Role,
32 set::FontSet,
33 shape::Dir,
34};
35
36use std::sync::Arc;
37
38fn main() -> Outcome<()> {
39 let args: Vec<String> = std::env::args().collect();
40 let out_dir = match args.get(1) {
41 Some(s) => s.clone(),
42 None => "mathgallery-out".to_string(),
43 };
44
45 let fonts = Arc::new(res!(oxedyne_fe2o3_austenite::fonts::libertinus()));
46 let geom = PageGeometry::a4();
47 let style = Theme::default();
48
49 res!(std::fs::create_dir_all(&out_dir));
50
51 for (i, expr) in exemplars().into_iter().enumerate() {
52 let n = i + 1;
53 res!(render_one(fonts.clone(), geom, &style, &expr, &out_dir, n));
54 }
55
56 println!("mathgallery: wrote {} equation PDF(s) to {}/", exemplars().len(), out_dir);
57 Ok(())
58}
59
60/// Authors a one-equation document, runs the driver to a fixed point, and writes the first page as a
61/// PDF. One display equation is the whole body, so the page holds only the maths, ready to crop.
62fn render_one(
63 fonts: Arc<FontSet>,
64 geom: PageGeometry,
65 style: &Theme,
66 expr: &Atom,
67 out_dir: &str,
68 n: usize,
69)
70 -> Outcome<()>
71{
72 let blocks = vec![Block::equation(expr.clone(), false, None)];
73 let (document, _heads) = res!(doc::author(fonts.clone(), geom, style, &oxedyne_fe2o3_austenite::fonts::FaceResolver::default(), &blocks, None, None));
74
75 let metrics = FontMetrics::new(fonts.clone(), Role::Body, Dir::Ltr, style.text.body_size);
76 let out = res!(driver::run(&document, &metrics, Config::default()));
77
78 let pdf = res!(oxedyne_fe2o3_austenite::emit::pdf::render_document(&out.pages));
79 res!(std::fs::write(fmt!("{}/eq{}.pdf", out_dir, n), pdf));
80 Ok(())
81}
82
83/// The exemplar set, each built as an [`Atom`] tree. The Typst oracle for each is the reference.
84fn exemplars() -> Vec<Atom> {
85 vec![
86 // 1. E = m c^2
87 Atom::row(vec![
88 Atom::var("E"),
89 Atom::rel("="),
90 Atom::var("m"),
91 Atom::sup(Atom::var("c"), Atom::num("2")),
92 ]),
93 // 2. a/b -- a display fraction.
94 Atom::frac(Atom::var("a"), Atom::var("b")),
95 // 3. the quadratic formula.
96 Atom::row(vec![
97 Atom::var("x"),
98 Atom::rel("="),
99 Atom::frac(
100 Atom::row(vec![
101 Atom::bin("\u{2212}"), // a unary minus, the minus glyph
102 Atom::var("b"),
103 Atom::bin("\u{00B1}"), // plus-or-minus
104 Atom::sqrt(Atom::row(vec![
105 Atom::sup(Atom::var("b"), Atom::num("2")),
106 Atom::bin("\u{2212}"),
107 Atom::num("4"),
108 Atom::var("a"),
109 Atom::var("c"),
110 ])),
111 ]),
112 Atom::row(vec![
113 Atom::num("2"),
114 Atom::var("a"),
115 ]),
116 ),
117 ]),
118 // 4. (a/(b+c))^2 -- a squared fenced fraction.
119 Atom::sup(
120 Atom::fence(
121 '(',
122 Atom::frac(
123 Atom::var("a"),
124 Atom::row(vec![Atom::var("b"), Atom::bin("+"), Atom::var("c")]),
125 ),
126 ')',
127 ),
128 Atom::num("2"),
129 ),
130 // 5. x^2 + x_i - x_i^2 -- scripts: sup, sub, subsup.
131 Atom::row(vec![
132 Atom::sup(Atom::var("x"), Atom::num("2")),
133 Atom::bin("+"),
134 Atom::sub(Atom::var("x"), Atom::Sym("i".to_string(), Class::Ord)),
135 Atom::bin("\u{2212}"),
136 Atom::subsup(Atom::var("x"), Atom::Sym("i".to_string(), Class::Ord), Atom::num("2")),
137 ]),
138 // 6. (x+1)/(y-1) inside grown parens.
139 Atom::fence(
140 '(',
141 Atom::frac(
142 Atom::row(vec![Atom::var("x"), Atom::bin("+"), Atom::num("1")]),
143 Atom::row(vec![Atom::var("y"), Atom::bin("\u{2212}"), Atom::num("1")]),
144 ),
145 ')',
146 ),
147 ]
148}