summaryrefslogtreecommitdiff
path: root/src/library/graphics
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-04-08 15:08:26 +0200
committerLaurenz <laurmaedje@gmail.com>2022-04-08 15:45:14 +0200
commit712c00ecb72b67da2c0788e5d3eb4dcc6366b2a7 (patch)
treef5d7ef4341a4728c980d020cc173fa6bb70feaff /src/library/graphics
parent977ac77e6a3298be2644a8231e93acbef9f7f396 (diff)
Em units
Diffstat (limited to 'src/library/graphics')
-rw-r--r--src/library/graphics/image.rs2
-rw-r--r--src/library/graphics/line.rs43
-rw-r--r--src/library/graphics/shape.rs9
-rw-r--r--src/library/graphics/transform.rs55
4 files changed, 80 insertions, 29 deletions
diff --git a/src/library/graphics/image.rs b/src/library/graphics/image.rs
index 23ad52ab..193dc60e 100644
--- a/src/library/graphics/image.rs
+++ b/src/library/graphics/image.rs
@@ -13,7 +13,7 @@ impl ImageNode {
fn construct(ctx: &mut Context, args: &mut Args) -> TypResult<Content> {
let path = args.expect::<Spanned<EcoString>>("path to image file")?;
- let full = ctx.resolve(&path.v);
+ let full = ctx.complete_path(&path.v);
let id = ctx.images.load(&full).map_err(|err| match err.kind() {
std::io::ErrorKind::NotFound => error!(path.span, "file not found"),
_ => error!(path.span, "failed to load image ({})", err),
diff --git a/src/library/graphics/line.rs b/src/library/graphics/line.rs
index 571506c1..1dd138e6 100644
--- a/src/library/graphics/line.rs
+++ b/src/library/graphics/line.rs
@@ -4,9 +4,9 @@ use crate::library::prelude::*;
#[derive(Debug, Hash)]
pub struct LineNode {
/// Where the line starts.
- origin: Spec<Relative<Length>>,
+ origin: Spec<Relative<RawLength>>,
/// The offset from the `origin` where the line ends.
- delta: Spec<Relative<Length>>,
+ delta: Spec<Relative<RawLength>>,
}
#[node]
@@ -14,15 +14,17 @@ impl LineNode {
/// How to stroke the line.
pub const STROKE: Paint = Color::BLACK.into();
/// The line's thickness.
- pub const THICKNESS: Length = Length::pt(1.0);
+ #[property(resolve)]
+ pub const THICKNESS: RawLength = Length::pt(1.0).into();
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
let origin = args.named("origin")?.unwrap_or_default();
- let delta = match args.named::<Spec<Relative<Length>>>("to")? {
+
+ let delta = match args.named::<Spec<Relative<RawLength>>>("to")? {
Some(to) => to.zip(origin).map(|(to, from)| to - from),
None => {
let length = args
- .named::<Relative<Length>>("length")?
+ .named::<Relative<RawLength>>("length")?
.unwrap_or(Length::cm(1.0).into());
let angle = args.named::<Angle>("angle")?.unwrap_or_default();
@@ -50,18 +52,37 @@ impl Layout for LineNode {
thickness,
});
- let resolved_origin =
- self.origin.zip(regions.base).map(|(l, b)| Relative::resolve(l, b));
- let resolved_delta =
- self.delta.zip(regions.base).map(|(l, b)| Relative::resolve(l, b));
+ let origin = self
+ .origin
+ .resolve(styles)
+ .zip(regions.base)
+ .map(|(l, b)| l.relative_to(b));
+
+ let delta = self
+ .delta
+ .resolve(styles)
+ .zip(regions.base)
+ .map(|(l, b)| l.relative_to(b));
- let geometry = Geometry::Line(resolved_delta.to_point());
+ let geometry = Geometry::Line(delta.to_point());
let shape = Shape { geometry, fill: None, stroke };
let target = regions.expand.select(regions.first, Size::zero());
let mut frame = Frame::new(target);
- frame.push(resolved_origin.to_point(), Element::Shape(shape));
+ frame.push(origin.to_point(), Element::Shape(shape));
Ok(vec![Arc::new(frame)])
}
}
+
+castable! {
+ Spec<Relative<RawLength>>,
+ Expected: "array of two relative lengths",
+ Value::Array(array) => {
+ let mut iter = array.into_iter();
+ match (iter.next(), iter.next(), iter.next()) {
+ (Some(a), Some(b), None) => Spec::new(a.cast()?, b.cast()?),
+ _ => Err("point array must contain exactly two entries")?,
+ }
+ },
+}
diff --git a/src/library/graphics/shape.rs b/src/library/graphics/shape.rs
index 9faa4c52..ec6f735b 100644
--- a/src/library/graphics/shape.rs
+++ b/src/library/graphics/shape.rs
@@ -26,14 +26,15 @@ impl<const S: ShapeKind> ShapeNode<S> {
/// How to stroke the shape.
pub const STROKE: Smart<Option<Paint>> = Smart::Auto;
/// The stroke's thickness.
- pub const THICKNESS: Length = Length::pt(1.0);
+ #[property(resolve)]
+ pub const THICKNESS: RawLength = Length::pt(1.0).into();
/// How much to pad the shape's content.
- pub const PADDING: Relative<Length> = Relative::zero();
+ pub const PADDING: Relative<RawLength> = Relative::zero();
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
let size = match S {
- SQUARE => args.named::<Length>("size")?.map(Relative::from),
- CIRCLE => args.named::<Length>("radius")?.map(|r| 2.0 * Relative::from(r)),
+ SQUARE => args.named::<RawLength>("size")?.map(Relative::from),
+ CIRCLE => args.named::<RawLength>("radius")?.map(|r| 2.0 * Relative::from(r)),
_ => None,
};
diff --git a/src/library/graphics/transform.rs b/src/library/graphics/transform.rs
index 67f9cad9..ea021cc1 100644
--- a/src/library/graphics/transform.rs
+++ b/src/library/graphics/transform.rs
@@ -1,6 +1,46 @@
use crate::geom::Transform;
use crate::library::prelude::*;
+/// Move a node without affecting layout.
+#[derive(Debug, Hash)]
+pub struct MoveNode {
+ /// The offset by which to move the node.
+ pub delta: Spec<Relative<RawLength>>,
+ /// The node whose contents should be moved.
+ pub child: LayoutNode,
+}
+
+#[node]
+impl MoveNode {
+ fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
+ let dx = args.named("x")?.unwrap_or_default();
+ let dy = args.named("y")?.unwrap_or_default();
+ Ok(Content::inline(Self {
+ delta: Spec::new(dx, dy),
+ child: args.expect("body")?,
+ }))
+ }
+}
+
+impl Layout for MoveNode {
+ fn layout(
+ &self,
+ ctx: &mut Context,
+ regions: &Regions,
+ styles: StyleChain,
+ ) -> TypResult<Vec<Arc<Frame>>> {
+ let mut frames = self.child.layout(ctx, regions, styles)?;
+
+ let delta = self.delta.resolve(styles);
+ for frame in &mut frames {
+ let delta = delta.zip(frame.size).map(|(d, s)| d.relative_to(s));
+ Arc::make_mut(frame).translate(delta.to_point());
+ }
+
+ Ok(frames)
+ }
+}
+
/// Transform a node without affecting layout.
#[derive(Debug, Hash)]
pub struct TransformNode<const T: TransformKind> {
@@ -10,13 +50,10 @@ pub struct TransformNode<const T: TransformKind> {
pub child: LayoutNode,
}
-/// Transform a node by translating it without affecting layout.
-pub type MoveNode = TransformNode<MOVE>;
-
-/// Transform a node by rotating it without affecting layout.
+/// Rotate a node without affecting layout.
pub type RotateNode = TransformNode<ROTATE>;
-/// Transform a node by scaling it without affecting layout.
+/// Scale a node without affecting layout.
pub type ScaleNode = TransformNode<SCALE>;
#[node]
@@ -27,11 +64,6 @@ impl<const T: TransformKind> TransformNode<T> {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
let transform = match T {
- MOVE => {
- let tx = args.named("x")?.unwrap_or_default();
- let ty = args.named("y")?.unwrap_or_default();
- Transform::translate(tx, ty)
- }
ROTATE => {
let angle = args.named_or_find("angle")?.unwrap_or_default();
Transform::rotate(angle)
@@ -77,9 +109,6 @@ impl<const T: TransformKind> Layout for TransformNode<T> {
/// Kinds of transformations.
pub type TransformKind = usize;
-/// A translation on the X and Y axes.
-const MOVE: TransformKind = 0;
-
/// A rotational transformation.
const ROTATE: TransformKind = 1;