summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock2
-rw-r--r--library/src/lib.rs2
-rw-r--r--library/src/math/mod.rs97
-rw-r--r--library/src/math/tex.rs20
-rw-r--r--src/model/eval.rs4
-rw-r--r--tests/ref/math/matrix.pngbin0 -> 4854 bytes
-rw-r--r--tests/ref/math/shorthand.pngbin1230 -> 1231 bytes
-rw-r--r--tests/ref/math/simple.pngbin6555 -> 6560 bytes
-rw-r--r--tests/ref/math/syntax.pngbin53462 -> 53524 bytes
-rw-r--r--tests/typ/math/matrix.typ20
10 files changed, 126 insertions, 19 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 37fe60d1..416d989b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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",
diff --git a/library/src/lib.rs b/library/src/lib.rs
index af5c252b..29a6cc94 100644
--- a/library/src/lib.rs
+++ b/library/src/lib.rs
@@ -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");
diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs
index 1e8145cc..c613ea2a 100644
--- a/library/src/math/mod.rs
+++ b/library/src/math/mod.rs
@@ -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(())
+ }
+}
diff --git a/library/src/math/tex.rs b/library/src/math/tex.rs
index da07f1d6..e8917f30 100644
--- a/library/src/math/tex.rs
+++ b/library/src/math/tex.rs
@@ -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) }),
}),
);
}
diff --git a/src/model/eval.rs b/src/model/eval.rs
index f3281dac..204dfbd4 100644
--- a/src/model/eval.rs
+++ b/src/model/eval.rs
@@ -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()));
}
}
diff --git a/tests/ref/math/matrix.png b/tests/ref/math/matrix.png
new file mode 100644
index 00000000..ed763710
--- /dev/null
+++ b/tests/ref/math/matrix.png
Binary files differ
diff --git a/tests/ref/math/shorthand.png b/tests/ref/math/shorthand.png
index 4b14006b..840feac2 100644
--- a/tests/ref/math/shorthand.png
+++ b/tests/ref/math/shorthand.png
Binary files differ
diff --git a/tests/ref/math/simple.png b/tests/ref/math/simple.png
index 72f9c1c6..6c7fde55 100644
--- a/tests/ref/math/simple.png
+++ b/tests/ref/math/simple.png
Binary files differ
diff --git a/tests/ref/math/syntax.png b/tests/ref/math/syntax.png
index eaf18528..1000adb5 100644
--- a/tests/ref/math/syntax.png
+++ b/tests/ref/math/syntax.png
Binary files differ
diff --git a/tests/typ/math/matrix.typ b/tests/typ/math/matrix.typ
new file mode 100644
index 00000000..ec84778c
--- /dev/null
+++ b/tests/typ/math/matrix.typ
@@ -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",
+) $