133 lines
4.1 KiB
Rust
133 lines
4.1 KiB
Rust
use std::f64::consts::SQRT_2;
|
|
|
|
use decorum::N64;
|
|
|
|
use super::*;
|
|
use crate::layout::{
|
|
BackgroundNode, BackgroundShape, FixedNode, ImageNode, PadNode, Paint,
|
|
};
|
|
|
|
/// `image`: An image.
|
|
pub fn image(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
|
let path = args.expect::<Spanned<EcoString>>(ctx, "path to image file");
|
|
let width = args.named(ctx, "width");
|
|
let height = args.named(ctx, "height");
|
|
|
|
let mut node = None;
|
|
if let Some(path) = &path {
|
|
if let Some((resolved, _)) = ctx.resolve(&path.v, path.span) {
|
|
if let Some(id) = ctx.cache.image.load(ctx.loader, &resolved) {
|
|
node = Some(ImageNode { id, width, height });
|
|
} else {
|
|
ctx.diag(error!(path.span, "failed to load image"));
|
|
}
|
|
}
|
|
}
|
|
|
|
Value::template(move |ctx| {
|
|
if let Some(node) = node {
|
|
ctx.push_into_par(node);
|
|
}
|
|
})
|
|
}
|
|
|
|
/// `rect`: A rectangle with optional content.
|
|
pub fn rect(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
|
let width = args.named(ctx, "width");
|
|
let height = args.named(ctx, "height");
|
|
let fill = args.named(ctx, "fill");
|
|
let body = args.eat(ctx).unwrap_or_default();
|
|
rect_impl(width, height, None, fill, body)
|
|
}
|
|
|
|
/// `square`: A square with optional content.
|
|
pub fn square(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
|
let length = args.named::<Length>(ctx, "length").map(Linear::from);
|
|
let width = length.or_else(|| args.named(ctx, "width"));
|
|
let height = width.is_none().then(|| args.named(ctx, "height")).flatten();
|
|
let fill = args.named(ctx, "fill");
|
|
let body = args.eat(ctx).unwrap_or_default();
|
|
rect_impl(width, height, Some(N64::from(1.0)), fill, body)
|
|
}
|
|
|
|
fn rect_impl(
|
|
width: Option<Linear>,
|
|
height: Option<Linear>,
|
|
aspect: Option<N64>,
|
|
fill: Option<Color>,
|
|
body: TemplateValue,
|
|
) -> Value {
|
|
Value::template(move |ctx| {
|
|
let mut stack = ctx.exec_template_stack(&body);
|
|
stack.aspect = aspect;
|
|
|
|
let fixed = FixedNode { width, height, child: stack.into() };
|
|
|
|
if let Some(fill) = fill {
|
|
ctx.push_into_par(BackgroundNode {
|
|
shape: BackgroundShape::Rect,
|
|
fill: Paint::Color(fill),
|
|
child: fixed.into(),
|
|
});
|
|
} else {
|
|
ctx.push_into_par(fixed);
|
|
}
|
|
})
|
|
}
|
|
|
|
/// `ellipse`: An ellipse with optional content.
|
|
pub fn ellipse(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
|
let width = args.named(ctx, "width");
|
|
let height = args.named(ctx, "height");
|
|
let fill = args.named(ctx, "fill");
|
|
let body = args.eat(ctx).unwrap_or_default();
|
|
ellipse_impl(width, height, None, fill, body)
|
|
}
|
|
|
|
/// `circle`: A circle with optional content.
|
|
pub fn circle(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
|
let radius = args.named::<Length>(ctx, "radius").map(|r| 2.0 * Linear::from(r));
|
|
let width = radius.or_else(|| args.named(ctx, "width"));
|
|
let height = width.is_none().then(|| args.named(ctx, "height")).flatten();
|
|
let fill = args.named(ctx, "fill");
|
|
let body = args.eat(ctx).unwrap_or_default();
|
|
ellipse_impl(width, height, Some(N64::from(1.0)), fill, body)
|
|
}
|
|
|
|
fn ellipse_impl(
|
|
width: Option<Linear>,
|
|
height: Option<Linear>,
|
|
aspect: Option<N64>,
|
|
fill: Option<Color>,
|
|
body: TemplateValue,
|
|
) -> Value {
|
|
Value::template(move |ctx| {
|
|
// This padding ratio ensures that the rectangular padded region fits
|
|
// perfectly into the ellipse.
|
|
const PAD: f64 = 0.5 - SQRT_2 / 4.0;
|
|
|
|
let mut stack = ctx.exec_template_stack(&body);
|
|
stack.aspect = aspect;
|
|
|
|
let fixed = FixedNode {
|
|
width,
|
|
height,
|
|
child: PadNode {
|
|
padding: Sides::splat(Relative::new(PAD).into()),
|
|
child: stack.into(),
|
|
}
|
|
.into(),
|
|
};
|
|
|
|
if let Some(fill) = fill {
|
|
ctx.push_into_par(BackgroundNode {
|
|
shape: BackgroundShape::Ellipse,
|
|
fill: Paint::Color(fill),
|
|
child: fixed.into(),
|
|
});
|
|
} else {
|
|
ctx.push_into_par(fixed);
|
|
}
|
|
})
|
|
}
|