Created new system for limits (fixes #1443) (#1452)

This commit is contained in:
sitandr 2023-06-20 17:58:22 +03:00 committed by GitHub
parent 6c542ea1a4
commit 03675fc429
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 99 additions and 34 deletions

View File

@ -77,18 +77,9 @@ impl LayoutMath for AttachElem {
let b = layout_attachment(ctx, Self::b)?;
ctx.unstyle();
let as_limits = self.base().is::<LimitsElem>()
|| (!self.base().is::<ScriptsElem>()
&& ctx.style.size == MathSize::Display
&& base.class() == Some(MathClass::Large)
&& match &base {
MathFragment::Variant(variant) => LIMITS.contains(&variant.c),
MathFragment::Frame(fragment) => fragment.limits,
_ => false,
});
let (t, tr) = if as_limits || tr.is_some() { (t, tr) } else { (None, t) };
let (b, br) = if as_limits || br.is_some() { (b, br) } else { (None, b) };
let limits = base.limits().active(ctx);
let (t, tr) = if limits || tr.is_some() { (t, tr) } else { (None, t) };
let (b, br) = if limits || br.is_some() { (b, br) } else { (None, b) };
layout_attachments(ctx, base, [tl, t, tr, bl, b, br])
}
}
@ -112,7 +103,10 @@ pub struct ScriptsElem {
impl LayoutMath for ScriptsElem {
#[tracing::instrument(skip(ctx))]
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
self.body().layout_math(ctx)
let mut fragment = ctx.layout_fragment(&self.body())?;
fragment.set_limits(Limits::Never);
ctx.push(fragment);
Ok(())
}
}
@ -135,10 +129,56 @@ pub struct LimitsElem {
impl LayoutMath for LimitsElem {
#[tracing::instrument(skip(ctx))]
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
self.body().layout_math(ctx)
let mut fragment = ctx.layout_fragment(&self.body())?;
fragment.set_limits(Limits::Always);
ctx.push(fragment);
Ok(())
}
}
/// Describes in which situation a frame should use limits for attachments.
#[derive(Debug, Copy, Clone)]
pub enum Limits {
/// Always scripts.
Never,
/// Display limits only in `display` math.
Display,
/// Always limits.
Always,
}
impl Limits {
/// The default limit configuration if the given character is the base.
pub fn for_char(c: char) -> Self {
if Self::DEFAULT_TO_LIMITS.contains(&c) {
Limits::Display
} else {
Limits::Never
}
}
/// Whether limits should be displayed in this context
pub fn active(&self, ctx: &MathContext) -> bool {
match self {
Self::Always => true,
Self::Display => ctx.style.size == MathSize::Display,
Self::Never => false,
}
}
/// Unicode codepoints that should show attachments as limits in display
/// mode.
#[rustfmt::skip]
const DEFAULT_TO_LIMITS: &[char] = &[
/**/ '\u{220F}', /**/ '\u{2210}', /**/ '\u{2211}',
/**/ '\u{22C0}', /* */ '\u{22C1}',
/**/ '\u{22C2}', /* */ '\u{22C3}',
/**/ '\u{2A00}', /**/ '\u{2A01}', /**/ '\u{2A02}',
/**/ '\u{2A03}', /**/ '\u{2A04}',
/**/ '\u{2A05}', /**/ '\u{2A06}',
];
}
macro_rules! measure {
($e: ident, $attr: ident) => {
$e.as_ref().map(|e| e.$attr()).unwrap_or_default()
@ -358,14 +398,3 @@ fn is_atomic_text_frame(frame: &Frame) -> bool {
.filter(|item| !matches!(item, FrameItem::Meta(_, _)));
matches!(iter.next(), Some(FrameItem::Text(_))) && iter.next().is_none()
}
/// Unicode codepoints that should have sub- and superscripts attached as limits.
#[rustfmt::skip]
const LIMITS: &[char] = &[
/**/ '\u{220F}', /**/ '\u{2210}', /**/ '\u{2211}',
/**/ '\u{22C0}', /* */ '\u{22C1}',
/**/ '\u{22C2}', /* */ '\u{22C3}',
/**/ '\u{2A00}', /**/ '\u{2A01}', /**/ '\u{2A02}',
/**/ '\u{2A03}', /**/ '\u{2A04}',
/**/ '\u{2A05}', /**/ '\u{2A06}',
];

View File

@ -90,6 +90,15 @@ impl MathFragment {
}
}
pub fn set_limits(&mut self, limits: Limits) {
match self {
Self::Glyph(glyph) => glyph.limits = limits,
Self::Variant(variant) => variant.limits = limits,
Self::Frame(fragment) => fragment.limits = limits,
_ => {}
}
}
pub fn is_spaced(&self) -> bool {
match self {
MathFragment::Frame(frame) => frame.spaced,
@ -113,6 +122,15 @@ impl MathFragment {
_ => Frame::new(self.size()),
}
}
pub fn limits(&self) -> Limits {
match self {
MathFragment::Glyph(glyph) => glyph.limits,
MathFragment::Variant(variant) => variant.limits,
MathFragment::Frame(fragment) => fragment.limits,
_ => Limits::Never,
}
}
}
impl From<GlyphFragment> for MathFragment {
@ -149,6 +167,7 @@ pub struct GlyphFragment {
pub class: Option<MathClass>,
pub span: Span,
pub meta: Vec<Meta>,
pub limits: Limits,
}
impl GlyphFragment {
@ -164,6 +183,10 @@ impl GlyphFragment {
}
pub fn with_id(ctx: &MathContext, c: char, id: GlyphId, span: Span) -> Self {
let class = match c {
':' => Some(MathClass::Relation),
_ => unicode_math_class::class(c),
};
let mut fragment = Self {
id,
c,
@ -175,11 +198,9 @@ impl GlyphFragment {
width: Abs::zero(),
ascent: Abs::zero(),
descent: Abs::zero(),
limits: Limits::for_char(c),
italics_correction: Abs::zero(),
class: match c {
':' => Some(MathClass::Relation),
_ => unicode_math_class::class(c),
},
class,
span,
meta: MetaElem::data_in(ctx.styles()),
};
@ -224,6 +245,7 @@ impl GlyphFragment {
italics_correction: self.italics_correction,
class: self.class,
span: self.span,
limits: self.limits,
frame: self.into_frame(),
}
}
@ -268,6 +290,7 @@ pub struct VariantFragment {
pub font_size: Abs,
pub class: Option<MathClass>,
pub span: Span,
pub limits: Limits,
}
impl Debug for VariantFragment {
@ -282,7 +305,7 @@ pub struct FrameFragment {
pub style: MathStyle,
pub font_size: Abs,
pub class: MathClass,
pub limits: bool,
pub limits: Limits,
pub spaced: bool,
pub base_ascent: Abs,
}
@ -296,7 +319,7 @@ impl FrameFragment {
font_size: ctx.size,
style: ctx.style,
class: MathClass::Normal,
limits: false,
limits: Limits::Never,
spaced: false,
base_ascent,
}
@ -306,7 +329,7 @@ impl FrameFragment {
Self { class, ..self }
}
pub fn with_limits(self, limits: bool) -> Self {
pub fn with_limits(self, limits: Limits) -> Self {
Self { limits, ..self }
}

View File

@ -26,7 +26,7 @@ pub struct OpElem {
#[required]
pub text: EcoString,
/// Whether the operator should force attachments to display as limits.
/// Whether the operator should show attachments as limits in display mode.
#[default(false)]
pub limits: bool,
}
@ -39,7 +39,11 @@ impl LayoutMath for OpElem {
ctx.push(
FrameFragment::new(ctx, fragment.into_frame())
.with_class(MathClass::Large)
.with_limits(self.limits(ctx.styles())),
.with_limits(if self.limits(ctx.styles()) {
Limits::Display
} else {
Limits::Never
}),
);
Ok(())
}

View File

@ -185,6 +185,7 @@ fn assemble(
italics_correction: Abs::zero(),
class: base.class,
span: base.span,
limits: base.limits,
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -91,3 +91,11 @@ $ limits(integral)_a^b != integral_a^b $
---
// Error: 30-34 unknown variable: oops
$ attach(A, t: #locate(it => oops)) $
---
// Show and let rules for limits and scripts
#let eq = $ _a^b iota_a^b $
#eq
#show "∫": math.limits
#show math.iota: math.limits
#eq