Vectors and cases
2
Cargo.lock
generated
@ -846,7 +846,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "rex"
|
name = "rex"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
source = "git+https://github.com/laurmaedje/ReX#672c321a947f945e9ba936ae9fbd982c4e043f1c"
|
source = "git+https://github.com/laurmaedje/ReX#523b29bd39a4daf50f9bb3ee48689c66b209c4ff"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itertools",
|
"itertools",
|
||||||
"nom",
|
"nom",
|
||||||
|
@ -53,6 +53,8 @@ fn scope() -> Scope {
|
|||||||
std.def_node::<math::AtomNode>("atom");
|
std.def_node::<math::AtomNode>("atom");
|
||||||
std.def_node::<math::FracNode>("frac");
|
std.def_node::<math::FracNode>("frac");
|
||||||
std.def_node::<math::SqrtNode>("sqrt");
|
std.def_node::<math::SqrtNode>("sqrt");
|
||||||
|
std.def_node::<math::VecNode>("vec");
|
||||||
|
std.def_node::<math::CasesNode>("cases");
|
||||||
|
|
||||||
// Layout.
|
// Layout.
|
||||||
std.def_node::<layout::PageNode>("page");
|
std.def_node::<layout::PageNode>("page");
|
||||||
|
@ -54,7 +54,7 @@ impl Layout for MathNode {
|
|||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
_: &Regions,
|
_: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
let mut t = Texifier::new();
|
let mut t = Texifier::new(styles);
|
||||||
self.texify(&mut t)?;
|
self.texify(&mut t)?;
|
||||||
layout_tex(vt, &t.finish(), self.display, styles)
|
layout_tex(vt, &t.finish(), self.display, styles)
|
||||||
}
|
}
|
||||||
@ -71,7 +71,7 @@ trait Texify {
|
|||||||
/// Texify the node, but trim parentheses..
|
/// Texify the node, but trim parentheses..
|
||||||
fn texify_unparen(&self, t: &mut Texifier) -> SourceResult<()> {
|
fn texify_unparen(&self, t: &mut Texifier) -> SourceResult<()> {
|
||||||
let s = {
|
let s = {
|
||||||
let mut sub = Texifier::new();
|
let mut sub = Texifier::new(t.styles);
|
||||||
self.texify(&mut sub)?;
|
self.texify(&mut sub)?;
|
||||||
sub.finish()
|
sub.finish()
|
||||||
};
|
};
|
||||||
@ -88,19 +88,21 @@ trait Texify {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the TeX representation of the formula.
|
/// Builds the TeX representation of the formula.
|
||||||
struct Texifier {
|
struct Texifier<'a> {
|
||||||
tex: EcoString,
|
tex: EcoString,
|
||||||
support: bool,
|
support: bool,
|
||||||
space: bool,
|
space: bool,
|
||||||
|
styles: StyleChain<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Texifier {
|
impl<'a> Texifier<'a> {
|
||||||
/// Create a new texifier.
|
/// Create a new texifier.
|
||||||
fn new() -> Self {
|
fn new(styles: StyleChain<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
tex: EcoString::new(),
|
tex: EcoString::new(),
|
||||||
support: false,
|
support: false,
|
||||||
space: false,
|
space: false,
|
||||||
|
styles,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,7 +348,7 @@ impl Texify for AlignNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A square root node.
|
/// A square root.
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, Hash)]
|
||||||
pub struct SqrtNode(Content);
|
pub struct SqrtNode(Content);
|
||||||
|
|
||||||
@ -365,3 +367,86 @@ impl Texify for SqrtNode {
|
|||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -69,7 +69,7 @@ pub fn layout_tex(
|
|||||||
},
|
},
|
||||||
baseline: top,
|
baseline: top,
|
||||||
font: font.clone(),
|
font: font.clone(),
|
||||||
fill: styles.get(TextNode::FILL),
|
paint: styles.get(TextNode::FILL),
|
||||||
lang: styles.get(TextNode::LANG),
|
lang: styles.get(TextNode::LANG),
|
||||||
colors: vec![],
|
colors: vec![],
|
||||||
};
|
};
|
||||||
@ -85,18 +85,18 @@ struct FrameBackend {
|
|||||||
frame: Frame,
|
frame: Frame,
|
||||||
baseline: Abs,
|
baseline: Abs,
|
||||||
font: Font,
|
font: Font,
|
||||||
fill: Paint,
|
paint: Paint,
|
||||||
lang: Lang,
|
lang: Lang,
|
||||||
colors: Vec<RGBA>,
|
colors: Vec<RGBA>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FrameBackend {
|
impl FrameBackend {
|
||||||
/// The currently active fill paint.
|
/// The currently active paint.
|
||||||
fn fill(&self) -> Paint {
|
fn paint(&self) -> Paint {
|
||||||
self.colors
|
self.colors
|
||||||
.last()
|
.last()
|
||||||
.map(|&RGBA(r, g, b, a)| RgbaColor::new(r, g, b, a).into())
|
.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.
|
/// Convert a cursor to a point.
|
||||||
@ -112,7 +112,7 @@ impl Backend for FrameBackend {
|
|||||||
Element::Text(Text {
|
Element::Text(Text {
|
||||||
font: self.font.clone(),
|
font: self.font.clone(),
|
||||||
size: Abs::pt(scale),
|
size: Abs::pt(scale),
|
||||||
fill: self.fill(),
|
fill: self.paint(),
|
||||||
lang: self.lang,
|
lang: self.lang,
|
||||||
glyphs: vec![Glyph {
|
glyphs: vec![Glyph {
|
||||||
id: gid,
|
id: gid,
|
||||||
@ -126,11 +126,11 @@ impl Backend for FrameBackend {
|
|||||||
|
|
||||||
fn rule(&mut self, pos: Cursor, width: f64, height: f64) {
|
fn rule(&mut self, pos: Cursor, width: f64, height: f64) {
|
||||||
self.frame.push(
|
self.frame.push(
|
||||||
self.transform(pos),
|
self.transform(pos) + Point::with_y(Abs::pt(height) / 2.0),
|
||||||
Element::Shape(Shape {
|
Element::Shape(Shape {
|
||||||
geometry: Geometry::Rect(Size::new(Abs::pt(width), Abs::pt(height))),
|
geometry: Geometry::Line(Point::new(Abs::pt(width), Abs::zero())),
|
||||||
fill: Some(self.fill()),
|
fill: None,
|
||||||
stroke: None,
|
stroke: Some(Stroke { paint: self.paint(), thickness: Abs::pt(height) }),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -426,7 +426,7 @@ impl Eval for ast::MathNode {
|
|||||||
Self::Escape(v) => (vm.items.math_atom)(v.get().into()),
|
Self::Escape(v) => (vm.items.math_atom)(v.get().into()),
|
||||||
Self::Shorthand(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::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::Script(v) => v.eval(vm)?,
|
||||||
Self::Frac(v) => v.eval(vm)?,
|
Self::Frac(v) => v.eval(vm)?,
|
||||||
Self::Align(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()
|
if self.as_untyped().len() == ident.len()
|
||||||
&& !vm.scopes.get(ident).is_ok()
|
&& !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()));
|
return Ok(node.spanned(self.span()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BIN
tests/ref/math/matrix.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
20
tests/typ/math/matrix.typ
Normal 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",
|
||||||
|
) $
|