show math.equation: set align(..) shall not break alignment points (#4094)

This commit is contained in:
Leedehai 2024-05-15 03:48:35 -04:00 committed by GitHub
parent 2d32ac73b6
commit 484a0e60d8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 60 additions and 37 deletions

View File

@ -10,7 +10,8 @@ use crate::layout::{
}; };
use crate::math::{ use crate::math::{
alignments, scaled_font_size, stack, style_for_denominator, AlignmentResult, alignments, scaled_font_size, stack, style_for_denominator, AlignmentResult,
FrameFragment, GlyphFragment, LayoutMath, MathContext, Scaled, DELIM_SHORT_FALL, FrameFragment, GlyphFragment, LayoutMath, LeftRightAlternator, MathContext, Scaled,
DELIM_SHORT_FALL,
}; };
use crate::syntax::{Span, Spanned}; use crate::syntax::{Span, Spanned};
use crate::text::TextElem; use crate::text::TextElem;
@ -67,6 +68,7 @@ impl LayoutMath for Packed<VecElem> {
self.children(), self.children(),
FixedAlignment::Center, FixedAlignment::Center,
self.gap(styles), self.gap(styles),
LeftRightAlternator::Right,
)?; )?;
layout_delimiters( layout_delimiters(
@ -324,6 +326,7 @@ impl LayoutMath for Packed<CasesElem> {
self.children(), self.children(),
FixedAlignment::Start, FixedAlignment::Start,
self.gap(styles), self.gap(styles),
LeftRightAlternator::None,
)?; )?;
let (open, close) = if self.reverse(styles) { let (open, close) = if self.reverse(styles) {
@ -387,6 +390,7 @@ fn layout_vec_body(
column: &[Content], column: &[Content],
align: FixedAlignment, align: FixedAlignment,
row_gap: Rel<Abs>, row_gap: Rel<Abs>,
alternator: LeftRightAlternator,
) -> SourceResult<Frame> { ) -> SourceResult<Frame> {
let gap = row_gap.relative_to(ctx.regions.base().y); let gap = row_gap.relative_to(ctx.regions.base().y);
@ -396,7 +400,7 @@ fn layout_vec_body(
flat.push(ctx.layout_into_run(child, styles.chain(&denom_style))?); flat.push(ctx.layout_into_run(child, styles.chain(&denom_style))?);
} }
Ok(stack(flat, align, gap, 0)) Ok(stack(flat, align, gap, 0, alternator))
} }
/// Layout the inner contents of a matrix. /// Layout the inner contents of a matrix.
@ -480,7 +484,7 @@ fn layout_mat_body(
let mut y = Abs::zero(); let mut y = Abs::zero();
for (cell, &(ascent, descent)) in col.into_iter().zip(&heights) { for (cell, &(ascent, descent)) in col.into_iter().zip(&heights) {
let cell = cell.into_line_frame(&points, FixedAlignment::Center); let cell = cell.into_line_frame(&points, LeftRightAlternator::Right);
let pos = Point::new( let pos = Point::new(
if points.is_empty() { x + (rcol - cell.width()) / 2.0 } else { x }, if points.is_empty() { x + (rcol - cell.width()) / 2.0 } else { x },
y + ascent - cell.ascent(), y + ascent - cell.ascent(),

View File

@ -3,7 +3,7 @@ use std::iter::once;
use unicode_math_class::MathClass; use unicode_math_class::MathClass;
use crate::foundations::{Resolve, StyleChain}; use crate::foundations::{Resolve, StyleChain};
use crate::layout::{Abs, AlignElem, Em, FixedAlignment, Frame, FrameKind, Point, Size}; use crate::layout::{Abs, AlignElem, Em, Frame, FrameKind, Point, Size};
use crate::math::{ use crate::math::{
alignments, scaled_font_size, spacing, EquationElem, FrameFragment, MathContext, alignments, scaled_font_size, spacing, EquationElem, FrameFragment, MathContext,
MathFragment, MathParItem, MathSize, MathFragment, MathParItem, MathSize,
@ -140,7 +140,7 @@ impl MathRun {
pub fn into_frame(self, ctx: &MathContext, styles: StyleChain) -> Frame { pub fn into_frame(self, ctx: &MathContext, styles: StyleChain) -> Frame {
if !self.is_multiline() { if !self.is_multiline() {
self.into_line_frame(&[], AlignElem::alignment_in(styles).resolve(styles).x) self.into_line_frame(&[], LeftRightAlternator::Right)
} else { } else {
self.multiline_frame_builder(ctx, styles).build() self.multiline_frame_builder(ctx, styles).build()
} }
@ -181,7 +181,7 @@ impl MathRun {
continue; continue;
} }
let sub = row.into_line_frame(&alignments.points, align); let sub = row.into_line_frame(&alignments.points, LeftRightAlternator::Right);
if i > 0 { if i > 0 {
size.y += leading; size.y += leading;
} }
@ -200,43 +200,37 @@ impl MathRun {
/// Lay out [`MathFragment`]s into a one-row [`Frame`], using the /// Lay out [`MathFragment`]s into a one-row [`Frame`], using the
/// caller-provided alignment points. /// caller-provided alignment points.
pub fn into_line_frame(self, points: &[Abs], align: FixedAlignment) -> Frame { pub fn into_line_frame(
self,
points: &[Abs],
mut alternator: LeftRightAlternator,
) -> Frame {
let ascent = self.ascent(); let ascent = self.ascent();
let mut frame = Frame::soft(Size::new(Abs::zero(), ascent + self.descent())); let mut frame = Frame::soft(Size::new(Abs::zero(), ascent + self.descent()));
frame.set_baseline(ascent); frame.set_baseline(ascent);
let mut next_x = { let mut next_x = {
let mut widths = Vec::new(); let widths: Vec<Abs> = if points.is_empty() {
if !points.is_empty() && align != FixedAlignment::Start { vec![]
let mut width = Abs::zero(); } else {
for fragment in self.iter() { self.iter()
if matches!(fragment, MathFragment::Align) { .as_slice()
widths.push(width); .split(|e| matches!(e, MathFragment::Align))
width = Abs::zero(); .map(|chunk| chunk.iter().map(|e| e.width()).sum())
} else { .collect()
width += fragment.width(); };
}
}
widths.push(width);
}
let widths = widths;
let mut prev_points = once(Abs::zero()).chain(points.iter().copied()); let mut prev_points = once(Abs::zero()).chain(points.iter().copied());
let mut point_widths = points.iter().copied().zip(widths); let mut point_widths = points.iter().copied().zip(widths);
let mut alternator = LeftRightAlternator::Right; move || {
move || match align { point_widths
FixedAlignment::Start => prev_points.next(),
FixedAlignment::End => {
point_widths.next().map(|(point, width)| point - width)
}
_ => point_widths
.next() .next()
.zip(prev_points.next()) .zip(prev_points.next())
.zip(alternator.next()) .zip(alternator.next())
.map(|(((point, width), prev_point), alternator)| match alternator { .map(|(((point, width), prev_point), alternator)| match alternator {
LeftRightAlternator::Left => prev_point,
LeftRightAlternator::Right => point - width, LeftRightAlternator::Right => point - width,
}), _ => prev_point,
})
} }
}; };
let mut x = next_x().unwrap_or_default(); let mut x = next_x().unwrap_or_default();
@ -352,8 +346,11 @@ impl<T: Into<MathFragment>> From<T> for MathRun {
} }
} }
/// An iterator that alternates between the `Left` and `Right` values, if the
/// initial value is not `None`.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
enum LeftRightAlternator { pub enum LeftRightAlternator {
None,
Left, Left,
Right, Right,
} }
@ -364,6 +361,7 @@ impl Iterator for LeftRightAlternator {
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let r = Some(*self); let r = Some(*self);
match self { match self {
Self::None => {}
Self::Left => *self = Self::Right, Self::Left => *self = Self::Right,
Self::Right => *self = Self::Left, Self::Right => *self = Self::Left,
} }

View File

@ -3,7 +3,8 @@ use crate::foundations::{elem, Content, Packed, StyleChain};
use crate::layout::{Abs, Em, FixedAlignment, Frame, FrameItem, Point, Size}; use crate::layout::{Abs, Em, FixedAlignment, Frame, FrameItem, Point, Size};
use crate::math::{ use crate::math::{
alignments, scaled_font_size, style_cramped, style_for_subscript, AlignmentResult, alignments, scaled_font_size, style_cramped, style_for_subscript, AlignmentResult,
FrameFragment, GlyphFragment, LayoutMath, MathContext, MathRun, Scaled, FrameFragment, GlyphFragment, LayoutMath, LeftRightAlternator, MathContext, MathRun,
Scaled,
}; };
use crate::syntax::Span; use crate::syntax::Span;
use crate::text::TextElem; use crate::text::TextElem;
@ -290,7 +291,8 @@ fn layout_underoverspreader(
baseline = rows.len() - 1; baseline = rows.len() - 1;
} }
let frame = stack(rows, FixedAlignment::Center, gap, baseline); let frame =
stack(rows, FixedAlignment::Center, gap, baseline, LeftRightAlternator::Right);
ctx.push(FrameFragment::new(ctx, styles, frame).with_class(body_class)); ctx.push(FrameFragment::new(ctx, styles, frame).with_class(body_class));
Ok(()) Ok(())
@ -298,28 +300,30 @@ fn layout_underoverspreader(
/// Stack rows on top of each other. /// Stack rows on top of each other.
/// ///
/// Add a `gap` between each row and uses the baseline of the `baseline`th /// Add a `gap` between each row and uses the baseline of the `baseline`-th
/// row for the whole frame. /// row for the whole frame. `alternator` controls the left/right alternating
/// alignment behavior of `AlignPointElem` in the rows.
pub(super) fn stack( pub(super) fn stack(
rows: Vec<MathRun>, rows: Vec<MathRun>,
align: FixedAlignment, align: FixedAlignment,
gap: Abs, gap: Abs,
baseline: usize, baseline: usize,
alternator: LeftRightAlternator,
) -> Frame { ) -> Frame {
let rows: Vec<_> = rows.into_iter().flat_map(|r| r.rows()).collect(); let rows: Vec<_> = rows.into_iter().flat_map(|r| r.rows()).collect();
let AlignmentResult { points, width } = alignments(&rows); let AlignmentResult { points, width } = alignments(&rows);
let rows: Vec<_> = rows let rows: Vec<_> = rows
.into_iter() .into_iter()
.map(|row| row.into_line_frame(&points, align)) .map(|row| row.into_line_frame(&points, alternator))
.collect(); .collect();
let mut y = Abs::zero();
let mut frame = Frame::soft(Size::new( let mut frame = Frame::soft(Size::new(
width, width,
rows.iter().map(|row| row.height()).sum::<Abs>() rows.iter().map(|row| row.height()).sum::<Abs>()
+ rows.len().saturating_sub(1) as f64 * gap, + rows.len().saturating_sub(1) as f64 * gap,
)); ));
let mut y = Abs::zero();
for (i, row) in rows.into_iter().enumerate() { for (i, row) in rows.into_iter().enumerate() {
let x = align.position(width - row.width()); let x = align.position(width - row.width());
let pos = Point::new(x, y); let pos = Point::new(x, y);

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -32,3 +32,20 @@ $
a &=b & quad c&=d \ a &=b & quad c&=d \
e &=f & g&=h e &=f & g&=h
$ $
--- issue-3973-math-equation-align ---
// In this bug, the alignment set with "show math.equation: set align(...)"
// overrides the left-right alternating behavior of alignment points.
#let equations = [
$ a + b &= c \
e &= f + g + h $
$ a &= b + c \
e + f + g &= h $
]
#equations
#show math.equation: set align(start)
#equations
#show math.equation: set align(end)
#equations