Vectors and cases

This commit is contained in:
Laurenz 2022-12-07 11:28:52 +01:00
parent b2572f9d48
commit 5a0053c729
10 changed files with 126 additions and 19 deletions

2
Cargo.lock generated
View File

@ -846,7 +846,7 @@ dependencies = [
[[package]]
name = "rex"
version = "0.1.2"
source = "git+https://github.com/laurmaedje/ReX#672c321a947f945e9ba936ae9fbd982c4e043f1c"
source = "git+https://github.com/laurmaedje/ReX#523b29bd39a4daf50f9bb3ee48689c66b209c4ff"
dependencies = [
"itertools",
"nom",

View File

@ -53,6 +53,8 @@ fn scope() -> Scope {
std.def_node::<math::AtomNode>("atom");
std.def_node::<math::FracNode>("frac");
std.def_node::<math::SqrtNode>("sqrt");
std.def_node::<math::VecNode>("vec");
std.def_node::<math::CasesNode>("cases");
// Layout.
std.def_node::<layout::PageNode>("page");

View File

@ -54,7 +54,7 @@ impl Layout for MathNode {
styles: StyleChain,
_: &Regions,
) -> SourceResult<Fragment> {
let mut t = Texifier::new();
let mut t = Texifier::new(styles);
self.texify(&mut t)?;
layout_tex(vt, &t.finish(), self.display, styles)
}
@ -71,7 +71,7 @@ trait Texify {
/// Texify the node, but trim parentheses..
fn texify_unparen(&self, t: &mut Texifier) -> SourceResult<()> {
let s = {
let mut sub = Texifier::new();
let mut sub = Texifier::new(t.styles);
self.texify(&mut sub)?;
sub.finish()
};
@ -88,19 +88,21 @@ trait Texify {
}
/// Builds the TeX representation of the formula.
struct Texifier {
struct Texifier<'a> {
tex: EcoString,
support: bool,
space: bool,
styles: StyleChain<'a>,
}
impl Texifier {
impl<'a> Texifier<'a> {
/// Create a new texifier.
fn new() -> Self {
fn new(styles: StyleChain<'a>) -> Self {
Self {
tex: EcoString::new(),
support: false,
space: false,
styles,
}
}
@ -346,7 +348,7 @@ impl Texify for AlignNode {
}
}
/// A square root node.
/// A square root.
#[derive(Debug, Hash)]
pub struct SqrtNode(Content);
@ -365,3 +367,86 @@ impl Texify for SqrtNode {
Ok(())
}
}
/// A column vector.
#[derive(Debug, Hash)]
pub struct VecNode(Vec<Content>);
#[node(Texify)]
impl VecNode {
/// The kind of delimiter.
pub const DELIM: Delimiter = Delimiter::Paren;
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self(args.all()?).pack())
}
}
impl Texify for VecNode {
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
let kind = match t.styles.get(Self::DELIM) {
Delimiter::Paren => "pmatrix",
Delimiter::Bracket => "bmatrix",
Delimiter::Brace => "Bmatrix",
Delimiter::Bar => "vmatrix",
};
t.push_str("\\begin{");
t.push_str(kind);
t.push_str("}");
for component in &self.0 {
component.texify_unparen(t)?;
t.push_str("\\\\");
}
t.push_str("\\end{");
t.push_str(kind);
t.push_str("}");
Ok(())
}
}
/// A vector / matrix delimiter.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Delimiter {
Paren,
Bracket,
Brace,
Bar,
}
castable! {
Delimiter,
Expected: "type of bracket or bar",
Value::Str(s) => match s.as_str() {
"(" => Self::Paren,
"[" => Self::Bracket,
"{" => Self::Brace,
"|" => Self::Bar,
_ => Err("expected \"(\", \"[\", \"{\", or \"|\"")?,
},
}
/// A case distinction.
#[derive(Debug, Hash)]
pub struct CasesNode(Vec<Content>);
#[node(Texify)]
impl CasesNode {
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self(args.all()?).pack())
}
}
impl Texify for CasesNode {
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
t.push_str("\\begin{cases}");
for component in &self.0 {
component.texify_unparen(t)?;
t.push_str("\\\\");
}
t.push_str("\\end{cases}");
Ok(())
}
}

View File

@ -69,7 +69,7 @@ pub fn layout_tex(
},
baseline: top,
font: font.clone(),
fill: styles.get(TextNode::FILL),
paint: styles.get(TextNode::FILL),
lang: styles.get(TextNode::LANG),
colors: vec![],
};
@ -85,18 +85,18 @@ struct FrameBackend {
frame: Frame,
baseline: Abs,
font: Font,
fill: Paint,
paint: Paint,
lang: Lang,
colors: Vec<RGBA>,
}
impl FrameBackend {
/// The currently active fill paint.
fn fill(&self) -> Paint {
/// The currently active paint.
fn paint(&self) -> Paint {
self.colors
.last()
.map(|&RGBA(r, g, b, a)| RgbaColor::new(r, g, b, a).into())
.unwrap_or(self.fill)
.unwrap_or(self.paint)
}
/// Convert a cursor to a point.
@ -112,7 +112,7 @@ impl Backend for FrameBackend {
Element::Text(Text {
font: self.font.clone(),
size: Abs::pt(scale),
fill: self.fill(),
fill: self.paint(),
lang: self.lang,
glyphs: vec![Glyph {
id: gid,
@ -126,11 +126,11 @@ impl Backend for FrameBackend {
fn rule(&mut self, pos: Cursor, width: f64, height: f64) {
self.frame.push(
self.transform(pos),
self.transform(pos) + Point::with_y(Abs::pt(height) / 2.0),
Element::Shape(Shape {
geometry: Geometry::Rect(Size::new(Abs::pt(width), Abs::pt(height))),
fill: Some(self.fill()),
stroke: None,
geometry: Geometry::Line(Point::new(Abs::pt(width), Abs::zero())),
fill: None,
stroke: Some(Stroke { paint: self.paint(), thickness: Abs::pt(height) }),
}),
);
}

View File

@ -426,7 +426,7 @@ impl Eval for ast::MathNode {
Self::Escape(v) => (vm.items.math_atom)(v.get().into()),
Self::Shorthand(v) => (vm.items.math_atom)(v.get().into()),
Self::Atom(v) => v.eval(vm)?,
Self::Symbol(v) => (vm.items.symbol)(v.get().clone()),
Self::Symbol(v) => (vm.items.symbol)(v.get().clone() + ":op".into()),
Self::Script(v) => v.eval(vm)?,
Self::Frac(v) => v.eval(vm)?,
Self::Align(v) => v.eval(vm)?,
@ -436,7 +436,7 @@ impl Eval for ast::MathNode {
if self.as_untyped().len() == ident.len()
&& !vm.scopes.get(ident).is_ok()
{
let node = (vm.items.symbol)(ident.get().clone());
let node = (vm.items.symbol)(ident.get().clone() + ":op".into());
return Ok(node.spanned(self.span()));
}
}

BIN
tests/ref/math/matrix.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

20
tests/typ/math/matrix.typ Normal file
View File

@ -0,0 +1,20 @@
// Test vectors, matrices, and cases.
---
$ v = vec(1, 2+3, 4) $
---
#set vec(delim: "|")
$ vec(1, 2) $
---
// Error: 17-20 expected "(", "[", "{", or "|"
#set vec(delim: "%")
---
$ f(x, y) := cases(
1 "if" (x dot y)/2 <= 0,
2 "if" x in NN,
3 "if" x "is even",
4 "else",
) $