diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-04-08 15:08:26 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-04-08 15:45:14 +0200 |
| commit | 712c00ecb72b67da2c0788e5d3eb4dcc6366b2a7 (patch) | |
| tree | f5d7ef4341a4728c980d020cc173fa6bb70feaff /src/library/graphics | |
| parent | 977ac77e6a3298be2644a8231e93acbef9f7f396 (diff) | |
Em units
Diffstat (limited to 'src/library/graphics')
| -rw-r--r-- | src/library/graphics/image.rs | 2 | ||||
| -rw-r--r-- | src/library/graphics/line.rs | 43 | ||||
| -rw-r--r-- | src/library/graphics/shape.rs | 9 | ||||
| -rw-r--r-- | src/library/graphics/transform.rs | 55 |
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; |
