summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-11-23 22:04:08 +0100
committerLaurenz <laurmaedje@gmail.com>2021-11-23 22:04:08 +0100
commit8a88f71cb11565c1a78bd57f02a8df17cb2bf7a0 (patch)
tree8802c1ff48e2be118e3872d25bd2f2c1f7a21b4a /src/library
parentc77c5a0f0ae6560a03a85e847006c29de9c7ae62 (diff)
Transformations
Diffstat (limited to 'src/library')
-rw-r--r--src/library/image.rs18
-rw-r--r--src/library/mod.rs8
-rw-r--r--src/library/par.rs2
-rw-r--r--src/library/placed.rs5
-rw-r--r--src/library/shape.rs21
-rw-r--r--src/library/sized.rs10
-rw-r--r--src/library/transform.rs66
7 files changed, 86 insertions, 44 deletions
diff --git a/src/library/image.rs b/src/library/image.rs
index a53eacc5..17822619 100644
--- a/src/library/image.rs
+++ b/src/library/image.rs
@@ -7,7 +7,8 @@ use crate::image::ImageId;
/// `image`: An image.
pub fn image(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
let path = args.expect::<Spanned<EcoString>>("path to image file")?;
- let sizing = Spec::new(args.named("width")?, args.named("height")?);
+ let width = args.named("width")?;
+ let height = args.named("height")?;
let fit = args.named("fit")?.unwrap_or_default();
// Load the image.
@@ -20,7 +21,7 @@ pub fn image(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
})?;
Ok(Value::Template(Template::from_inline(move |_| {
- ImageNode { id, fit }.pack().sized(sizing)
+ ImageNode { id, fit }.pack().sized(Spec::new(width, height))
})))
}
@@ -81,13 +82,12 @@ impl Layout for ImageNode {
// Create a clipping group if the image mode is `cover`.
if self.fit == ImageFit::Cover {
- let group = Group {
- frame: Rc::new(frame),
- clips: self.fit == ImageFit::Cover,
- };
-
- frame = Frame::new(canvas, canvas.h);
- frame.push(Point::zero(), Element::Group(group));
+ let mut wrapper = Frame::new(canvas, canvas.h);
+ wrapper.push(
+ Point::zero(),
+ Element::Group(Group::new(Rc::new(frame)).clips(true)),
+ );
+ frame = wrapper;
}
let mut cts = Constraints::new(regions.expand);
diff --git a/src/library/mod.rs b/src/library/mod.rs
index 9e7f6f28..4d730a7e 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -76,12 +76,14 @@ pub fn new() -> Scope {
std.def_func("box", box_);
std.def_func("block", block);
std.def_func("flow", flow);
+ std.def_func("stack", stack);
+ std.def_func("grid", grid);
+ std.def_func("pad", pad);
std.def_func("align", align);
std.def_func("place", place);
std.def_func("move", move_);
- std.def_func("stack", stack);
- std.def_func("pad", pad);
- std.def_func("grid", grid);
+ std.def_func("scale", scale);
+ std.def_func("rotate", rotate);
// Elements.
std.def_func("image", image);
diff --git a/src/library/par.rs b/src/library/par.rs
index 46dc304a..7f3f1088 100644
--- a/src/library/par.rs
+++ b/src/library/par.rs
@@ -628,7 +628,7 @@ impl<'a> LineStack<'a> {
for line in self.lines.drain(..) {
let frame = line.build(ctx, self.size.w);
- let pos = Point::new(Length::zero(), offset);
+ let pos = Point::with_y(offset);
if first {
output.baseline = pos.y + frame.baseline;
first = false;
diff --git a/src/library/placed.rs b/src/library/placed.rs
index e2c2e9e8..4f1c5b85 100644
--- a/src/library/placed.rs
+++ b/src/library/placed.rs
@@ -3,11 +3,12 @@ use super::prelude::*;
/// `place`: Place content at an absolute position.
pub fn place(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
let aligns = args.find().unwrap_or(Spec::new(Some(Align::Left), None));
- let offset = Spec::new(args.named("dx")?, args.named("dy")?);
+ let tx = args.named("dx")?.unwrap_or_default();
+ let ty = args.named("dy")?.unwrap_or_default();
let body: Template = args.expect("body")?;
Ok(Value::Template(Template::from_block(move |style| {
PlacedNode {
- child: body.pack(style).moved(offset).aligned(aligns),
+ child: body.pack(style).moved(Point::new(tx, ty)).aligned(aligns),
}
})))
}
diff --git a/src/library/shape.rs b/src/library/shape.rs
index 061b4d25..5d3504d0 100644
--- a/src/library/shape.rs
+++ b/src/library/shape.rs
@@ -5,8 +5,9 @@ use crate::util::RcExt;
/// `rect`: A rectangle with optional content.
pub fn rect(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
- let sizing = Spec::new(args.named("width")?, args.named("height")?);
- shape_impl(args, ShapeKind::Rect, sizing)
+ let width = args.named("width")?;
+ let height = args.named("height")?;
+ shape_impl(args, ShapeKind::Rect, width, height)
}
/// `square`: A square with optional content.
@@ -20,14 +21,14 @@ pub fn square(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
None => args.named("height")?,
size => size,
};
- let sizing = Spec::new(width, height);
- shape_impl(args, ShapeKind::Square, sizing)
+ shape_impl(args, ShapeKind::Square, width, height)
}
/// `ellipse`: An ellipse with optional content.
pub fn ellipse(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
- let sizing = Spec::new(args.named("width")?, args.named("height")?);
- shape_impl(args, ShapeKind::Ellipse, sizing)
+ let width = args.named("width")?;
+ let height = args.named("height")?;
+ shape_impl(args, ShapeKind::Ellipse, width, height)
}
/// `circle`: A circle with optional content.
@@ -41,14 +42,14 @@ pub fn circle(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
None => args.named("height")?,
diameter => diameter,
};
- let sizing = Spec::new(width, height);
- shape_impl(args, ShapeKind::Circle, sizing)
+ shape_impl(args, ShapeKind::Circle, width, height)
}
fn shape_impl(
args: &mut Args,
kind: ShapeKind,
- sizing: Spec<Option<Linear>>,
+ width: Option<Linear>,
+ height: Option<Linear>,
) -> TypResult<Value> {
// The default appearance of a shape.
let default = Stroke {
@@ -80,7 +81,7 @@ fn shape_impl(
child: body.as_ref().map(|body| body.pack(style).padded(padding)),
}
.pack()
- .sized(sizing)
+ .sized(Spec::new(width, height))
})))
}
diff --git a/src/library/sized.rs b/src/library/sized.rs
index 8d69afac..d137c51e 100644
--- a/src/library/sized.rs
+++ b/src/library/sized.rs
@@ -2,19 +2,21 @@ use super::prelude::*;
/// `box`: Size content and place it into a paragraph.
pub fn box_(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
- let sizing = Spec::new(args.named("width")?, args.named("height")?);
+ let width = args.named("width")?;
+ let height = args.named("height")?;
let body: Template = args.find().unwrap_or_default();
Ok(Value::Template(Template::from_inline(move |style| {
- body.pack(style).sized(sizing)
+ body.pack(style).sized(Spec::new(width, height))
})))
}
/// `block`: Size content and place it into the flow.
pub fn block(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
- let sizing = Spec::new(args.named("width")?, args.named("height")?);
+ let width = args.named("width")?;
+ let height = args.named("height")?;
let body: Template = args.find().unwrap_or_default();
Ok(Value::Template(Template::from_block(move |style| {
- body.pack(style).sized(sizing)
+ body.pack(style).sized(Spec::new(width, height))
})))
}
diff --git a/src/library/transform.rs b/src/library/transform.rs
index 7553bef2..8d1c6132 100644
--- a/src/library/transform.rs
+++ b/src/library/transform.rs
@@ -1,24 +1,54 @@
use super::prelude::*;
+use crate::geom::Transform;
/// `move`: Move content without affecting layout.
pub fn move_(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
- let offset = Spec::new(args.named("x")?, args.named("y")?);
+ let tx = args.named("x")?.unwrap_or_default();
+ let ty = args.named("y")?.unwrap_or_default();
+ let transform = Transform::translation(tx, ty);
+ transform_impl(args, transform)
+}
+
+/// `scale`: Scale content without affecting layout.
+pub fn scale(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
+ 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());
+ let transform = Transform::scaling(sx, sy);
+ transform_impl(args, transform)
+}
+
+/// `rotate`: Rotate content without affecting layout.
+pub fn rotate(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
+ let angle = args.expect("angle")?;
+ let transform = Transform::rotation(angle);
+ transform_impl(args, transform)
+}
+
+fn transform_impl(args: &mut Args, transform: Transform) -> TypResult<Value> {
let body: Template = args.expect("body")?;
+ let origin = args
+ .named("origin")?
+ .unwrap_or(Spec::splat(None))
+ .unwrap_or(Spec::new(Align::Center, Align::Horizon));
+
Ok(Value::Template(Template::from_inline(move |style| {
- body.pack(style).moved(offset)
+ body.pack(style).transformed(transform, origin)
})))
}
-/// A node that moves its child without affecting layout.
+/// A node that transforms its child without affecting layout.
#[derive(Debug, Hash)]
-pub struct MoveNode {
- /// The node whose contents should be moved.
+pub struct TransformNode {
+ /// The node whose contents should be transformed.
pub child: PackedNode,
- /// How much to move the contents.
- pub offset: Spec<Option<Linear>>,
+ /// Transformation to apply to the contents.
+ pub transform: Transform,
+ /// The origin of the transformation.
+ pub origin: Spec<Align>,
}
-impl Layout for MoveNode {
+impl Layout for TransformNode {
fn layout(
&self,
ctx: &mut LayoutContext,
@@ -26,13 +56,19 @@ impl Layout for MoveNode {
) -> Vec<Constrained<Rc<Frame>>> {
let mut frames = self.child.layout(ctx, regions);
- for (Constrained { item: frame, .. }, (_, base)) in
- frames.iter_mut().zip(regions.iter())
- {
- Rc::make_mut(frame).translate(Point::new(
- self.offset.x.map(|x| x.resolve(base.w)).unwrap_or_default(),
- self.offset.y.map(|y| y.resolve(base.h)).unwrap_or_default(),
- ));
+ for Constrained { item: frame, .. } in frames.iter_mut() {
+ let x = self.origin.x.resolve(frame.size.w);
+ let y = self.origin.y.resolve(frame.size.h);
+ let transform = Transform::translation(x, y)
+ .pre_concat(self.transform)
+ .pre_concat(Transform::translation(-x, -y));
+
+ let mut wrapper = Frame::new(frame.size, frame.baseline);
+ wrapper.push(
+ Point::zero(),
+ Element::Group(Group::new(std::mem::take(frame)).transform(transform)),
+ );
+ *frame = Rc::new(wrapper);
}
frames