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)?; let b = layout_attachment(ctx, Self::b)?;
ctx.unstyle(); ctx.unstyle();
let as_limits = self.base().is::<LimitsElem>() let limits = base.limits().active(ctx);
|| (!self.base().is::<ScriptsElem>() let (t, tr) = if limits || tr.is_some() { (t, tr) } else { (None, t) };
&& ctx.style.size == MathSize::Display let (b, br) = if limits || br.is_some() { (b, br) } else { (None, b) };
&& 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) };
layout_attachments(ctx, base, [tl, t, tr, bl, b, br]) layout_attachments(ctx, base, [tl, t, tr, bl, b, br])
} }
} }
@ -112,7 +103,10 @@ pub struct ScriptsElem {
impl LayoutMath for ScriptsElem { impl LayoutMath for ScriptsElem {
#[tracing::instrument(skip(ctx))] #[tracing::instrument(skip(ctx))]
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { 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 { impl LayoutMath for LimitsElem {
#[tracing::instrument(skip(ctx))] #[tracing::instrument(skip(ctx))]
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { 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 { macro_rules! measure {
($e: ident, $attr: ident) => { ($e: ident, $attr: ident) => {
$e.as_ref().map(|e| e.$attr()).unwrap_or_default() $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(_, _))); .filter(|item| !matches!(item, FrameItem::Meta(_, _)));
matches!(iter.next(), Some(FrameItem::Text(_))) && iter.next().is_none() 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 { pub fn is_spaced(&self) -> bool {
match self { match self {
MathFragment::Frame(frame) => frame.spaced, MathFragment::Frame(frame) => frame.spaced,
@ -113,6 +122,15 @@ impl MathFragment {
_ => Frame::new(self.size()), _ => 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 { impl From<GlyphFragment> for MathFragment {
@ -149,6 +167,7 @@ pub struct GlyphFragment {
pub class: Option<MathClass>, pub class: Option<MathClass>,
pub span: Span, pub span: Span,
pub meta: Vec<Meta>, pub meta: Vec<Meta>,
pub limits: Limits,
} }
impl GlyphFragment { impl GlyphFragment {
@ -164,6 +183,10 @@ impl GlyphFragment {
} }
pub fn with_id(ctx: &MathContext, c: char, id: GlyphId, span: Span) -> Self { 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 { let mut fragment = Self {
id, id,
c, c,
@ -175,11 +198,9 @@ impl GlyphFragment {
width: Abs::zero(), width: Abs::zero(),
ascent: Abs::zero(), ascent: Abs::zero(),
descent: Abs::zero(), descent: Abs::zero(),
limits: Limits::for_char(c),
italics_correction: Abs::zero(), italics_correction: Abs::zero(),
class: match c { class,
':' => Some(MathClass::Relation),
_ => unicode_math_class::class(c),
},
span, span,
meta: MetaElem::data_in(ctx.styles()), meta: MetaElem::data_in(ctx.styles()),
}; };
@ -224,6 +245,7 @@ impl GlyphFragment {
italics_correction: self.italics_correction, italics_correction: self.italics_correction,
class: self.class, class: self.class,
span: self.span, span: self.span,
limits: self.limits,
frame: self.into_frame(), frame: self.into_frame(),
} }
} }
@ -268,6 +290,7 @@ pub struct VariantFragment {
pub font_size: Abs, pub font_size: Abs,
pub class: Option<MathClass>, pub class: Option<MathClass>,
pub span: Span, pub span: Span,
pub limits: Limits,
} }
impl Debug for VariantFragment { impl Debug for VariantFragment {
@ -282,7 +305,7 @@ pub struct FrameFragment {
pub style: MathStyle, pub style: MathStyle,
pub font_size: Abs, pub font_size: Abs,
pub class: MathClass, pub class: MathClass,
pub limits: bool, pub limits: Limits,
pub spaced: bool, pub spaced: bool,
pub base_ascent: Abs, pub base_ascent: Abs,
} }
@ -296,7 +319,7 @@ impl FrameFragment {
font_size: ctx.size, font_size: ctx.size,
style: ctx.style, style: ctx.style,
class: MathClass::Normal, class: MathClass::Normal,
limits: false, limits: Limits::Never,
spaced: false, spaced: false,
base_ascent, base_ascent,
} }
@ -306,7 +329,7 @@ impl FrameFragment {
Self { class, ..self } Self { class, ..self }
} }
pub fn with_limits(self, limits: bool) -> Self { pub fn with_limits(self, limits: Limits) -> Self {
Self { limits, ..self } Self { limits, ..self }
} }

View File

@ -26,7 +26,7 @@ pub struct OpElem {
#[required] #[required]
pub text: EcoString, 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)] #[default(false)]
pub limits: bool, pub limits: bool,
} }
@ -39,7 +39,11 @@ impl LayoutMath for OpElem {
ctx.push( ctx.push(
FrameFragment::new(ctx, fragment.into_frame()) FrameFragment::new(ctx, fragment.into_frame())
.with_class(MathClass::Large) .with_class(MathClass::Large)
.with_limits(self.limits(ctx.styles())), .with_limits(if self.limits(ctx.styles()) {
Limits::Display
} else {
Limits::Never
}),
); );
Ok(()) Ok(())
} }

View File

@ -185,6 +185,7 @@ fn assemble(
italics_correction: Abs::zero(), italics_correction: Abs::zero(),
class: base.class, class: base.class,
span: base.span, 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 // Error: 30-34 unknown variable: oops
$ attach(A, t: #locate(it => 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