summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-02-17 11:55:28 +0100
committerLaurenz <laurmaedje@gmail.com>2022-02-17 13:28:04 +0100
commitab95627d873239182e7b28b266f8b9f9da5cdbb1 (patch)
treeae6589d60d32a7ae884326911600c733dc32d977 /src
parent5965515a1ef1fe398235311185d531efc2750247 (diff)
Switch to const generics for nodes
Diffstat (limited to 'src')
-rw-r--r--src/diag.rs2
-rw-r--r--src/eval/mod.rs10
-rw-r--r--src/eval/template.rs4
-rw-r--r--src/layout/mod.rs8
-rw-r--r--src/library/deco.rs58
-rw-r--r--src/library/list.rs53
-rw-r--r--src/library/mod.rs36
-rw-r--r--src/library/shape.rs80
-rw-r--r--src/library/text.rs10
-rw-r--r--src/library/transform.rs81
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;