mirror of
https://github.com/typst/typst
synced 2025-05-13 12:36:23 +08:00
Code Review: Heap is Stack. Unsafe is Good.
Spaghetti Code is Style.
This commit is contained in:
parent
33213abe7d
commit
6a8a0ec6ec
@ -40,6 +40,14 @@ impl Frame {
|
|||||||
self.elements.insert(0, (pos, element));
|
self.elements.insert(0, (pos, element));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add multiple elements at a position in the background.
|
||||||
|
pub fn prepend_multiple<I>(&mut self, insert: I)
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = (Point, Element)>,
|
||||||
|
{
|
||||||
|
self.elements.splice(0 .. 0, insert);
|
||||||
|
}
|
||||||
|
|
||||||
/// Add an element at a position in the foreground.
|
/// Add an element at a position in the foreground.
|
||||||
pub fn push(&mut self, pos: Point, element: Element) {
|
pub fn push(&mut self, pos: Point, element: Element) {
|
||||||
self.elements.push((pos, element));
|
self.elements.push((pos, element));
|
||||||
|
@ -64,51 +64,6 @@ impl Angle {
|
|||||||
pub fn cos(self) -> f64 {
|
pub fn cos(self) -> f64 {
|
||||||
self.to_rad().cos()
|
self.to_rad().cos()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the control points for a bezier curve that describes a circular arc
|
|
||||||
/// of this angle with the given radius.
|
|
||||||
pub fn bezier_arc(
|
|
||||||
self,
|
|
||||||
radius: Length,
|
|
||||||
rotate: bool,
|
|
||||||
mirror_x: bool,
|
|
||||||
mirror_y: bool,
|
|
||||||
) -> [Point; 4] {
|
|
||||||
let end = Point::new(self.cos() * radius - radius, self.sin() * radius);
|
|
||||||
let center = Point::new(-radius, Length::zero());
|
|
||||||
|
|
||||||
let mut ts = if mirror_y {
|
|
||||||
Transform::mirror_y()
|
|
||||||
} else {
|
|
||||||
Transform::identity()
|
|
||||||
};
|
|
||||||
|
|
||||||
if mirror_x {
|
|
||||||
ts = ts.pre_concat(Transform::mirror_x());
|
|
||||||
}
|
|
||||||
|
|
||||||
if rotate {
|
|
||||||
ts = ts.pre_concat(Transform::rotate(Angle::deg(90.0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
let a = center * -1.0;
|
|
||||||
let b = end - center;
|
|
||||||
|
|
||||||
let q1 = a.x.to_raw() * a.x.to_raw() + a.y.to_raw() * a.y.to_raw();
|
|
||||||
let q2 = q1 + a.x.to_raw() * b.x.to_raw() + a.y.to_raw() * b.y.to_raw();
|
|
||||||
let k2 = (4.0 / 3.0) * ((2.0 * q1 * q2).sqrt() - q2)
|
|
||||||
/ (a.x.to_raw() * b.y.to_raw() - a.y.to_raw() * b.x.to_raw());
|
|
||||||
|
|
||||||
let control_1 = Point::new(center.x + a.x - k2 * a.y, center.y + a.y + k2 * a.x);
|
|
||||||
let control_2 = Point::new(center.x + b.x + k2 * b.y, center.y + b.y - k2 * b.x);
|
|
||||||
|
|
||||||
[
|
|
||||||
Point::zero(),
|
|
||||||
control_1.transform(ts),
|
|
||||||
control_2.transform(ts),
|
|
||||||
end.transform(ts),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Numeric for Angle {
|
impl Numeric for Angle {
|
||||||
|
@ -71,3 +71,48 @@ impl Path {
|
|||||||
self.0.push(PathElement::ClosePath);
|
self.0.push(PathElement::ClosePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the control points for a bezier curve that describes a circular arc
|
||||||
|
/// of this angle with the given radius.
|
||||||
|
pub fn bezier_arc(
|
||||||
|
angle: Angle,
|
||||||
|
radius: Length,
|
||||||
|
rotate: bool,
|
||||||
|
mirror_x: bool,
|
||||||
|
mirror_y: bool,
|
||||||
|
) -> [Point; 4] {
|
||||||
|
let end = Point::new(angle.cos() * radius - radius, angle.sin() * radius);
|
||||||
|
let center = Point::new(-radius, Length::zero());
|
||||||
|
|
||||||
|
let mut ts = if mirror_y {
|
||||||
|
Transform::mirror_y()
|
||||||
|
} else {
|
||||||
|
Transform::identity()
|
||||||
|
};
|
||||||
|
|
||||||
|
if mirror_x {
|
||||||
|
ts = ts.pre_concat(Transform::mirror_x());
|
||||||
|
}
|
||||||
|
|
||||||
|
if rotate {
|
||||||
|
ts = ts.pre_concat(Transform::rotate(Angle::deg(90.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let a = center * -1.0;
|
||||||
|
let b = end - center;
|
||||||
|
|
||||||
|
let q1 = a.x.to_raw() * a.x.to_raw() + a.y.to_raw() * a.y.to_raw();
|
||||||
|
let q2 = q1 + a.x.to_raw() * b.x.to_raw() + a.y.to_raw() * b.y.to_raw();
|
||||||
|
let k2 = (4.0 / 3.0) * ((2.0 * q1 * q2).sqrt() - q2)
|
||||||
|
/ (a.x.to_raw() * b.y.to_raw() - a.y.to_raw() * b.x.to_raw());
|
||||||
|
|
||||||
|
let control_1 = Point::new(center.x + a.x - k2 * a.y, center.y + a.y + k2 * a.x);
|
||||||
|
let control_2 = Point::new(center.x + b.x + k2 * b.y, center.y + b.y - k2 * b.x);
|
||||||
|
|
||||||
|
[
|
||||||
|
Point::zero(),
|
||||||
|
control_1.transform(ts),
|
||||||
|
control_2.transform(ts),
|
||||||
|
end.transform(ts),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
134
src/geom/rect.rs
134
src/geom/rect.rs
@ -3,7 +3,7 @@ use super::*;
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
/// A rectangle with rounded corners.
|
/// A rectangle with rounded corners.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub struct Rect {
|
pub struct Rect {
|
||||||
size: Size,
|
size: Size,
|
||||||
radius: Sides<Length>,
|
radius: Sides<Length>,
|
||||||
@ -19,7 +19,7 @@ impl Rect {
|
|||||||
/// in the foreground. The function will output multiple items if the stroke
|
/// in the foreground. The function will output multiple items if the stroke
|
||||||
/// properties differ by side.
|
/// properties differ by side.
|
||||||
pub fn shapes(
|
pub fn shapes(
|
||||||
&self,
|
self,
|
||||||
fill: Option<Paint>,
|
fill: Option<Paint>,
|
||||||
stroke: Sides<Option<Stroke>>,
|
stroke: Sides<Option<Stroke>>,
|
||||||
) -> Vec<Shape> {
|
) -> Vec<Shape> {
|
||||||
@ -28,48 +28,64 @@ impl Rect {
|
|||||||
res.push(Shape {
|
res.push(Shape {
|
||||||
geometry: self.fill_geometry(),
|
geometry: self.fill_geometry(),
|
||||||
fill,
|
fill,
|
||||||
stroke: stroke.is_uniform().then(|| stroke.top).flatten(),
|
stroke: if stroke.is_uniform() { stroke.top } else { None },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if !stroke.is_uniform() {
|
if !stroke.is_uniform() {
|
||||||
for (path, stroke) in self.stroke_segments(Some(stroke)) {
|
for (path, stroke) in self.stroke_segments(stroke) {
|
||||||
if !stroke.is_some() {
|
if stroke.is_some() {
|
||||||
continue;
|
res.push(Shape {
|
||||||
|
geometry: Geometry::Path(path),
|
||||||
|
fill: None,
|
||||||
|
stroke,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
res.push(Shape {
|
|
||||||
geometry: Geometry::Path(path),
|
|
||||||
fill: None,
|
|
||||||
stroke,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Output the shape of the rectangle as a path or primitive rectangle,
|
||||||
|
/// depending on whether it is rounded.
|
||||||
|
fn fill_geometry(self) -> Geometry {
|
||||||
|
if self.radius.iter().copied().all(Length::is_zero) {
|
||||||
|
Geometry::Rect(self.size)
|
||||||
|
} else {
|
||||||
|
let mut paths = self.stroke_segments(Sides::splat(None));
|
||||||
|
assert_eq!(paths.len(), 1);
|
||||||
|
|
||||||
|
Geometry::Path(paths.pop().unwrap().0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Output the minimum number of paths along the rectangles border.
|
/// Output the minimum number of paths along the rectangles border.
|
||||||
fn stroke_segments(
|
fn stroke_segments(
|
||||||
&self,
|
self,
|
||||||
strokes: Option<Sides<Option<Stroke>>>,
|
strokes: Sides<Option<Stroke>>,
|
||||||
) -> Vec<(Path, Option<Stroke>)> {
|
) -> Vec<(Path, Option<Stroke>)> {
|
||||||
let strokes = strokes.unwrap_or_else(|| Sides::splat(None));
|
|
||||||
let mut res = vec![];
|
let mut res = vec![];
|
||||||
|
|
||||||
let mut connection = Connection::None;
|
let mut connection = Connection::default();
|
||||||
let mut path = Path::new();
|
let mut path = Path::new();
|
||||||
let mut always_continuous = true;
|
let mut always_continuous = true;
|
||||||
|
|
||||||
for side in [Side::Top, Side::Right, Side::Bottom, Side::Left] {
|
for side in [Side::Top, Side::Right, Side::Bottom, Side::Left] {
|
||||||
let radius = [self.radius.get(side.next_ccw()), self.radius.get(side)];
|
let is_continuous = strokes.get(side) == strokes.get(side.next_cw());
|
||||||
|
connection = connection.advance(is_continuous && side != Side::Left);
|
||||||
|
always_continuous &= is_continuous;
|
||||||
|
|
||||||
let stroke_continuity = strokes.get(side) == strokes.get(side.next_cw());
|
draw_side(
|
||||||
connection = connection.advance(stroke_continuity && side != Side::Left);
|
&mut path,
|
||||||
always_continuous &= stroke_continuity;
|
side,
|
||||||
|
self.size,
|
||||||
|
self.radius.get(side.next_ccw()),
|
||||||
|
self.radius.get(side),
|
||||||
|
connection,
|
||||||
|
);
|
||||||
|
|
||||||
draw_side(&mut path, side, self.size, radius[0], radius[1], connection);
|
if !is_continuous {
|
||||||
|
|
||||||
if !stroke_continuity {
|
|
||||||
res.push((mem::take(&mut path), strokes.get(side)));
|
res.push((mem::take(&mut path), strokes.get(side)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,19 +100,6 @@ impl Rect {
|
|||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Output the shape of the rectangle as a path or primitive rectangle,
|
|
||||||
/// depending on whether it is rounded.
|
|
||||||
fn fill_geometry(&self) -> Geometry {
|
|
||||||
if self.radius.iter().copied().all(Length::is_zero) {
|
|
||||||
Geometry::Rect(self.size)
|
|
||||||
} else {
|
|
||||||
let mut paths = self.stroke_segments(None);
|
|
||||||
assert_eq!(paths.len(), 1);
|
|
||||||
|
|
||||||
Geometry::Path(paths.pop().unwrap().0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draws one side of the rounded rectangle. Will always draw the left arc. The
|
/// Draws one side of the rounded rectangle. Will always draw the left arc. The
|
||||||
@ -110,19 +113,18 @@ fn draw_side(
|
|||||||
connection: Connection,
|
connection: Connection,
|
||||||
) {
|
) {
|
||||||
let reversed = |angle: Angle, radius, rotate, mirror_x, mirror_y| {
|
let reversed = |angle: Angle, radius, rotate, mirror_x, mirror_y| {
|
||||||
let [a, b, c, d] = angle.bezier_arc(radius, rotate, mirror_x, mirror_y);
|
let [a, b, c, d] = bezier_arc(angle, radius, rotate, mirror_x, mirror_y);
|
||||||
[d, c, b, a]
|
[d, c, b, a]
|
||||||
};
|
};
|
||||||
|
|
||||||
let angle_left = Angle::deg(if connection.left() { 90.0 } else { 45.0 });
|
let angle_left = Angle::deg(if connection.prev { 90.0 } else { 45.0 });
|
||||||
let angle_right = Angle::deg(if connection.right() { 90.0 } else { 45.0 });
|
let angle_right = Angle::deg(if connection.next { 90.0 } else { 45.0 });
|
||||||
|
|
||||||
let (arc1, arc2) = match side {
|
let (arc1, arc2) = match side {
|
||||||
Side::Top => {
|
Side::Top => {
|
||||||
let arc1 = reversed(angle_left, radius_left, true, true, false)
|
let arc1 = reversed(angle_left, radius_left, true, true, false)
|
||||||
.map(|x| x + Point::with_x(radius_left));
|
.map(|x| x + Point::with_x(radius_left));
|
||||||
let arc2 = (-angle_right)
|
let arc2 = bezier_arc(-angle_right, radius_right, true, true, false)
|
||||||
.bezier_arc(radius_right, true, true, false)
|
|
||||||
.map(|x| x + Point::with_x(size.x - radius_right));
|
.map(|x| x + Point::with_x(size.x - radius_right));
|
||||||
|
|
||||||
(arc1, arc2)
|
(arc1, arc2)
|
||||||
@ -131,8 +133,7 @@ fn draw_side(
|
|||||||
let arc1 = reversed(-angle_left, radius_left, false, false, false)
|
let arc1 = reversed(-angle_left, radius_left, false, false, false)
|
||||||
.map(|x| x + Point::new(size.x, radius_left));
|
.map(|x| x + Point::new(size.x, radius_left));
|
||||||
|
|
||||||
let arc2 = angle_right
|
let arc2 = bezier_arc(angle_right, radius_right, false, false, false)
|
||||||
.bezier_arc(radius_right, false, false, false)
|
|
||||||
.map(|x| x + Point::new(size.x, size.y - radius_right));
|
.map(|x| x + Point::new(size.x, size.y - radius_right));
|
||||||
|
|
||||||
(arc1, arc2)
|
(arc1, arc2)
|
||||||
@ -141,8 +142,7 @@ fn draw_side(
|
|||||||
let arc1 = reversed(-angle_left, radius_left, true, false, false)
|
let arc1 = reversed(-angle_left, radius_left, true, false, false)
|
||||||
.map(|x| x + Point::new(size.x - radius_left, size.y));
|
.map(|x| x + Point::new(size.x - radius_left, size.y));
|
||||||
|
|
||||||
let arc2 = angle_right
|
let arc2 = bezier_arc(angle_right, radius_right, true, false, false)
|
||||||
.bezier_arc(radius_right, true, false, false)
|
|
||||||
.map(|x| x + Point::new(radius_right, size.y));
|
.map(|x| x + Point::new(radius_right, size.y));
|
||||||
|
|
||||||
(arc1, arc2)
|
(arc1, arc2)
|
||||||
@ -151,15 +151,14 @@ fn draw_side(
|
|||||||
let arc1 = reversed(angle_left, radius_left, false, false, true)
|
let arc1 = reversed(angle_left, radius_left, false, false, true)
|
||||||
.map(|x| x + Point::with_y(size.y - radius_left));
|
.map(|x| x + Point::with_y(size.y - radius_left));
|
||||||
|
|
||||||
let arc2 = (-angle_right)
|
let arc2 = bezier_arc(-angle_right, radius_right, false, false, true)
|
||||||
.bezier_arc(radius_right, false, false, true)
|
|
||||||
.map(|x| x + Point::with_y(radius_right));
|
.map(|x| x + Point::with_y(radius_right));
|
||||||
|
|
||||||
(arc1, arc2)
|
(arc1, arc2)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if !connection.left() {
|
if !connection.prev {
|
||||||
path.move_to(if radius_left.is_zero() { arc1[3] } else { arc1[0] });
|
path.move_to(if radius_left.is_zero() { arc1[3] } else { arc1[0] });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,51 +168,24 @@ fn draw_side(
|
|||||||
|
|
||||||
path.line_to(arc2[0]);
|
path.line_to(arc2[0]);
|
||||||
|
|
||||||
if !connection.right() && !radius_right.is_zero() {
|
if !connection.next && !radius_right.is_zero() {
|
||||||
path.cubic_to(arc2[1], arc2[2], arc2[3]);
|
path.cubic_to(arc2[1], arc2[2], arc2[3]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A state machine that indicates which sides of the border strokes in a 2D
|
/// A state machine that indicates which sides of the border strokes in a 2D
|
||||||
/// polygon are connected to their neighboring sides.
|
/// polygon are connected to their neighboring sides.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
|
||||||
enum Connection {
|
struct Connection {
|
||||||
None,
|
prev: bool,
|
||||||
Left,
|
next: bool,
|
||||||
Right,
|
|
||||||
Both,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Connection {
|
impl Connection {
|
||||||
/// Advance to the next clockwise side of the polygon. The argument
|
/// Advance to the next clockwise side of the polygon. The argument
|
||||||
/// indicates whether the border is connected on the right side of the next
|
/// indicates whether the border is connected on the right side of the next
|
||||||
/// edge.
|
/// edge.
|
||||||
pub fn advance(self, right: bool) -> Self {
|
pub fn advance(self, next: bool) -> Self {
|
||||||
match self {
|
Self { prev: self.next, next }
|
||||||
Self::Right | Self::Both => {
|
|
||||||
if right {
|
|
||||||
Self::Both
|
|
||||||
} else {
|
|
||||||
Self::Left
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::Left | Self::None => {
|
|
||||||
if right {
|
|
||||||
Self::Right
|
|
||||||
} else {
|
|
||||||
Self::None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether there is a connection on the left.
|
|
||||||
fn left(self) -> bool {
|
|
||||||
matches!(self, Self::Left | Self::Both)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether there is a connection on the right.
|
|
||||||
fn right(self) -> bool {
|
|
||||||
matches!(self, Self::Right | Self::Both)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,19 @@ impl<T> Sides<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Zip two instances into an instance.
|
||||||
|
pub fn zip<F, V, W>(self, other: Sides<V>, mut f: F) -> Sides<W>
|
||||||
|
where
|
||||||
|
F: FnMut(T, V, Side) -> W,
|
||||||
|
{
|
||||||
|
Sides {
|
||||||
|
left: f(self.left, other.left, Side::Left),
|
||||||
|
top: f(self.top, other.top, Side::Top),
|
||||||
|
right: f(self.right, other.right, Side::Right),
|
||||||
|
bottom: f(self.bottom, other.bottom, Side::Bottom),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns an iterator over the sides.
|
/// Returns an iterator over the sides.
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||||
[&self.left, &self.top, &self.right, &self.bottom].into_iter()
|
[&self.left, &self.top, &self.right, &self.bottom].into_iter()
|
||||||
|
@ -26,26 +26,12 @@ impl Transform {
|
|||||||
|
|
||||||
/// Transform by mirroring along the x-axis.
|
/// Transform by mirroring along the x-axis.
|
||||||
pub fn mirror_x() -> Self {
|
pub fn mirror_x() -> Self {
|
||||||
Self {
|
Self::scale(Ratio::one(), -Ratio::one())
|
||||||
sx: Ratio::one(),
|
|
||||||
ky: Ratio::zero(),
|
|
||||||
kx: Ratio::zero(),
|
|
||||||
sy: -Ratio::one(),
|
|
||||||
tx: Length::zero(),
|
|
||||||
ty: Length::zero(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transform by mirroring along the y-axis.
|
/// Transform by mirroring along the y-axis.
|
||||||
pub fn mirror_y() -> Self {
|
pub fn mirror_y() -> Self {
|
||||||
Self {
|
Self::scale(-Ratio::one(), Ratio::one())
|
||||||
sx: -Ratio::one(),
|
|
||||||
ky: Ratio::zero(),
|
|
||||||
kx: Ratio::zero(),
|
|
||||||
sy: Ratio::one(),
|
|
||||||
tx: Length::zero(),
|
|
||||||
ty: Length::zero(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A translate transform.
|
/// A translate transform.
|
||||||
|
@ -78,7 +78,7 @@ impl<const S: ShapeKind> ShapeNode<S> {
|
|||||||
styles.set_opt(Self::INSET, args.named("inset")?);
|
styles.set_opt(Self::INSET, args.named("inset")?);
|
||||||
styles.set_opt(Self::OUTSET, args.named("outset")?);
|
styles.set_opt(Self::OUTSET, args.named("outset")?);
|
||||||
|
|
||||||
if S != CIRCLE {
|
if !is_round(S) {
|
||||||
styles.set_opt(Self::RADIUS, args.named("radius")?);
|
styles.set_opt(Self::RADIUS, args.named("radius")?);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,10 +97,7 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> {
|
|||||||
if let Some(child) = &self.0 {
|
if let Some(child) = &self.0 {
|
||||||
let mut inset = styles.get(Self::INSET);
|
let mut inset = styles.get(Self::INSET);
|
||||||
if is_round(S) {
|
if is_round(S) {
|
||||||
inset = inset.map(|mut side| {
|
inset = inset.map(|side| side + Ratio::new(0.5 - SQRT_2 / 4.0));
|
||||||
side.rel += Ratio::new(0.5 - SQRT_2 / 4.0);
|
|
||||||
side
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pad the child.
|
// Pad the child.
|
||||||
@ -158,18 +155,8 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let outset = styles.get(Self::OUTSET);
|
let outset = styles.get(Self::OUTSET).relative_to(frame.size);
|
||||||
let outset = Sides {
|
let size = frame.size + outset.sum_by_axis();
|
||||||
left: outset.left.relative_to(frame.size.x),
|
|
||||||
top: outset.top.relative_to(frame.size.y),
|
|
||||||
right: outset.right.relative_to(frame.size.x),
|
|
||||||
bottom: outset.bottom.relative_to(frame.size.y),
|
|
||||||
};
|
|
||||||
|
|
||||||
let size = Spec::new(
|
|
||||||
frame.size.x + outset.left + outset.right,
|
|
||||||
frame.size.y + outset.top + outset.bottom,
|
|
||||||
);
|
|
||||||
|
|
||||||
let radius = styles
|
let radius = styles
|
||||||
.get(Self::RADIUS)
|
.get(Self::RADIUS)
|
||||||
@ -186,11 +173,12 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> {
|
|||||||
};
|
};
|
||||||
frame.prepend(pos, Element::Shape(shape));
|
frame.prepend(pos, Element::Shape(shape));
|
||||||
} else {
|
} else {
|
||||||
for shape in
|
frame.prepend_multiple(
|
||||||
Rect::new(size, radius).shapes(fill, stroke).into_iter().rev()
|
Rect::new(size, radius)
|
||||||
{
|
.shapes(fill, stroke)
|
||||||
frame.prepend(pos, Element::Shape(shape));
|
.into_iter()
|
||||||
}
|
.map(|x| (pos, Element::Shape(x))),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,8 @@ impl PageNode {
|
|||||||
|
|
||||||
/// The page margin.
|
/// The page margin.
|
||||||
#[property(fold)]
|
#[property(fold)]
|
||||||
pub const MARGINS: Sides<Smart<Relative<RawLength>>> = Sides::splat(Smart::Auto);
|
pub const MARGINS: Sides<Option<Smart<Relative<RawLength>>>> =
|
||||||
|
Sides::splat(Smart::Auto);
|
||||||
|
|
||||||
/// How many columns the page has.
|
/// How many columns the page has.
|
||||||
pub const COLUMNS: NonZeroUsize = NonZeroUsize::new(1).unwrap();
|
pub const COLUMNS: NonZeroUsize = NonZeroUsize::new(1).unwrap();
|
||||||
@ -48,9 +49,7 @@ impl PageNode {
|
|||||||
|
|
||||||
styles.set_opt(Self::WIDTH, args.named("width")?);
|
styles.set_opt(Self::WIDTH, args.named("width")?);
|
||||||
styles.set_opt(Self::HEIGHT, args.named("height")?);
|
styles.set_opt(Self::HEIGHT, args.named("height")?);
|
||||||
|
|
||||||
styles.set_opt(Self::MARGINS, args.named("margins")?);
|
styles.set_opt(Self::MARGINS, args.named("margins")?);
|
||||||
|
|
||||||
styles.set_opt(Self::FLIPPED, args.named("flipped")?);
|
styles.set_opt(Self::FLIPPED, args.named("flipped")?);
|
||||||
styles.set_opt(Self::FILL, args.named("fill")?);
|
styles.set_opt(Self::FILL, args.named("fill")?);
|
||||||
styles.set_opt(Self::COLUMNS, args.named("columns")?);
|
styles.set_opt(Self::COLUMNS, args.named("columns")?);
|
||||||
|
@ -466,12 +466,7 @@ where
|
|||||||
type Output = Sides<T::Output>;
|
type Output = Sides<T::Output>;
|
||||||
|
|
||||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
fn fold(self, outer: Self::Output) -> Self::Output {
|
||||||
Sides {
|
self.zip(outer, |inner, outer, _| inner.fold(outer))
|
||||||
left: self.left.fold(outer.left),
|
|
||||||
top: self.top.fold(outer.top),
|
|
||||||
right: self.right.fold(outer.right),
|
|
||||||
bottom: self.bottom.fold(outer.bottom),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -479,25 +474,15 @@ impl Fold for Sides<Option<Relative<Length>>> {
|
|||||||
type Output = Sides<Relative<Length>>;
|
type Output = Sides<Relative<Length>>;
|
||||||
|
|
||||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
fn fold(self, outer: Self::Output) -> Self::Output {
|
||||||
Sides {
|
self.zip(outer, |inner, outer, _| inner.unwrap_or(outer))
|
||||||
left: self.left.unwrap_or(outer.left),
|
|
||||||
top: self.top.unwrap_or(outer.top),
|
|
||||||
right: self.right.unwrap_or(outer.right),
|
|
||||||
bottom: self.bottom.unwrap_or(outer.bottom),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fold for Sides<Smart<Relative<RawLength>>> {
|
impl Fold for Sides<Option<Smart<Relative<RawLength>>>> {
|
||||||
type Output = Sides<Smart<Relative<RawLength>>>;
|
type Output = Sides<Smart<Relative<RawLength>>>;
|
||||||
|
|
||||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
fn fold(self, outer: Self::Output) -> Self::Output {
|
||||||
Sides {
|
self.zip(outer, |inner, outer, _| inner.unwrap_or(outer))
|
||||||
left: self.left.or(outer.left),
|
|
||||||
top: self.top.or(outer.top),
|
|
||||||
right: self.right.or(outer.right),
|
|
||||||
bottom: self.bottom.or(outer.bottom),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
@ -35,20 +35,20 @@
|
|||||||
|
|
||||||
// Different strokes.
|
// Different strokes.
|
||||||
[
|
[
|
||||||
#set rect(stroke: (right: red,))
|
#set rect(stroke: (right: red))
|
||||||
#rect(width: 100%, fill: lime, stroke: (x: 5pt, y: 1pt))
|
#rect(width: 100%, fill: lime, stroke: (x: 5pt, y: 1pt))
|
||||||
]
|
]
|
||||||
|
|
||||||
---
|
---
|
||||||
// Outset padding.
|
// Outset padding.
|
||||||
#let inline-code(body) = [
|
#show node: raw as [
|
||||||
#set text("IBM Plex Mono", 8pt)
|
#set text("IBM Plex Mono", 8pt)
|
||||||
#h(.7em, weak: true)
|
#h(.7em, weak: true)
|
||||||
#rect(radius: 3pt, outset: (y: 3pt, x: 2.5pt), fill: rgb(239, 241, 243), body)
|
#rect(radius: 3pt, outset: (y: 3pt, x: 2.5pt), fill: rgb(239, 241, 243))[{node.text}]
|
||||||
#h(.7em, weak: true)
|
#h(.7em, weak: true)
|
||||||
]
|
]
|
||||||
|
|
||||||
Use the #inline-code[\*const ptr] pointer.
|
Use the `*const ptr` pointer.
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 15-38 unexpected key "cake"
|
// Error: 15-38 unexpected key "cake"
|
||||||
|
@ -11,10 +11,10 @@
|
|||||||
---
|
---
|
||||||
// Set individual margins.
|
// Set individual margins.
|
||||||
#set page(height: 40pt)
|
#set page(height: 40pt)
|
||||||
[#set page(margins: (left: 0pt,)); #align(left)[Left]]
|
[#set page(margins: (left: 0pt)); #align(left)[Left]]
|
||||||
[#set page(margins: (right: 0pt,)); #align(right)[Right]]
|
[#set page(margins: (right: 0pt)); #align(right)[Right]]
|
||||||
[#set page(margins: (top: 0pt,)); #align(top)[Top]]
|
[#set page(margins: (top: 0pt)); #align(top)[Top]]
|
||||||
[#set page(margins: (bottom: 0pt,)); #align(bottom)[Bottom]]
|
[#set page(margins: (bottom: 0pt)); #align(bottom)[Bottom]]
|
||||||
|
|
||||||
// Ensure that specific margins override general margins.
|
// Ensure that specific margins override general margins.
|
||||||
[#set page(margins: (rest: 0pt, left: 20pt)); Overriden]
|
[#set page(margins: (rest: 0pt, left: 20pt)); Overriden]
|
||||||
|
@ -66,7 +66,7 @@ fn main() {
|
|||||||
styles.set(PageNode::HEIGHT, Smart::Auto);
|
styles.set(PageNode::HEIGHT, Smart::Auto);
|
||||||
styles.set(
|
styles.set(
|
||||||
PageNode::MARGINS,
|
PageNode::MARGINS,
|
||||||
Sides::splat(Smart::Custom(Length::pt(10.0).into())),
|
Sides::splat(Some(Smart::Custom(Length::pt(10.0).into()))),
|
||||||
);
|
);
|
||||||
styles.set(TextNode::SIZE, TextSize(Length::pt(10.0).into()));
|
styles.set(TextNode::SIZE, TextSize(Length::pt(10.0).into()));
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user