diff --git a/library/src/math/frac.rs b/library/src/math/frac.rs index f3edf852f..50a68fea1 100644 --- a/library/src/math/frac.rs +++ b/library/src/math/frac.rs @@ -25,7 +25,7 @@ use super::*; /// ## Category /// math #[func] -#[capable(Texify)] +#[capable(LayoutMath)] #[derive(Debug, Hash)] pub struct FracNode { /// The numerator. @@ -43,14 +43,9 @@ impl FracNode { } } -impl Texify for FracNode { - fn texify(&self, t: &mut Texifier) -> SourceResult<()> { - t.push_str("\\frac{"); - self.num.texify_unparen(t)?; - t.push_str("}{"); - self.denom.texify_unparen(t)?; - t.push_str("}"); - Ok(()) +impl LayoutMath for FracNode { + fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { + layout(ctx, &self.num, &self.denom, false) } } @@ -72,7 +67,7 @@ impl Texify for FracNode { /// ## Category /// math #[func] -#[capable(Texify)] +#[capable(LayoutMath)] #[derive(Debug, Hash)] pub struct BinomNode { /// The upper index. @@ -90,13 +85,83 @@ impl BinomNode { } } -impl Texify for BinomNode { - fn texify(&self, t: &mut Texifier) -> SourceResult<()> { - t.push_str("\\binom{"); - self.upper.texify(t)?; - t.push_str("}{"); - self.lower.texify(t)?; - t.push_str("}"); - Ok(()) +impl LayoutMath for BinomNode { + fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { + layout(ctx, &self.upper, &self.lower, true) } } + +/// Layout a fraction or binomial. +fn layout( + ctx: &mut MathContext, + num: &Content, + denom: &Content, + binom: bool, +) -> SourceResult<()> { + let axis = scaled!(ctx, axis_height); + let thickness = scaled!(ctx, fraction_rule_thickness); + let shift_up = scaled!( + ctx, + text: fraction_numerator_shift_up, + display: fraction_numerator_display_style_shift_up, + ); + let shift_down = scaled!( + ctx, + text: fraction_denominator_shift_down, + display: fraction_denominator_display_style_shift_down, + ); + let num_min = scaled!( + ctx, + text: fraction_numerator_gap_min, + display: fraction_num_display_style_gap_min, + ); + let denom_min = scaled!( + ctx, + text: fraction_denominator_gap_min, + display: fraction_denom_display_style_gap_min, + ); + + ctx.style(ctx.style.for_numerator()); + let num = ctx.layout_frame(num)?; + ctx.unstyle(); + + ctx.style(ctx.style.for_denominator()); + let denom = ctx.layout_frame(denom)?; + ctx.unstyle(); + + let around = Em::new(0.1).scaled(ctx); + let num_gap = (shift_up - axis - num.descent()).max(num_min + thickness / 2.0); + let denom_gap = (shift_down + axis - denom.ascent()).max(denom_min + thickness / 2.0); + + let line_width = num.width().max(denom.width()); + let width = line_width + 2.0 * around; + let height = num.height() + num_gap + thickness + denom_gap + denom.height(); + let size = Size::new(width, height); + let num_pos = Point::with_x((width - num.width()) / 2.0); + let line_pos = + Point::new((width - line_width) / 2.0, num.height() + num_gap + thickness / 2.0); + let denom_pos = Point::new((width - denom.width()) / 2.0, height - denom.height()); + let baseline = line_pos.y + axis; + + let mut frame = Frame::new(size); + frame.set_baseline(baseline); + frame.push_frame(num_pos, num); + frame.push_frame(denom_pos, denom); + + if binom { + ctx.push(GlyphFragment::new(ctx, '(')); + ctx.push(frame); + ctx.push(GlyphFragment::new(ctx, ')')); + } else { + frame.push( + line_pos, + Element::Shape( + Geometry::Line(Point::with_x(line_width)) + .stroked(Stroke { paint: ctx.fill, thickness }), + ), + ); + ctx.push(frame); + } + + Ok(()) +} diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs index e3fc13f62..de954fbf5 100644 --- a/library/src/math/mod.rs +++ b/library/src/math/mod.rs @@ -47,6 +47,7 @@ use crate::text::{families, variant, FallbackList, FontFamily, SpaceNode, Symbol pub fn define(scope: &mut Scope) { scope.def_func::("math"); scope.def_func::("frac"); + scope.def_func::("binom"); scope.def_func::("script"); scope.def_func::("sqrt"); scope.def_func::("root");