summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-03-20 20:19:30 +0100
committerLaurenz <laurmaedje@gmail.com>2021-03-20 20:19:30 +0100
commit898728f260923a91444eb23b522d0abf01a4299b (patch)
tree64fdd3f78e16e6428e765a8e2d99c3cd910bd9df /src/library
parent6cb9fe9064a037224b6560b69b441b72e787fa94 (diff)
Square, circle and ellipse 🔵
Diffstat (limited to 'src/library')
-rw-r--r--src/library/font.rs2
-rw-r--r--src/library/image.rs10
-rw-r--r--src/library/mod.rs3
-rw-r--r--src/library/shapes.rs155
-rw-r--r--src/library/spacing.rs7
5 files changed, 146 insertions, 31 deletions
diff --git a/src/library/font.rs b/src/library/font.rs
index 0993f7f0..ed2c0ef3 100644
--- a/src/library/font.rs
+++ b/src/library/font.rs
@@ -16,7 +16,7 @@ use super::*;
/// - Font Stretch: `stretch`, of type `relative`, between 0.5 and 2.0.
/// - Top edge of the font: `top-edge`, of type `vertical-font-metric`.
/// - Bottom edge of the font: `bottom-edge`, of type `vertical-font-metric`.
-/// - Fill color the glyphs: `color`, of type `color`.
+/// - Color the glyphs: `color`, of type `color`.
/// - Serif family definition: `serif`, of type `font-familiy-list`.
/// - Sans-serif family definition: `sans-serif`, of type `font-familiy-list`.
/// - Monospace family definition: `monospace`, of type `font-familiy-list`.
diff --git a/src/library/image.rs b/src/library/image.rs
index 8cb09463..10217e31 100644
--- a/src/library/image.rs
+++ b/src/library/image.rs
@@ -25,7 +25,7 @@ pub fn image(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
let loaded = ctx.env.resources.load(&path.v, ImageResource::parse);
if let Some((res, img)) = loaded {
let dimensions = img.buf.dimensions();
- ctx.push(NodeImage {
+ ctx.push(ImageNode {
res,
dimensions,
width,
@@ -41,7 +41,7 @@ pub fn image(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
/// An image node.
#[derive(Debug, Clone, PartialEq)]
-struct NodeImage {
+struct ImageNode {
/// How to align this image node in its parent.
aligns: LayoutAligns,
/// The resource id of the image file.
@@ -54,7 +54,7 @@ struct NodeImage {
height: Option<Linear>,
}
-impl Layout for NodeImage {
+impl Layout for ImageNode {
fn layout(&self, _: &mut LayoutContext, areas: &Areas) -> Fragment {
let Areas { current, full, .. } = areas;
@@ -90,8 +90,8 @@ impl Layout for NodeImage {
}
}
-impl From<NodeImage> for AnyNode {
- fn from(image: NodeImage) -> Self {
+impl From<ImageNode> for AnyNode {
+ fn from(image: ImageNode) -> Self {
Self::new(image)
}
}
diff --git a/src/library/mod.rs b/src/library/mod.rs
index d0920cf1..b09f94a0 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -51,6 +51,8 @@ pub fn new() -> Scope {
}
func!("align", align);
+ func!("circle", circle);
+ func!("ellipse", ellipse);
func!("font", font);
func!("h", h);
func!("image", image);
@@ -58,6 +60,7 @@ pub fn new() -> Scope {
func!("page", page);
func!("pagebreak", pagebreak);
func!("paragraph", par);
+ func!("square", square);
func!("rect", rect);
func!("repr", repr);
func!("rgb", rgb);
diff --git a/src/library/shapes.rs b/src/library/shapes.rs
index 211a4f2e..a5faf73e 100644
--- a/src/library/shapes.rs
+++ b/src/library/shapes.rs
@@ -1,49 +1,162 @@
+use std::f64::consts::SQRT_2;
+
use super::*;
-use crate::layout::{BackgroundNode, Fill, FixedNode};
+use crate::color::Color;
+use crate::layout::{BackgroundNode, BackgroundShape, Fill, FixedNode, PadNode};
-/// `rect`: Create a rectangular box.
+/// `rect`: Create a rectangle.
///
/// # Positional parameters
/// - Body: optional, of type `template`.
///
/// # Named parameters
-/// - Width of the box: `width`, of type `linear` relative to parent width.
-/// - Height of the box: `height`, of type `linear` relative to parent height.
-/// - Main layouting direction: `main-dir`, of type `direction`.
-/// - Cross layouting direction: `cross-dir`, of type `direction`.
-/// - Fill color of the box: `fill`, of type `color`.
+/// - Width: `width`, of type `linear` relative to parent width.
+/// - Height: `height`, of type `linear` relative to parent height.
+/// - Fill color: `fill`, of type `color`.
///
/// # Return value
/// A template that places the body into a rectangle.
-///
-/// # Relevant types and constants
-/// - Type `direction`
-/// - `ltr` (left to right)
-/// - `rtl` (right to left)
-/// - `ttb` (top to bottom)
-/// - `btt` (bottom to top)
pub fn rect(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
let width = args.get(ctx, "width");
let height = args.get(ctx, "height");
- let main = args.get(ctx, "main-dir");
- let cross = args.get(ctx, "cross-dir");
let fill = args.get(ctx, "fill");
let body = args.find::<TemplateValue>(ctx).unwrap_or_default();
+ rect_impl("rect", width, height, None, fill, body)
+}
- Value::template("box", move |ctx| {
+/// `square`: Create a square.
+///
+/// # Positional parameters
+/// - Body: optional, of type `template`.
+///
+/// # Named parameters
+/// - Side length: `length`, of type `length`.
+/// - Width: `width`, of type `linear` relative to parent width.
+/// - Height: `height`, of type `linear` relative to parent height.
+/// - Fill color: `fill`, of type `color`.
+///
+/// Note that you can specify only one of `length`, `width` and `height`. The
+/// width and height parameters exist so that you can size the square relative
+/// to its parent's size, which isn't possible by setting the side length.
+///
+/// # Return value
+/// A template that places the body into a square.
+pub fn square(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
+ let length = args.get::<Length>(ctx, "length").map(Linear::from);
+ let width = length.or_else(|| args.get(ctx, "width"));
+ let height = if width.is_none() { args.get(ctx, "height") } else { None };
+ let fill = args.get(ctx, "fill");
+ let body = args.find::<TemplateValue>(ctx).unwrap_or_default();
+ rect_impl("square", width, height, Some(1.0), fill, body)
+}
+
+fn rect_impl(
+ name: &str,
+ width: Option<Linear>,
+ height: Option<Linear>,
+ aspect: Option<f64>,
+ fill: Option<Color>,
+ body: TemplateValue,
+) -> Value {
+ Value::template(name, move |ctx| {
let snapshot = ctx.state.clone();
+ let child = ctx.exec(&body).into();
+ let node = FixedNode { width, height, aspect, child };
+
+ if let Some(color) = fill {
+ ctx.push(BackgroundNode {
+ shape: BackgroundShape::Rect,
+ fill: Fill::Color(color),
+ child: node.into(),
+ });
+ } else {
+ ctx.push(node);
+ }
+
+ ctx.state = snapshot;
+ })
+}
- ctx.set_dirs(Gen::new(main, cross));
+/// `ellipse`: Create an ellipse.
+///
+/// # Positional parameters
+/// - Body: optional, of type `template`.
+///
+/// # Named parameters
+/// - Width: `width`, of type `linear` relative to parent width.
+/// - Height: `height`, of type `linear` relative to parent height.
+/// - Fill color: `fill`, of type `color`.
+///
+/// # Return value
+/// A template that places the body into an ellipse.
+pub fn ellipse(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
+ let width = args.get(ctx, "width");
+ let height = args.get(ctx, "height");
+ let fill = args.get(ctx, "fill");
+ let body = args.find::<TemplateValue>(ctx).unwrap_or_default();
+ ellipse_impl("ellipse", width, height, None, fill, body)
+}
+/// `circle`: Create a circle.
+///
+/// # Positional parameters
+/// - Body: optional, of type `template`.
+///
+/// # Named parameters
+/// - Radius: `radius`, of type `length`.
+/// - Width: `width`, of type `linear` relative to parent width.
+/// - Height: `height`, of type `linear` relative to parent height.
+/// - Fill color: `fill`, of type `color`.
+///
+/// Note that you can specify only one of `radius`, `width` and `height`. The
+/// width and height parameters exist so that you can size the circle relative
+/// to its parent's size, which isn't possible by setting the radius.
+///
+/// # Return value
+/// A template that places the body into a circle.
+pub fn circle(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
+ let radius = args.get::<Length>(ctx, "radius").map(|r| 2.0 * Linear::from(r));
+ let width = radius.or_else(|| args.get(ctx, "width"));
+ let height = if width.is_none() { args.get(ctx, "height") } else { None };
+ let fill = args.get(ctx, "fill");
+ let body = args.find::<TemplateValue>(ctx).unwrap_or_default();
+ ellipse_impl("circle", width, height, Some(1.0), fill, body)
+}
+
+fn ellipse_impl(
+ name: &str,
+ width: Option<Linear>,
+ height: Option<Linear>,
+ aspect: Option<f64>,
+ fill: Option<Color>,
+ body: TemplateValue,
+) -> Value {
+ Value::template(name, move |ctx| {
+ // This padding ratio ensures that the rectangular padded area fits
+ // perfectly into the ellipse.
+ const PAD: f64 = 0.5 - SQRT_2 / 4.0;
+
+ let snapshot = ctx.state.clone();
let child = ctx.exec(&body).into();
- let fixed = FixedNode { width, height, child };
+ let node = FixedNode {
+ width,
+ height,
+ aspect,
+ child: PadNode {
+ padding: Sides::uniform(Relative::new(PAD).into()),
+ child,
+ }
+ .into(),
+ };
+
if let Some(color) = fill {
ctx.push(BackgroundNode {
+ shape: BackgroundShape::Ellipse,
fill: Fill::Color(color),
- child: fixed.into(),
+ child: node.into(),
});
} else {
- ctx.push(fixed);
+ ctx.push(node);
}
ctx.state = snapshot;
diff --git a/src/library/spacing.rs b/src/library/spacing.rs
index fee802fa..c96b8be4 100644
--- a/src/library/spacing.rs
+++ b/src/library/spacing.rs
@@ -9,7 +9,7 @@ use crate::layout::SpacingNode;
/// # Return value
/// A template that adds horizontal spacing.
pub fn h(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- spacing(ctx, args, SpecAxis::Horizontal)
+ spacing_impl(ctx, args, SpecAxis::Horizontal)
}
/// `v`: Add vertical spacing.
@@ -20,11 +20,10 @@ pub fn h(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
/// # Return value
/// A template that adds vertical spacing.
pub fn v(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- spacing(ctx, args, SpecAxis::Vertical)
+ spacing_impl(ctx, args, SpecAxis::Vertical)
}
-/// Apply spacing along a specific axis.
-fn spacing(ctx: &mut EvalContext, args: &mut FuncArgs, axis: SpecAxis) -> Value {
+fn spacing_impl(ctx: &mut EvalContext, args: &mut FuncArgs, axis: SpecAxis) -> Value {
let spacing: Option<Linear> = args.require(ctx, "spacing");
Value::template("spacing", move |ctx| {
if let Some(linear) = spacing {