diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-02-17 11:55:28 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-02-17 13:28:04 +0100 |
| commit | ab95627d873239182e7b28b266f8b9f9da5cdbb1 (patch) | |
| tree | ae6589d60d32a7ae884326911600c733dc32d977 /src | |
| parent | 5965515a1ef1fe398235311185d531efc2750247 (diff) | |
Switch to const generics for nodes
Diffstat (limited to 'src')
| -rw-r--r-- | src/diag.rs | 2 | ||||
| -rw-r--r-- | src/eval/mod.rs | 10 | ||||
| -rw-r--r-- | src/eval/template.rs | 4 | ||||
| -rw-r--r-- | src/layout/mod.rs | 8 | ||||
| -rw-r--r-- | src/library/deco.rs | 58 | ||||
| -rw-r--r-- | src/library/list.rs | 53 | ||||
| -rw-r--r-- | src/library/mod.rs | 36 | ||||
| -rw-r--r-- | src/library/shape.rs | 80 | ||||
| -rw-r--r-- | src/library/text.rs | 10 | ||||
| -rw-r--r-- | src/library/transform.rs | 81 |
10 files changed, 112 insertions, 230 deletions
diff --git a/src/diag.rs b/src/diag.rs index bd91df26..70a5bdaf 100644 --- a/src/diag.rs +++ b/src/diag.rs @@ -7,7 +7,7 @@ use crate::syntax::{Span, Spanned}; /// Early-return with a vec-boxed [`Error`]. macro_rules! bail { ($span:expr, $message:expr $(,)?) => { - return Err($crate::diag::Error::boxed($span, $message,)) + return Err($crate::diag::Error::boxed($span, $message)) }; ($span:expr, $fmt:expr, $($arg:expr),+ $(,)?) => { diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 5a67555c..9ad23538 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -41,7 +41,7 @@ use crate::diag::{At, Error, StrResult, Trace, Tracepoint, TypResult}; use crate::geom::{Angle, Fractional, Length, Relative}; use crate::image::ImageStore; use crate::layout::Layout; -use crate::library::{self}; +use crate::library::{self, ORDERED, UNORDERED}; use crate::loading::Loader; use crate::source::{SourceId, SourceStore}; use crate::syntax::ast::*; @@ -272,9 +272,9 @@ impl Eval for ListNode { type Output = Template; fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { - Ok(Template::show(library::ListNode { + Ok(Template::show(library::ListNode::<UNORDERED> { + number: None, child: self.body().eval(ctx)?.pack(), - label: library::Unordered, })) } } @@ -283,9 +283,9 @@ impl Eval for EnumNode { type Output = Template; fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { - Ok(Template::show(library::ListNode { + Ok(Template::show(library::ListNode::<ORDERED> { + number: self.number(), child: self.body().eval(ctx)?.pack(), - label: library::Ordered(self.number()), })) } } diff --git a/src/eval/template.rs b/src/eval/template.rs index 84888b95..f953287d 100644 --- a/src/eval/template.rs +++ b/src/eval/template.rs @@ -14,7 +14,7 @@ use crate::layout::{Layout, LayoutNode}; use crate::library::prelude::*; use crate::library::{ DecoNode, FlowChild, FlowNode, PageNode, ParChild, ParNode, PlaceNode, SpacingKind, - TextNode, Underline, + TextNode, UNDERLINE, }; use crate::util::EcoString; use crate::Context; @@ -145,7 +145,7 @@ impl Template { /// Underline this template. pub fn underlined(self) -> Self { - Self::show(DecoNode { kind: Underline, body: self }) + Self::show(DecoNode::<UNDERLINE>(self)) } /// Create a new sequence template. diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 7755f84c..c334433e 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -18,9 +18,9 @@ use std::sync::Arc; use crate::eval::StyleChain; use crate::font::FontStore; use crate::frame::{Element, Frame, Geometry, Shape, Stroke}; -use crate::geom::{Align, Linear, Paint, Point, Sides, Size, Spec}; +use crate::geom::{Align, Linear, Paint, Point, Sides, Size, Spec, Transform}; use crate::image::ImageStore; -use crate::library::{AlignNode, Move, PadNode, TransformNode}; +use crate::library::{AlignNode, PadNode, TransformNode, MOVE}; use crate::util::Prehashed; use crate::Context; @@ -150,8 +150,8 @@ impl LayoutNode { /// Transform this node's contents without affecting layout. pub fn moved(self, offset: Point) -> Self { if !offset.is_zero() { - TransformNode { - kind: Move(offset.x, offset.y), + TransformNode::<MOVE> { + transform: Transform::translation(offset.x, offset.y), child: self, } .pack() diff --git a/src/library/deco.rs b/src/library/deco.rs index 40ef4e73..5e438d54 100644 --- a/src/library/deco.rs +++ b/src/library/deco.rs @@ -5,15 +5,10 @@ use super::TextNode; /// Typeset underline, striken-through or overlined text. #[derive(Debug, Hash)] -pub struct DecoNode<L: LineKind> { - /// The kind of line. - pub kind: L, - /// The decorated contents. - pub body: Template, -} +pub struct DecoNode<const L: DecoLine>(pub Template); #[class] -impl<L: LineKind> DecoNode<L> { +impl<const L: DecoLine> DecoNode<L> { /// Stroke color of the line, defaults to the text color if `None`. #[shorthand] pub const STROKE: Option<Paint> = None; @@ -32,17 +27,14 @@ impl<L: LineKind> DecoNode<L> { pub const EVADE: bool = true; fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { - Ok(Template::show(Self { - kind: L::default(), - body: args.expect::<Template>("body")?, - })) + Ok(Template::show(Self(args.expect::<Template>("body")?))) } } -impl<L: LineKind> Show for DecoNode<L> { +impl<const L: DecoLine> Show for DecoNode<L> { fn show(&self, styles: StyleChain) -> Template { - self.body.clone().styled(TextNode::LINES, vec![Decoration { - line: L::LINE, + self.0.clone().styled(TextNode::LINES, vec![Decoration { + line: L, stroke: styles.get(Self::STROKE), thickness: styles.get(Self::THICKNESS), offset: styles.get(Self::OFFSET), @@ -65,42 +57,14 @@ pub struct Decoration { pub evade: bool, } -/// The kind of decorative line. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub enum DecoLine { - /// A line under text. - Underline, - /// A line through text. - Strikethrough, - /// A line over text. - Overline, -} - -/// Different kinds of decorative lines for text. -pub trait LineKind: Debug + Default + Hash + Sync + Send + 'static { - const LINE: DecoLine; -} +/// A kind of decorative line. +pub type DecoLine = usize; /// A line under text. -#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)] -pub struct Underline; - -impl LineKind for Underline { - const LINE: DecoLine = DecoLine::Underline; -} +pub const UNDERLINE: DecoLine = 0; /// A line through text. -#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)] -pub struct Strikethrough; - -impl LineKind for Strikethrough { - const LINE: DecoLine = DecoLine::Strikethrough; -} +pub const STRIKETHROUGH: DecoLine = 1; /// A line over text. -#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)] -pub struct Overline; - -impl LineKind for Overline { - const LINE: DecoLine = DecoLine::Overline; -} +pub const OVERLINE: DecoLine = 2; diff --git a/src/library/list.rs b/src/library/list.rs index 3b7cef68..e07177f4 100644 --- a/src/library/list.rs +++ b/src/library/list.rs @@ -5,15 +5,15 @@ use super::{GridNode, TextNode, TrackSizing}; /// An unordered or ordered list. #[derive(Debug, Hash)] -pub struct ListNode<L: ListLabel> { - /// The list label -- unordered or ordered with index. - pub label: L, +pub struct ListNode<const L: Labelling> { + /// The number of the item. + pub number: Option<usize>, /// The node that produces the item's body. pub child: LayoutNode, } #[class] -impl<L: ListLabel> ListNode<L> { +impl<const L: Labelling> ListNode<L> { /// The indentation of each item's label. pub const LABEL_INDENT: Linear = Relative::new(0.0).into(); /// The space between the label and the body of each item. @@ -24,17 +24,22 @@ impl<L: ListLabel> ListNode<L> { .all()? .into_iter() .enumerate() - .map(|(i, child)| Template::show(Self { label: L::new(1 + i), child })) + .map(|(i, child)| Template::show(Self { number: Some(1 + i), child })) .sum()) } } -impl<L: ListLabel> Show for ListNode<L> { +impl<const L: Labelling> Show for ListNode<L> { fn show(&self, styles: StyleChain) -> Template { let em = styles.get(TextNode::SIZE).abs; let label_indent = styles.get(Self::LABEL_INDENT).resolve(em); let body_indent = styles.get(Self::BODY_INDENT).resolve(em); + let label = match L { + UNORDERED => '•'.into(), + ORDERED | _ => format_eco!("{}.", self.number.unwrap_or(1)), + }; + Template::block(GridNode { tracks: Spec::with_x(vec![ TrackSizing::Linear(label_indent.into()), @@ -45,7 +50,7 @@ impl<L: ListLabel> Show for ListNode<L> { gutter: Spec::default(), children: vec![ LayoutNode::default(), - Template::Text(self.label.label()).pack(), + Template::Text(label).pack(), LayoutNode::default(), self.child.clone(), ], @@ -54,38 +59,10 @@ impl<L: ListLabel> Show for ListNode<L> { } /// How to label a list. -pub trait ListLabel: Debug + Default + Hash + Sync + Send + 'static { - /// Create a new list label. - fn new(number: usize) -> Self; - - /// Return the item's label. - fn label(&self) -> EcoString; -} +pub type Labelling = usize; /// Unordered list labelling style. -#[derive(Debug, Default, Hash)] -pub struct Unordered; - -impl ListLabel for Unordered { - fn new(_: usize) -> Self { - Self - } - - fn label(&self) -> EcoString { - '•'.into() - } -} +pub const UNORDERED: Labelling = 0; /// Ordered list labelling style. -#[derive(Debug, Default, Hash)] -pub struct Ordered(pub Option<usize>); - -impl ListLabel for Ordered { - fn new(number: usize) -> Self { - Self(Some(number)) - } - - fn label(&self) -> EcoString { - format_eco!("{}.", self.0.unwrap_or(1)) - } -} +pub const ORDERED: Labelling = 1; diff --git a/src/library/mod.rs b/src/library/mod.rs index b2cb8698..db173daf 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -53,16 +53,8 @@ pub use text::*; pub use transform::*; pub use utility::*; -macro_rules! prelude { - ($($reexport:item)*) => { - /// Helpful imports for creating library functionality. - pub mod prelude { - $(#[doc(no_inline)] $reexport)* - } - }; -} - -prelude! { +/// Helpful imports for creating library functionality. +pub mod prelude { pub use std::fmt::{self, Debug, Formatter}; pub use std::hash::Hash; pub use std::num::NonZeroUsize; @@ -101,19 +93,19 @@ pub fn new() -> Scope { std.def_class::<EmphNode>("emph"); std.def_class::<RawNode>("raw"); std.def_class::<MathNode>("math"); - std.def_class::<DecoNode<Underline>>("underline"); - std.def_class::<DecoNode<Strikethrough>>("strike"); - std.def_class::<DecoNode<Overline>>("overline"); + std.def_class::<DecoNode<UNDERLINE>>("underline"); + std.def_class::<DecoNode<STRIKETHROUGH>>("strike"); + std.def_class::<DecoNode<OVERLINE>>("overline"); std.def_class::<LinkNode>("link"); std.def_class::<HeadingNode>("heading"); - std.def_class::<ListNode<Unordered>>("list"); - std.def_class::<ListNode<Ordered>>("enum"); + std.def_class::<ListNode<UNORDERED>>("list"); + std.def_class::<ListNode<ORDERED>>("enum"); std.def_class::<TableNode>("table"); std.def_class::<ImageNode>("image"); - std.def_class::<ShapeNode<Rect>>("rect"); - std.def_class::<ShapeNode<Square>>("square"); - std.def_class::<ShapeNode<Ellipse>>("ellipse"); - std.def_class::<ShapeNode<Circle>>("circle"); + std.def_class::<ShapeNode<RECT>>("rect"); + std.def_class::<ShapeNode<SQUARE>>("square"); + std.def_class::<ShapeNode<ELLIPSE>>("ellipse"); + std.def_class::<ShapeNode<CIRCLE>>("circle"); // Layout. std.def_class::<HNode>("h"); @@ -123,9 +115,9 @@ pub fn new() -> Scope { std.def_class::<AlignNode>("align"); std.def_class::<PadNode>("pad"); std.def_class::<PlaceNode>("place"); - std.def_class::<TransformNode<Move>>("move"); - std.def_class::<TransformNode<Scale>>("scale"); - std.def_class::<TransformNode<Rotate>>("rotate"); + std.def_class::<TransformNode<MOVE>>("move"); + std.def_class::<TransformNode<SCALE>>("scale"); + std.def_class::<TransformNode<ROTATE>>("rotate"); std.def_class::<HideNode>("hide"); std.def_class::<StackNode>("stack"); std.def_class::<GridNode>("grid"); diff --git a/src/library/shape.rs b/src/library/shape.rs index 1eaad11a..dd306f91 100644 --- a/src/library/shape.rs +++ b/src/library/shape.rs @@ -7,15 +7,10 @@ use super::TextNode; /// Place a node into a sizable and fillable shape. #[derive(Debug, Hash)] -pub struct ShapeNode<S: ShapeKind> { - /// Which shape to place the child into. - pub kind: S, - /// The child node to place into the shape, if any. - pub child: Option<LayoutNode>, -} +pub struct ShapeNode<const S: ShapeKind>(pub Option<LayoutNode>); #[class] -impl<S: ShapeKind> ShapeNode<S> { +impl<const S: ShapeKind> ShapeNode<S> { /// How to fill the shape. pub const FILL: Option<Paint> = None; /// How the stroke the shape. @@ -26,12 +21,10 @@ impl<S: ShapeKind> ShapeNode<S> { pub const PADDING: Linear = Linear::zero(); fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { - let size = if !S::ROUND && S::QUADRATIC { - args.named::<Length>("size")?.map(Linear::from) - } else if S::ROUND && S::QUADRATIC { - args.named("radius")?.map(|r: Length| 2.0 * Linear::from(r)) - } else { - None + let size = match S { + SQUARE => args.named::<Length>("size")?.map(Linear::from), + CIRCLE => args.named::<Length>("radius")?.map(|r| 2.0 * Linear::from(r)), + _ => None, }; let width = match size { @@ -45,14 +38,12 @@ impl<S: ShapeKind> ShapeNode<S> { }; Ok(Template::inline( - ShapeNode { kind: S::default(), child: args.find()? } - .pack() - .sized(Spec::new(width, height)), + Self(args.find()?).pack().sized(Spec::new(width, height)), )) } } -impl<S: ShapeKind> Layout for ShapeNode<S> { +impl<const S: ShapeKind> Layout for ShapeNode<S> { fn layout( &self, ctx: &mut LayoutContext, @@ -60,9 +51,9 @@ impl<S: ShapeKind> Layout for ShapeNode<S> { styles: StyleChain, ) -> Vec<Constrained<Arc<Frame>>> { let mut frames; - if let Some(child) = &self.child { + if let Some(child) = &self.0 { let mut padding = styles.get(Self::PADDING); - if S::ROUND { + if is_round(S) { padding.rel += Relative::new(0.5 - SQRT_2 / 4.0); } @@ -74,7 +65,7 @@ impl<S: ShapeKind> Layout for ShapeNode<S> { // Relayout with full expansion into square region to make sure // the result is really a square or circle. - if S::QUADRATIC { + if is_quadratic(S) { let length = if regions.expand.x || regions.expand.y { let target = regions.expand.select(regions.current, Size::zero()); target.x.max(target.y) @@ -95,7 +86,7 @@ impl<S: ShapeKind> Layout for ShapeNode<S> { let mut size = Size::new(Length::pt(45.0), Length::pt(30.0)).min(regions.current); - if S::QUADRATIC { + if is_quadratic(S) { let length = if regions.expand.x || regions.expand.y { let target = regions.expand.select(regions.current, Size::zero()); target.x.max(target.y) @@ -121,7 +112,7 @@ impl<S: ShapeKind> Layout for ShapeNode<S> { .map(|paint| Stroke { paint, thickness }); if fill.is_some() || stroke.is_some() { - let geometry = if S::ROUND { + let geometry = if is_round(S) { Geometry::Ellipse(frame.size) } else { Geometry::Rect(frame.size) @@ -140,44 +131,27 @@ impl<S: ShapeKind> Layout for ShapeNode<S> { } } -/// Categorizes shapes. -pub trait ShapeKind: Debug + Default + Hash + Sync + Send + 'static { - const ROUND: bool; - const QUADRATIC: bool; -} +/// A category of shape. +pub type ShapeKind = usize; /// A rectangle with equal side lengths. -#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)] -pub struct Square; - -impl ShapeKind for Square { - const ROUND: bool = false; - const QUADRATIC: bool = true; -} +pub const SQUARE: ShapeKind = 0; /// A quadrilateral with four right angles. -#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)] -pub struct Rect; - -impl ShapeKind for Rect { - const ROUND: bool = false; - const QUADRATIC: bool = false; -} +pub const RECT: ShapeKind = 1; /// An ellipse with coinciding foci. -#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)] -pub struct Circle; - -impl ShapeKind for Circle { - const ROUND: bool = true; - const QUADRATIC: bool = true; -} +pub const CIRCLE: ShapeKind = 2; /// A curve around two focal points. -#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)] -pub struct Ellipse; +pub const ELLIPSE: ShapeKind = 3; + +/// Whether a shape kind is curvy. +fn is_round(kind: ShapeKind) -> bool { + matches!(kind, CIRCLE | ELLIPSE) +} -impl ShapeKind for Ellipse { - const ROUND: bool = true; - const QUADRATIC: bool = false; +/// Whether a shape kind has equal side length. +fn is_quadratic(kind: ShapeKind) -> bool { + matches!(kind, SQUARE | CIRCLE) } diff --git a/src/library/text.rs b/src/library/text.rs index 1ccbb1c6..4ee64e7a 100644 --- a/src/library/text.rs +++ b/src/library/text.rs @@ -9,7 +9,7 @@ use rustybuzz::{Feature, UnicodeBuffer}; use ttf_parser::{GlyphId, OutlineBuilder, Tag}; use super::prelude::*; -use super::{DecoLine, Decoration}; +use super::Decoration; use crate::font::{ Face, FaceId, FontStore, FontStretch, FontStyle, FontVariant, FontWeight, VerticalFontMetric, @@ -826,12 +826,12 @@ impl<'a> ShapedText<'a> { ) { let face = fonts.get(text.face_id); let metrics = match deco.line { - DecoLine::Underline => face.underline, - DecoLine::Strikethrough => face.strikethrough, - DecoLine::Overline => face.overline, + super::STRIKETHROUGH => face.strikethrough, + super::OVERLINE => face.overline, + super::UNDERLINE | _ => face.underline, }; - let evade = deco.evade && deco.line != DecoLine::Strikethrough; + let evade = deco.evade && deco.line != super::STRIKETHROUGH; let extent = deco.extent.resolve(text.size); let offset = deco .offset diff --git a/src/library/transform.rs b/src/library/transform.rs index 6c16d02a..cc40921b 100644 --- a/src/library/transform.rs +++ b/src/library/transform.rs @@ -5,27 +5,45 @@ use crate::geom::Transform; /// Transform a node without affecting layout. #[derive(Debug, Hash)] -pub struct TransformNode<T: TransformKind> { +pub struct TransformNode<const T: TransformKind> { /// Transformation to apply to the contents. - pub kind: T, + pub transform: Transform, /// The node whose contents should be transformed. pub child: LayoutNode, } #[class] -impl<T: TransformKind> TransformNode<T> { +impl<const T: TransformKind> TransformNode<T> { /// The origin of the transformation. pub const ORIGIN: Spec<Option<Align>> = Spec::default(); fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + let transform = match T { + MOVE => { + let tx = args.named("x")?.unwrap_or_default(); + let ty = args.named("y")?.unwrap_or_default(); + Transform::translation(tx, ty) + } + ROTATE => { + let angle = args.named_or_find("angle")?.unwrap_or_default(); + Transform::rotation(angle) + } + SCALE | _ => { + let all = args.find()?; + let sx = args.named("x")?.or(all).unwrap_or(Relative::one()); + let sy = args.named("y")?.or(all).unwrap_or(Relative::one()); + Transform::scale(sx, sy) + } + }; + Ok(Template::inline(Self { - kind: T::construct(args)?, + transform, child: args.expect("body")?, })) } } -impl<T: TransformKind> Layout for TransformNode<T> { +impl<const T: TransformKind> Layout for TransformNode<T> { fn layout( &self, ctx: &mut LayoutContext, @@ -33,14 +51,12 @@ impl<T: TransformKind> Layout for TransformNode<T> { styles: StyleChain, ) -> Vec<Constrained<Arc<Frame>>> { let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON); - let matrix = self.kind.matrix(); - let mut frames = self.child.layout(ctx, regions, styles); for Constrained { item: frame, .. } in &mut frames { let Spec { x, y } = origin.zip(frame.size).map(|(o, s)| o.resolve(s)); let transform = Transform::translation(x, y) - .pre_concat(matrix) + .pre_concat(self.transform) .pre_concat(Transform::translation(-x, -y)); Arc::make_mut(frame).transform(transform); @@ -51,54 +67,13 @@ impl<T: TransformKind> Layout for TransformNode<T> { } /// Kinds of transformations. -pub trait TransformKind: Debug + Hash + Sized + Sync + Send + 'static { - fn construct(args: &mut Args) -> TypResult<Self>; - fn matrix(&self) -> Transform; -} +pub type TransformKind = usize; /// A translation on the X and Y axes. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub struct Move(pub Length, pub Length); - -impl TransformKind for Move { - fn construct(args: &mut Args) -> TypResult<Self> { - let tx = args.named("x")?.unwrap_or_default(); - let ty = args.named("y")?.unwrap_or_default(); - Ok(Self(tx, ty)) - } - - fn matrix(&self) -> Transform { - Transform::translation(self.0, self.1) - } -} +pub const MOVE: TransformKind = 0; /// A rotational transformation. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub struct Rotate(pub Angle); - -impl TransformKind for Rotate { - fn construct(args: &mut Args) -> TypResult<Self> { - Ok(Self(args.named_or_find("angle")?.unwrap_or_default())) - } - - fn matrix(&self) -> Transform { - Transform::rotation(self.0) - } -} +pub const ROTATE: TransformKind = 1; /// A scale transformation. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub struct Scale(pub Relative, pub Relative); - -impl TransformKind for Scale { - fn construct(args: &mut Args) -> TypResult<Self> { - let all = args.find()?; - let sx = args.named("x")?.or(all).unwrap_or(Relative::one()); - let sy = args.named("y")?.or(all).unwrap_or(Relative::one()); - Ok(Self(sx, sy)) - } - - fn matrix(&self) -> Transform { - Transform::scale(self.0, self.1) - } -} +pub const SCALE: TransformKind = 2; |
