mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Progressing stack layouter 🚊
This commit is contained in:
parent
7e98022435
commit
92586d3e68
@ -14,7 +14,7 @@ pub mod prelude {
|
|||||||
pub use crate::func::{Scope, ParseFunc, LayoutFunc, Command, Commands};
|
pub use crate::func::{Scope, ParseFunc, LayoutFunc, Command, Commands};
|
||||||
pub use crate::layout::{
|
pub use crate::layout::{
|
||||||
layout_tree, Layout, MultiLayout,
|
layout_tree, Layout, MultiLayout,
|
||||||
LayoutContext, LayoutSpace, LayoutSpaces,
|
LayoutContext, LayoutSpace, LayoutSpaces, LayoutExpansion,
|
||||||
LayoutAxes, Axis, GenericAxisKind, SpecificAxisKind,
|
LayoutAxes, Axis, GenericAxisKind, SpecificAxisKind,
|
||||||
LayoutAlignment, Alignment,
|
LayoutAlignment, Alignment,
|
||||||
SpacingKind, LayoutResult,
|
SpacingKind, LayoutResult,
|
||||||
|
@ -68,8 +68,8 @@ impl LayoutActions {
|
|||||||
pub fn new() -> LayoutActions {
|
pub fn new() -> LayoutActions {
|
||||||
LayoutActions {
|
LayoutActions {
|
||||||
actions: vec![],
|
actions: vec![],
|
||||||
origin: Size2D::zero(),
|
origin: Size2D::ZERO,
|
||||||
active_font: (std::usize::MAX, Size::zero()),
|
active_font: (std::usize::MAX, Size::ZERO),
|
||||||
next_pos: None,
|
next_pos: None,
|
||||||
next_font: None,
|
next_font: None,
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ impl FlexLine {
|
|||||||
FlexLine {
|
FlexLine {
|
||||||
usable,
|
usable,
|
||||||
actions: LayoutActions::new(),
|
actions: LayoutActions::new(),
|
||||||
combined_dimensions: Size2D::zero(),
|
combined_dimensions: Size2D::ZERO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -51,7 +51,7 @@ impl PartialLine {
|
|||||||
PartialLine {
|
PartialLine {
|
||||||
usable,
|
usable,
|
||||||
content: vec![],
|
content: vec![],
|
||||||
dimensions: Size2D::zero(),
|
dimensions: Size2D::ZERO,
|
||||||
space: LastSpacing::Hard,
|
space: LastSpacing::Hard,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
//! The core layouting engine.
|
//! The core layouting engine.
|
||||||
|
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use toddle::query::{FontClass, SharedFontLoader};
|
use toddle::query::{FontClass, SharedFontLoader};
|
||||||
@ -91,7 +90,7 @@ pub struct LayoutSpace {
|
|||||||
/// Whether to expand the dimensions of the resulting layout to the full
|
/// Whether to expand the dimensions of the resulting layout to the full
|
||||||
/// dimensions of this space or to shrink them to fit the content for the
|
/// dimensions of this space or to shrink them to fit the content for the
|
||||||
/// horizontal and vertical axis.
|
/// horizontal and vertical axis.
|
||||||
pub expand: (bool, bool),
|
pub expand: LayoutExpansion,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayoutSpace {
|
impl LayoutSpace {
|
||||||
@ -110,12 +109,25 @@ impl LayoutSpace {
|
|||||||
pub fn usable_space(&self) -> LayoutSpace {
|
pub fn usable_space(&self) -> LayoutSpace {
|
||||||
LayoutSpace {
|
LayoutSpace {
|
||||||
dimensions: self.usable(),
|
dimensions: self.usable(),
|
||||||
padding: SizeBox::zero(),
|
padding: SizeBox::ZERO,
|
||||||
expand: (false, false),
|
expand: LayoutExpansion::new(false, false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether to fit to content or expand to the space's size.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub struct LayoutExpansion {
|
||||||
|
pub horizontal: bool,
|
||||||
|
pub vertical: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LayoutExpansion {
|
||||||
|
pub fn new(horizontal: bool, vertical: bool) -> LayoutExpansion {
|
||||||
|
LayoutExpansion { horizontal, vertical }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The axes along which the content is laid out.
|
/// The axes along which the content is laid out.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct LayoutAxes {
|
pub struct LayoutAxes {
|
||||||
@ -132,28 +144,8 @@ impl LayoutAxes {
|
|||||||
LayoutAxes { primary, secondary }
|
LayoutAxes { primary, secondary }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the generalized version of a `Size2D` dependent on
|
|
||||||
/// the layouting axes, that is:
|
|
||||||
/// - The x coordinate describes the primary axis instead of the horizontal one.
|
|
||||||
/// - The y coordinate describes the secondary axis instead of the vertical one.
|
|
||||||
pub fn generalize(&self, size: Size2D) -> Size2D {
|
|
||||||
if self.primary.is_horizontal() {
|
|
||||||
size
|
|
||||||
} else {
|
|
||||||
Size2D { x: size.y, y: size.x }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the specialized version of this generalized Size2D.
|
|
||||||
/// (Inverse to `generalized`).
|
|
||||||
pub fn specialize(&self, size: Size2D) -> Size2D {
|
|
||||||
// In fact, generalized is its own inverse. For reasons of clarity
|
|
||||||
// at the call site, we still have this second function.
|
|
||||||
self.generalize(size)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the specified generic axis.
|
/// Return the specified generic axis.
|
||||||
pub fn get_generic(&self, axis: GenericAxisKind) -> Axis {
|
pub fn generic(&self, axis: GenericAxisKind) -> Axis {
|
||||||
match axis {
|
match axis {
|
||||||
GenericAxisKind::Primary => self.primary,
|
GenericAxisKind::Primary => self.primary,
|
||||||
GenericAxisKind::Secondary => self.secondary,
|
GenericAxisKind::Secondary => self.secondary,
|
||||||
@ -161,8 +153,8 @@ impl LayoutAxes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the specified specific axis.
|
/// Return the specified specific axis.
|
||||||
pub fn get_specific(&self, axis: SpecificAxisKind) -> Axis {
|
pub fn specific(&self, axis: SpecificAxisKind) -> Axis {
|
||||||
self.get_generic(axis.generic(*self))
|
self.generic(axis.generic(*self))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the generic axis kind which is the horizontal axis.
|
/// Returns the generic axis kind which is the horizontal axis.
|
||||||
@ -222,6 +214,15 @@ impl LayoutAxes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for LayoutAxes {
|
||||||
|
fn default() -> LayoutAxes {
|
||||||
|
LayoutAxes {
|
||||||
|
primary: Axis::LeftToRight,
|
||||||
|
secondary: Axis::TopToBottom,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Directions along which content is laid out.
|
/// Directions along which content is laid out.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub enum Axis {
|
pub enum Axis {
|
||||||
@ -318,7 +319,7 @@ impl SpecificAxisKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The place to put a layout in a container.
|
/// The place to put a layout in a container.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct LayoutAlignment {
|
pub struct LayoutAlignment {
|
||||||
pub primary: Alignment,
|
pub primary: Alignment,
|
||||||
pub secondary: Alignment,
|
pub secondary: Alignment,
|
||||||
@ -331,7 +332,7 @@ impl LayoutAlignment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Where to align content.
|
/// Where to align content.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
pub enum Alignment {
|
pub enum Alignment {
|
||||||
Origin,
|
Origin,
|
||||||
Center,
|
Center,
|
||||||
@ -349,6 +350,12 @@ impl Alignment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Alignment {
|
||||||
|
fn default() -> Alignment {
|
||||||
|
Alignment::Origin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Whitespace between boxes with different interaction properties.
|
/// Whitespace between boxes with different interaction properties.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub enum SpacingKind {
|
pub enum SpacingKind {
|
||||||
@ -377,7 +384,7 @@ impl LastSpacing {
|
|||||||
fn soft_or_zero(&self) -> Size {
|
fn soft_or_zero(&self) -> Size {
|
||||||
match self {
|
match self {
|
||||||
LastSpacing::Soft(space, _) => *space,
|
LastSpacing::Soft(space, _) => *space,
|
||||||
_ => Size::zero(),
|
_ => Size::ZERO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use smallvec::smallvec;
|
use smallvec::smallvec;
|
||||||
|
use crate::size::{min, max};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// The stack layouter arranges boxes stacked onto each other.
|
/// The stack layouter arranges boxes stacked onto each other.
|
||||||
@ -13,9 +14,6 @@ pub struct StackLayouter {
|
|||||||
layouts: MultiLayout,
|
layouts: MultiLayout,
|
||||||
/// The currently active layout space.
|
/// The currently active layout space.
|
||||||
space: Space,
|
space: Space,
|
||||||
/// The remaining subspace of the active space. Whenever the layouting axes
|
|
||||||
/// change a new subspace is started.
|
|
||||||
sub: Subspace,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The context for stack layouting.
|
/// The context for stack layouting.
|
||||||
@ -37,40 +35,20 @@ struct Space {
|
|||||||
/// Whether to add the layout for this space even if it would be empty.
|
/// Whether to add the layout for this space even if it would be empty.
|
||||||
hard: bool,
|
hard: bool,
|
||||||
/// The so-far accumulated subspaces.
|
/// The so-far accumulated subspaces.
|
||||||
subs: Vec<Subspace>,
|
layouts: Vec<(LayoutAxes, Layout)>,
|
||||||
}
|
/// The specialized size of this subspace.
|
||||||
|
|
||||||
/// A part of a space with fixed axes and secondary alignment.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct Subspace {
|
|
||||||
/// The axes along which contents in this subspace are laid out.
|
|
||||||
axes: LayoutAxes,
|
|
||||||
/// The secondary alignment of this subspace.
|
|
||||||
alignment: Alignment,
|
|
||||||
/// The beginning of this subspace in the parent space (specialized).
|
|
||||||
origin: Size2D,
|
|
||||||
/// The total usable space of this subspace (generalized).
|
|
||||||
usable: Size2D,
|
|
||||||
/// The used size of this subspace (generalized), with
|
|
||||||
/// - `x` being the maximum of the primary size of all boxes.
|
|
||||||
/// - `y` being the total extent of all boxes and space in the secondary
|
|
||||||
/// direction.
|
|
||||||
size: Size2D,
|
size: Size2D,
|
||||||
/// The so-far accumulated layouts.
|
/// The specialized remaining space.
|
||||||
layouts: Vec<LayoutEntry>,
|
usable: Size2D,
|
||||||
|
/// The specialized extra-needed dimensions to affect the size at all.
|
||||||
|
extra: Size2D,
|
||||||
|
/// The maximal secondary alignment for both specialized axes (horizontal,
|
||||||
|
/// vertical).
|
||||||
|
alignment: (Alignment, Alignment),
|
||||||
/// The last added spacing if the last added thing was spacing.
|
/// The last added spacing if the last added thing was spacing.
|
||||||
last_spacing: LastSpacing,
|
last_spacing: LastSpacing,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single layout in a subspace.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct LayoutEntry {
|
|
||||||
/// The offset of this box on the secondary axis.
|
|
||||||
offset: Size,
|
|
||||||
/// The layout itself.
|
|
||||||
layout: Layout,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StackLayouter {
|
impl StackLayouter {
|
||||||
/// Create a new stack layouter.
|
/// Create a new stack layouter.
|
||||||
pub fn new(ctx: StackContext) -> StackLayouter {
|
pub fn new(ctx: StackContext) -> StackLayouter {
|
||||||
@ -80,55 +58,55 @@ impl StackLayouter {
|
|||||||
StackLayouter {
|
StackLayouter {
|
||||||
ctx,
|
ctx,
|
||||||
layouts: MultiLayout::new(),
|
layouts: MultiLayout::new(),
|
||||||
space: Space::new(0, true),
|
space: Space::new(0, true, space.usable()),
|
||||||
sub: Subspace::new(axes, Alignment::Origin, space.start(), space.usable()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a layout to the stack.
|
/// Add a layout to the stack.
|
||||||
pub fn add(&mut self, layout: Layout) -> LayoutResult<()> {
|
pub fn add(&mut self, layout: Layout) -> LayoutResult<()> {
|
||||||
if layout.alignment.secondary != self.sub.alignment {
|
// If the layout's secondary alignment is less than what we have already
|
||||||
self.finish_subspace(layout.alignment.secondary);
|
// seen, it needs to go into the next space.
|
||||||
|
if layout.alignment.secondary < *self.secondary_alignment() {
|
||||||
|
self.finish_space(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We want the new maximal alignment and since the layout's secondary
|
||||||
|
// alignment is at least the previous maximum, we just take it.
|
||||||
|
*self.secondary_alignment() = layout.alignment.secondary;
|
||||||
|
|
||||||
// Add a cached soft space if there is one.
|
// Add a cached soft space if there is one.
|
||||||
if let LastSpacing::Soft(space, _) = self.sub.last_spacing {
|
if let LastSpacing::Soft(spacing, _) = self.space.last_spacing {
|
||||||
self.add_spacing(space, SpacingKind::Hard);
|
self.add_spacing(spacing, SpacingKind::Hard);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The new primary size is the maximum of the current one and the
|
// Find the first space that fits the layout.
|
||||||
// layout's one while the secondary size grows by the layout's size.
|
while !self.space.usable.fits(layout.dimensions) {
|
||||||
let size = self.ctx.axes.generalize(layout.dimensions);
|
|
||||||
let mut new_size = Size2D {
|
|
||||||
x: crate::size::max(self.sub.size.x, size.x),
|
|
||||||
y: self.sub.size.y + size.y
|
|
||||||
};
|
|
||||||
|
|
||||||
// Find the first (sub-)space that fits the layout.
|
|
||||||
while !self.sub.usable.fits(new_size) {
|
|
||||||
if self.space_is_last() && self.space_is_empty() {
|
if self.space_is_last() && self.space_is_empty() {
|
||||||
error!("box of size {} does not fit into remaining stack of size {}",
|
error!("box of size {} does not fit into remaining usable size {}",
|
||||||
size, self.sub.usable - Size2D::with_y(self.sub.size.y));
|
layout.dimensions, self.space.usable);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.finish_space(true);
|
self.finish_space(true);
|
||||||
new_size = size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The secondary offset from the start of layouts is given by the
|
let axes = self.ctx.axes;
|
||||||
// current primary size of the subspace.
|
let dimensions = layout.dimensions.generalized(axes);
|
||||||
let offset = self.sub.size.y;
|
|
||||||
self.sub.layouts.push(LayoutEntry {
|
|
||||||
offset,
|
|
||||||
layout,
|
|
||||||
});
|
|
||||||
|
|
||||||
// The new size of the subspace is the previously calculated
|
let mut size = self.space.size.generalized(axes);
|
||||||
// combination.
|
let mut extra = self.space.extra.generalized(axes);
|
||||||
self.sub.size = new_size;
|
|
||||||
|
|
||||||
// Since the last item was a box, last spacing is reset to `None`.
|
size.x += max(dimensions.x - extra.x, Size::ZERO);
|
||||||
self.sub.last_spacing = LastSpacing::None;
|
size.y += max(dimensions.y - extra.y, Size::ZERO);
|
||||||
|
extra.x = max(extra.x, dimensions.x);
|
||||||
|
extra.y = max(extra.y - dimensions.y, Size::ZERO);
|
||||||
|
|
||||||
|
self.space.size = size.specialized(axes);
|
||||||
|
self.space.extra = extra.specialized(axes);
|
||||||
|
|
||||||
|
*self.space.usable.secondary_mut(axes) -= dimensions.y;
|
||||||
|
|
||||||
|
self.space.layouts.push((self.ctx.axes, layout));
|
||||||
|
self.space.last_spacing = LastSpacing::None;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -144,30 +122,34 @@ impl StackLayouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add secondary spacing to the stack.
|
/// Add secondary spacing to the stack.
|
||||||
pub fn add_spacing(&mut self, space: Size, kind: SpacingKind) {
|
pub fn add_spacing(&mut self, mut spacing: Size, kind: SpacingKind) {
|
||||||
match kind {
|
match kind {
|
||||||
// A hard space is directly added to the sub's size.
|
// A hard space is directly added to the sub's size.
|
||||||
SpacingKind::Hard => {
|
SpacingKind::Hard => {
|
||||||
if self.sub.size.y + space > self.sub.usable.y {
|
// Reduce the spacing such that definitely fits.
|
||||||
self.sub.size.y = self.sub.usable.y;
|
spacing.min_eq(self.space.usable.secondary(self.ctx.axes));
|
||||||
} else {
|
|
||||||
self.sub.size.y += space;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.sub.last_spacing = LastSpacing::Hard;
|
self.add(Layout {
|
||||||
|
dimensions: Size2D::with_y(spacing).specialized(self.ctx.axes),
|
||||||
|
baseline: None,
|
||||||
|
alignment: LayoutAlignment::default(),
|
||||||
|
actions: vec![],
|
||||||
|
}).expect("spacing should fit");
|
||||||
|
|
||||||
|
self.space.last_spacing = LastSpacing::Hard;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A hard space is cached if it is not consumed by a hard space or
|
// A hard space is cached if it is not consumed by a hard space or
|
||||||
// previous soft space with higher level.
|
// previous soft space with higher level.
|
||||||
SpacingKind::Soft(level) => {
|
SpacingKind::Soft(level) => {
|
||||||
let consumes = match self.sub.last_spacing {
|
let consumes = match self.space.last_spacing {
|
||||||
LastSpacing::None => true,
|
LastSpacing::None => true,
|
||||||
LastSpacing::Soft(_, prev) if level < prev => true,
|
LastSpacing::Soft(_, prev) if level < prev => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if consumes {
|
if consumes {
|
||||||
self.sub.last_spacing = LastSpacing::Soft(space, level);
|
self.space.last_spacing = LastSpacing::Soft(spacing, level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,13 +160,7 @@ impl StackLayouter {
|
|||||||
/// This starts a new subspace (if the axes are actually different from the
|
/// This starts a new subspace (if the axes are actually different from the
|
||||||
/// current ones).
|
/// current ones).
|
||||||
pub fn set_axes(&mut self, axes: LayoutAxes) {
|
pub fn set_axes(&mut self, axes: LayoutAxes) {
|
||||||
if axes != self.ctx.axes {
|
self.ctx.axes = axes;
|
||||||
self.finish_subspace(Alignment::Origin);
|
|
||||||
|
|
||||||
let (origin, usable) = self.remaining_subspace();
|
|
||||||
self.sub = Subspace::new(axes, Alignment::Origin, origin, usable);
|
|
||||||
self.ctx.axes = axes;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change the layouting spaces to use.
|
/// Change the layouting spaces to use.
|
||||||
@ -206,9 +182,9 @@ impl StackLayouter {
|
|||||||
/// out into these spaces, it will fit into this stack.
|
/// out into these spaces, it will fit into this stack.
|
||||||
pub fn remaining(&self) -> LayoutSpaces {
|
pub fn remaining(&self) -> LayoutSpaces {
|
||||||
let mut spaces = smallvec![LayoutSpace {
|
let mut spaces = smallvec![LayoutSpace {
|
||||||
dimensions: self.remaining_subspace().1,
|
dimensions: self.space.usable,
|
||||||
padding: SizeBox::zero(),
|
padding: SizeBox::ZERO,
|
||||||
expand: (false, false),
|
expand: LayoutExpansion::new(false, false),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
for space in &self.ctx.spaces[self.next_space()..] {
|
for space in &self.ctx.spaces[self.next_space()..] {
|
||||||
@ -220,12 +196,12 @@ impl StackLayouter {
|
|||||||
|
|
||||||
/// The usable size along the primary axis.
|
/// The usable size along the primary axis.
|
||||||
pub fn primary_usable(&self) -> Size {
|
pub fn primary_usable(&self) -> Size {
|
||||||
self.sub.usable.x
|
self.space.usable.primary(self.ctx.axes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the current layout space (not subspace) is empty.
|
/// Whether the current layout space (not subspace) is empty.
|
||||||
pub fn space_is_empty(&self) -> bool {
|
pub fn space_is_empty(&self) -> bool {
|
||||||
self.subspace_is_empty() && self.space.subs.is_empty()
|
self.space.size == Size2D::ZERO && self.space.layouts.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the current layout space is the last is the followup list.
|
/// Whether the current layout space is the last is the followup list.
|
||||||
@ -243,117 +219,36 @@ impl StackLayouter {
|
|||||||
|
|
||||||
/// Finish the current space and start a new one.
|
/// Finish the current space and start a new one.
|
||||||
pub fn finish_space(&mut self, hard: bool) {
|
pub fn finish_space(&mut self, hard: bool) {
|
||||||
self.finish_subspace(Alignment::Origin);
|
|
||||||
|
|
||||||
println!();
|
|
||||||
println!("FINISHING SPACE:");
|
|
||||||
println!();
|
|
||||||
|
|
||||||
let space = self.ctx.spaces[self.space.index];
|
let space = self.ctx.spaces[self.space.index];
|
||||||
let mut subs = std::mem::replace(&mut self.space.subs, vec![]);
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// Compute the size of the whole space.
|
|
||||||
let usable = space.usable();
|
let usable = space.usable();
|
||||||
let mut max = Size2D {
|
if space.expand.horizontal { self.space.size.x = usable.x; }
|
||||||
x: if space.expand.0 { usable.x } else { Size::zero() },
|
if space.expand.vertical { self.space.size.y = usable.y; }
|
||||||
y: if space.expand.1 { usable.y } else { Size::zero() },
|
|
||||||
};
|
|
||||||
|
|
||||||
// The total size is determined by the maximum position + extent of one
|
let dimensions = self.space.size.padded(space.padding);
|
||||||
// of the boxes.
|
|
||||||
for sub in &subs {
|
|
||||||
max.max_eq(sub.origin + sub.axes.specialize(sub.size));
|
|
||||||
}
|
|
||||||
|
|
||||||
let dimensions = max.padded(space.padding);
|
|
||||||
|
|
||||||
println!("WITH DIMENSIONS: {}", dimensions);
|
|
||||||
|
|
||||||
println!("SUBS: {:#?}", subs);
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// Justify the boxes according to their alignment and give each box
|
|
||||||
// the appropriate origin and usable space.
|
|
||||||
|
|
||||||
// use Alignment::*;
|
|
||||||
|
|
||||||
for sub in &mut subs {
|
|
||||||
// The usable width should not exceed the total usable width
|
|
||||||
// (previous value) or the maximum width of the layout as a whole.
|
|
||||||
sub.usable.x = crate::size::min(
|
|
||||||
sub.usable.x,
|
|
||||||
sub.axes.specialize(max - sub.origin).x,
|
|
||||||
);
|
|
||||||
|
|
||||||
sub.usable.y = sub.size.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if space.expand.1 {
|
|
||||||
// let height = subs.iter().map(|sub| sub.size.y).sum();
|
|
||||||
// let centers = subs.iter()
|
|
||||||
// .filter(|sub| sub.alignment == Alignment::Center)
|
|
||||||
// .count()
|
|
||||||
// .max(1);
|
|
||||||
|
|
||||||
// let grow = max.y - height;
|
|
||||||
// let center_grow = grow / (centers as i32);
|
|
||||||
|
|
||||||
// println!("center grow = {}", center_grow);
|
|
||||||
|
|
||||||
// let mut offset = Size::zero();
|
|
||||||
// for sub in &mut subs {
|
|
||||||
// sub.origin.y += offset;
|
|
||||||
// if sub.alignment == Center {
|
|
||||||
// sub.usable.y += center_grow;
|
|
||||||
// offset += center_grow;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if let Some(last) = subs.last_mut() {
|
|
||||||
// last.usable.y += grow - offset;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// Do the thing
|
|
||||||
|
|
||||||
// Add a debug box with this boxes size.
|
|
||||||
let mut actions = LayoutActions::new();
|
let mut actions = LayoutActions::new();
|
||||||
actions.add(LayoutAction::DebugBox(dimensions));
|
actions.add(LayoutAction::DebugBox(dimensions));
|
||||||
|
|
||||||
for sub in subs {
|
let mut cursor = space.start();
|
||||||
let LayoutAxes { primary, secondary } = sub.axes;
|
for (axes, layout) in std::mem::replace(&mut self.space.layouts, vec![]) {
|
||||||
|
let LayoutAxes { primary, secondary } = axes;
|
||||||
|
let size = layout.dimensions.specialized(axes);
|
||||||
|
let alignment = layout.alignment.primary;
|
||||||
|
|
||||||
// The factor is +1 if the axis is positive and -1 otherwise.
|
let primary_usable = self.space.size.primary(axes) - cursor.primary(axes);
|
||||||
let factor = sub.axes.secondary.factor();
|
|
||||||
|
|
||||||
// The anchor is the position of the origin-most point of the
|
let position = Size2D {
|
||||||
// layout.
|
x: cursor.primary(axes)
|
||||||
let anchor =
|
+ primary_usable.anchor(alignment, primary.is_positive())
|
||||||
sub.usable.y.anchor(sub.alignment, secondary.is_positive())
|
- size.x.anchor(alignment, primary.is_positive()),
|
||||||
- factor * sub.size.y.anchor(sub.alignment, true);
|
y: cursor.secondary(axes),
|
||||||
|
};
|
||||||
|
|
||||||
for entry in sub.layouts {
|
actions.add_layout(position.specialized(axes), layout);
|
||||||
let layout = entry.layout;
|
*cursor.secondary_mut(axes) += size.y;
|
||||||
let alignment = layout.alignment.primary;
|
|
||||||
let size = sub.axes.generalize(layout.dimensions);
|
|
||||||
|
|
||||||
let x =
|
|
||||||
sub.usable.x.anchor(alignment, primary.is_positive())
|
|
||||||
- size.x.anchor(alignment, primary.is_positive());
|
|
||||||
|
|
||||||
let y = anchor
|
|
||||||
+ factor * entry.offset
|
|
||||||
- size.y.anchor(Alignment::Origin, secondary.is_positive());
|
|
||||||
|
|
||||||
let pos = sub.origin + sub.axes.specialize(Size2D::new(x, y));
|
|
||||||
actions.add_layout(pos, layout);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
|
|
||||||
self.layouts.push(Layout {
|
self.layouts.push(Layout {
|
||||||
dimensions,
|
dimensions,
|
||||||
baseline: None,
|
baseline: None,
|
||||||
@ -365,14 +260,9 @@ impl StackLayouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Start a new space with the given index.
|
/// Start a new space with the given index.
|
||||||
fn start_space(&mut self, space: usize, hard: bool) {
|
fn start_space(&mut self, index: usize, hard: bool) {
|
||||||
// Start the space.
|
let space = self.ctx.spaces[index];
|
||||||
self.space = Space::new(space, hard);
|
self.space = Space::new(index, hard, space.usable());
|
||||||
|
|
||||||
// Start the subspace.
|
|
||||||
let space = self.ctx.spaces[space];
|
|
||||||
let axes = self.ctx.axes;
|
|
||||||
self.sub = Subspace::new(axes, Alignment::Origin, space.start(), space.usable());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The index of the next space.
|
/// The index of the next space.
|
||||||
@ -380,62 +270,25 @@ impl StackLayouter {
|
|||||||
(self.space.index + 1).min(self.ctx.spaces.len() - 1)
|
(self.space.index + 1).min(self.ctx.spaces.len() - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finish the current subspace.
|
// Access the secondary alignment in the current system of axes.
|
||||||
fn finish_subspace(&mut self, new_alignment: Alignment) {
|
fn secondary_alignment(&mut self) -> &mut Alignment {
|
||||||
let empty = self.subspace_is_empty();
|
match self.ctx.axes.primary.is_horizontal() {
|
||||||
|
true => &mut self.space.alignment.1,
|
||||||
let axes = self.ctx.axes;
|
false => &mut self.space.alignment.0,
|
||||||
let (origin, usable) = self.remaining_subspace();
|
|
||||||
let new_sub = Subspace::new(axes, new_alignment, origin, usable);
|
|
||||||
let sub = std::mem::replace(&mut self.sub, new_sub);
|
|
||||||
|
|
||||||
if !empty {
|
|
||||||
self.space.subs.push(sub);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The remaining sub
|
|
||||||
fn remaining_subspace(&self) -> (Size2D, Size2D) {
|
|
||||||
let offset = self.sub.size.y + self.sub.last_spacing.soft_or_zero();
|
|
||||||
|
|
||||||
let new_origin = self.sub.origin + match self.ctx.axes.secondary.is_positive() {
|
|
||||||
true => self.ctx.axes.specialize(Size2D::with_y(offset)),
|
|
||||||
false => Size2D::zero(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let new_usable = self.ctx.axes.specialize(Size2D {
|
|
||||||
x: self.sub.usable.x,
|
|
||||||
y: self.sub.usable.y - offset,
|
|
||||||
});
|
|
||||||
|
|
||||||
(new_origin, new_usable)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether the current layout space (not subspace) is empty.
|
|
||||||
fn subspace_is_empty(&self) -> bool {
|
|
||||||
self.sub.layouts.is_empty() && self.sub.size == Size2D::zero()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Space {
|
impl Space {
|
||||||
fn new(index: usize, hard: bool) -> Space {
|
fn new(index: usize, hard: bool, usable: Size2D) -> Space {
|
||||||
Space {
|
Space {
|
||||||
index,
|
index,
|
||||||
hard,
|
hard,
|
||||||
subs: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Subspace {
|
|
||||||
fn new(axes: LayoutAxes, alignment: Alignment, origin: Size2D, usable: Size2D) -> Subspace {
|
|
||||||
Subspace {
|
|
||||||
axes,
|
|
||||||
alignment,
|
|
||||||
origin,
|
|
||||||
usable: axes.generalize(usable),
|
|
||||||
size: Size2D::zero(),
|
|
||||||
layouts: vec![],
|
layouts: vec![],
|
||||||
|
size: Size2D::ZERO,
|
||||||
|
usable,
|
||||||
|
extra: Size2D::ZERO,
|
||||||
|
alignment: (Alignment::Origin, Alignment::Origin),
|
||||||
last_spacing: LastSpacing::Hard,
|
last_spacing: LastSpacing::Hard,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ impl<'a, 'p> TextLayouter<'a, 'p> {
|
|||||||
actions: LayoutActions::new(),
|
actions: LayoutActions::new(),
|
||||||
buffer: String::new(),
|
buffer: String::new(),
|
||||||
active_font: std::usize::MAX,
|
active_font: std::usize::MAX,
|
||||||
width: Size::zero(),
|
width: Size::ZERO,
|
||||||
classes: ctx.style.classes.clone(),
|
classes: ctx.style.classes.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
|||||||
LayoutSpace {
|
LayoutSpace {
|
||||||
dimensions: style.dimensions,
|
dimensions: style.dimensions,
|
||||||
padding: style.margins,
|
padding: style.margins,
|
||||||
expand: (true, true),
|
expand: LayoutExpansion::new(true, true),
|
||||||
}
|
}
|
||||||
], true);
|
], true);
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ use toddle::Error as FontError;
|
|||||||
use crate::func::Scope;
|
use crate::func::Scope;
|
||||||
use crate::layout::{layout_tree, MultiLayout, LayoutContext};
|
use crate::layout::{layout_tree, MultiLayout, LayoutContext};
|
||||||
use crate::layout::{LayoutAxes, LayoutAlignment, Axis, Alignment};
|
use crate::layout::{LayoutAxes, LayoutAlignment, Axis, Alignment};
|
||||||
use crate::layout::{LayoutResult, LayoutSpace};
|
use crate::layout::{LayoutResult, LayoutSpace, LayoutExpansion};
|
||||||
use crate::syntax::{parse, SyntaxTree, ParseContext, Span, ParseResult};
|
use crate::syntax::{parse, SyntaxTree, ParseContext, Span, ParseResult};
|
||||||
use crate::style::{LayoutStyle, PageStyle, TextStyle};
|
use crate::style::{LayoutStyle, PageStyle, TextStyle};
|
||||||
|
|
||||||
@ -98,11 +98,11 @@ impl<'p> Typesetter<'p> {
|
|||||||
style: &self.style,
|
style: &self.style,
|
||||||
spaces: smallvec![LayoutSpace {
|
spaces: smallvec![LayoutSpace {
|
||||||
dimensions: self.style.page.dimensions,
|
dimensions: self.style.page.dimensions,
|
||||||
expand: (true, true),
|
expand: LayoutExpansion::new(true, true),
|
||||||
padding: self.style.page.margins,
|
padding: self.style.page.margins,
|
||||||
}],
|
}],
|
||||||
axes: LayoutAxes::new(Axis::LeftToRight, Axis::TopToBottom),
|
axes: LayoutAxes::default(),
|
||||||
alignment: LayoutAlignment::new(Alignment::Origin, Alignment::Origin),
|
alignment: LayoutAlignment::default(),
|
||||||
},
|
},
|
||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ function! {
|
|||||||
|
|
||||||
parse(args, body, ctx) {
|
parse(args, body, ctx) {
|
||||||
Boxed {
|
Boxed {
|
||||||
body: parse!(expected: body, ctx),
|
body: parse!(optional: body, ctx).unwrap_or(SyntaxTree::new()),
|
||||||
map: ExtentMap::new(&mut args, false)?,
|
map: ExtentMap::new(&mut args, false)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -22,8 +22,8 @@ function! {
|
|||||||
let space = &mut ctx.spaces[0];
|
let space = &mut ctx.spaces[0];
|
||||||
self.map.apply_with(ctx.axes, |axis, p| {
|
self.map.apply_with(ctx.axes, |axis, p| {
|
||||||
let entity = match axis {
|
let entity = match axis {
|
||||||
Horizontal => { space.expand.0 = true; &mut space.dimensions.x },
|
Horizontal => { space.expand.horizontal = true; &mut space.dimensions.x },
|
||||||
Vertical => { space.expand.1 = true; &mut space.dimensions.y },
|
Vertical => { space.expand.vertical = true; &mut space.dimensions.y },
|
||||||
};
|
};
|
||||||
|
|
||||||
*entity = p.concretize(*entity)
|
*entity = p.concretize(*entity)
|
||||||
|
@ -118,7 +118,7 @@ impl AlignmentKey {
|
|||||||
use AlignmentKey::*;
|
use AlignmentKey::*;
|
||||||
use SpecificAxisKind::*;
|
use SpecificAxisKind::*;
|
||||||
|
|
||||||
let positive = axes.get_specific(axis).is_positive();
|
let positive = axes.specific(axis).is_positive();
|
||||||
match (self, axis, positive) {
|
match (self, axis, positive) {
|
||||||
(Origin, Horizontal, true) | (End, Horizontal, false) => Left,
|
(Origin, Horizontal, true) | (End, Horizontal, false) => Left,
|
||||||
(End, Horizontal, true) | (Origin, Horizontal, false) => Right,
|
(End, Horizontal, true) | (Origin, Horizontal, false) => Right,
|
||||||
|
@ -86,8 +86,8 @@ impl<E: ExpressionKind + Copy> ExtentMap<E> {
|
|||||||
let key = match arg.v.key.v.0.as_str() {
|
let key = match arg.v.key.v.0.as_str() {
|
||||||
"width" | "w" => AxisKey::Horizontal,
|
"width" | "w" => AxisKey::Horizontal,
|
||||||
"height" | "h" => AxisKey::Vertical,
|
"height" | "h" => AxisKey::Vertical,
|
||||||
"primary-size" => AxisKey::Primary,
|
"primary-size" | "ps" => AxisKey::Primary,
|
||||||
"secondary-size" => AxisKey::Secondary,
|
"secondary-size" | "ss" => AxisKey::Secondary,
|
||||||
_ => if enforce {
|
_ => if enforce {
|
||||||
error!("expected dimension")
|
error!("expected dimension")
|
||||||
} else {
|
} else {
|
||||||
|
94
src/size.rs
94
src/size.rs
@ -6,7 +6,7 @@ use std::iter::Sum;
|
|||||||
use std::ops::*;
|
use std::ops::*;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::layout::Alignment;
|
use crate::layout::{LayoutAxes, Alignment};
|
||||||
|
|
||||||
/// A general space type.
|
/// A general space type.
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
@ -51,8 +51,11 @@ pub type FSize = ScaleSize;
|
|||||||
pub type PSize = ScaleSize;
|
pub type PSize = ScaleSize;
|
||||||
|
|
||||||
impl Size {
|
impl Size {
|
||||||
|
/// The zeroed size.
|
||||||
|
pub const ZERO: Size = Size { points: 0.0 };
|
||||||
|
|
||||||
/// Create a zeroed size.
|
/// Create a zeroed size.
|
||||||
pub fn zero() -> Size { Size { points: 0.0 } }
|
pub fn zero() -> Size { Size::ZERO }
|
||||||
|
|
||||||
/// Create a size from an amount of points.
|
/// Create a size from an amount of points.
|
||||||
pub fn pt(points: f32) -> Size { Size { points } }
|
pub fn pt(points: f32) -> Size { Size { points } }
|
||||||
@ -83,12 +86,17 @@ impl Size {
|
|||||||
*self = max(*self, other);
|
*self = max(*self, other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set this size to the minimum of itself and the other size.
|
||||||
|
pub fn min_eq(&mut self, other: Size) {
|
||||||
|
*self = min(*self, other);
|
||||||
|
}
|
||||||
|
|
||||||
/// The specialized anchor position for an item with the given alignment in a
|
/// The specialized anchor position for an item with the given alignment in a
|
||||||
/// container with a given size along the given axis.
|
/// container with a given size along the given axis.
|
||||||
pub fn anchor(&self, alignment: Alignment, positive: bool) -> Size {
|
pub fn anchor(&self, alignment: Alignment, positive: bool) -> Size {
|
||||||
use Alignment::*;
|
use Alignment::*;
|
||||||
match (positive, alignment) {
|
match (positive, alignment) {
|
||||||
(true, Origin) | (false, End) => Size::zero(),
|
(true, Origin) | (false, End) => Size::ZERO,
|
||||||
(_, Center) => *self / 2,
|
(_, Center) => *self / 2,
|
||||||
(true, End) | (false, Origin) => *self,
|
(true, End) | (false, Origin) => *self,
|
||||||
}
|
}
|
||||||
@ -97,6 +105,9 @@ impl Size {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Size2D {
|
impl Size2D {
|
||||||
|
/// The zeroed 2D-size.
|
||||||
|
pub const ZERO: Size2D = Size2D { x: Size::ZERO, y: Size::ZERO };
|
||||||
|
|
||||||
/// Create a new 2D-size from two sizes.
|
/// Create a new 2D-size from two sizes.
|
||||||
pub fn new(x: Size, y: Size) -> Size2D {
|
pub fn new(x: Size, y: Size) -> Size2D {
|
||||||
Size2D { x, y }
|
Size2D { x, y }
|
||||||
@ -104,7 +115,7 @@ impl Size2D {
|
|||||||
|
|
||||||
/// Create a 2D-size with both sizes set to zero.
|
/// Create a 2D-size with both sizes set to zero.
|
||||||
pub fn zero() -> Size2D {
|
pub fn zero() -> Size2D {
|
||||||
Size2D { x: Size::zero(), y: Size::zero() }
|
Size2D::ZERO
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a 2D-size with `x` and `y` set to the same value `s`.
|
/// Create a 2D-size with `x` and `y` set to the same value `s`.
|
||||||
@ -114,12 +125,63 @@ impl Size2D {
|
|||||||
|
|
||||||
/// Create a new 2D-size with `x` set to a value and `y` zero.
|
/// Create a new 2D-size with `x` set to a value and `y` zero.
|
||||||
pub fn with_x(x: Size) -> Size2D {
|
pub fn with_x(x: Size) -> Size2D {
|
||||||
Size2D { x, y: Size::zero() }
|
Size2D { x, y: Size::ZERO }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new 2D-size with `y` set to a value and `x` zero.
|
/// Create a new 2D-size with `y` set to a value and `x` zero.
|
||||||
pub fn with_y(y: Size) -> Size2D {
|
pub fn with_y(y: Size) -> Size2D {
|
||||||
Size2D { x: Size::zero(), y }
|
Size2D { x: Size::ZERO, y }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access the primary size of this 2D-size.
|
||||||
|
pub fn primary(&self, axes: LayoutAxes) -> Size {
|
||||||
|
match axes.primary.is_horizontal() {
|
||||||
|
true => self.x,
|
||||||
|
false => self.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access the secondary size of this 2D-size.
|
||||||
|
pub fn secondary(&self, axes: LayoutAxes) -> Size {
|
||||||
|
match axes.primary.is_horizontal() {
|
||||||
|
true => self.y,
|
||||||
|
false => self.x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access the primary size of this 2D-size.
|
||||||
|
pub fn primary_mut(&mut self, axes: LayoutAxes) -> &mut Size {
|
||||||
|
match axes.primary.is_horizontal() {
|
||||||
|
true => &mut self.x,
|
||||||
|
false => &mut self.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access the secondary size of this 2D-size.
|
||||||
|
pub fn secondary_mut(&mut self, axes: LayoutAxes) -> &mut Size {
|
||||||
|
match axes.primary.is_horizontal() {
|
||||||
|
true => &mut self.y,
|
||||||
|
false => &mut self.x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the generalized version of a `Size2D` dependent on
|
||||||
|
/// the layouting axes, that is:
|
||||||
|
/// - The x coordinate describes the primary axis instead of the horizontal one.
|
||||||
|
/// - The y coordinate describes the secondary axis instead of the vertical one.
|
||||||
|
pub fn generalized(&self, axes: LayoutAxes) -> Size2D {
|
||||||
|
match axes.primary.is_horizontal() {
|
||||||
|
true => *self,
|
||||||
|
false => Size2D { x: self.y, y: self.x },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the specialized version of this generalized Size2D.
|
||||||
|
/// (Inverse to `generalized`).
|
||||||
|
pub fn specialized(&self, axes: LayoutAxes) -> Size2D {
|
||||||
|
// In fact, generalized is its own inverse. For reasons of clarity
|
||||||
|
// at the call site, we still have this second function.
|
||||||
|
self.generalized(axes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a 2D-size padded by the paddings of the given box.
|
/// Return a 2D-size padded by the paddings of the given box.
|
||||||
@ -150,9 +212,24 @@ impl Size2D {
|
|||||||
self.x.max_eq(other.x);
|
self.x.max_eq(other.x);
|
||||||
self.y.max_eq(other.y);
|
self.y.max_eq(other.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set this size to the minimum of itself and the other size
|
||||||
|
/// (for both dimensions).
|
||||||
|
pub fn min_eq(&mut self, other: Size2D) {
|
||||||
|
self.x.min_eq(other.x);
|
||||||
|
self.y.min_eq(other.y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SizeBox {
|
impl SizeBox {
|
||||||
|
/// The zeroed size box.
|
||||||
|
pub const ZERO: SizeBox = SizeBox {
|
||||||
|
left: Size::ZERO,
|
||||||
|
top: Size::ZERO,
|
||||||
|
right: Size::ZERO,
|
||||||
|
bottom: Size::ZERO,
|
||||||
|
};
|
||||||
|
|
||||||
/// Create a new box from four sizes.
|
/// Create a new box from four sizes.
|
||||||
pub fn new(left: Size, top: Size, right: Size, bottom: Size) -> SizeBox {
|
pub fn new(left: Size, top: Size, right: Size, bottom: Size) -> SizeBox {
|
||||||
SizeBox {
|
SizeBox {
|
||||||
@ -165,8 +242,7 @@ impl SizeBox {
|
|||||||
|
|
||||||
/// Create a box with all values set to zero.
|
/// Create a box with all values set to zero.
|
||||||
pub fn zero() -> SizeBox {
|
pub fn zero() -> SizeBox {
|
||||||
let zero = Size::zero();
|
SizeBox::ZERO
|
||||||
SizeBox::new(zero, zero, zero, zero)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a box with all four fields set to the same value `s`.
|
/// Create a box with all four fields set to the same value `s`.
|
||||||
@ -269,7 +345,7 @@ impl Neg for Size {
|
|||||||
impl Sum for Size {
|
impl Sum for Size {
|
||||||
fn sum<I>(iter: I) -> Size
|
fn sum<I>(iter: I) -> Size
|
||||||
where I: Iterator<Item = Size> {
|
where I: Iterator<Item = Size> {
|
||||||
iter.fold(Size::zero(), Add::add)
|
iter.fold(Size::ZERO, Add::add)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,31 +1,21 @@
|
|||||||
[page.size: w=5cm, h=5cm]
|
[page.size: w=5cm, h=5cm]
|
||||||
[page.margins: 0cm]
|
[page.margins: 0cm]
|
||||||
|
|
||||||
// [box: w=4cm, h=3cm][1]
|
// Test 1
|
||||||
// //
|
// [box][
|
||||||
// [direction: ttb, ltr]
|
// [align: center]
|
||||||
// [box: w=2cm, h=1][2]
|
// [box: ps=3cm, ss=1cm]
|
||||||
// //
|
// [direction: ttb, ltr]
|
||||||
// [direction: btt, rtl]
|
// [box: ps=3cm, ss=1cm]
|
||||||
// [align: bottom, right]
|
// [box: ps=1cm, ss=1cm]
|
||||||
// [box: w=3cm, h=1][3]
|
// [box: ps=2cm, ss=1cm]
|
||||||
// //
|
// [box: ps=1cm, ss=1cm]
|
||||||
// [direction: ltr, ttb]
|
// ]
|
||||||
// [align: center, center]
|
|
||||||
// [box: w=2cm, h=2cm][4]
|
|
||||||
|
|
||||||
[align: center]
|
// Test 2
|
||||||
|
[align: secondary=top] Top
|
||||||
//[direction: primary=btt, secondary=rtl]
|
[align: secondary=center] Center
|
||||||
//[align: primary=bottom, secondary=right]
|
[align: secondary=bottom] Bottom
|
||||||
//[box][Hi]
|
[direction: ttb, ltr]
|
||||||
|
[align: primary=bottom]
|
||||||
[box][
|
[box: w=1cm, h=1cm]
|
||||||
//[align: primary=center, secondary=bottom]
|
|
||||||
[direction: secondary=btt]
|
|
||||||
Blabla
|
|
||||||
[v: 0.5cm]
|
|
||||||
[align: vertical=end] Origin 2]
|
|
||||||
//[align: vertical=center] Center
|
|
||||||
//[align: vertical=center] Center
|
|
||||||
//[align: vertical=end] End End End
|
|
||||||
|
@ -105,10 +105,10 @@ class BoxRenderer:
|
|||||||
|
|
||||||
img = Image.new('RGBA', self.size, (255, 255, 255, 255))
|
img = Image.new('RGBA', self.size, (255, 255, 255, 255))
|
||||||
pixels = numpy.array(img)
|
pixels = numpy.array(img)
|
||||||
for i in range(0, int(height)):
|
# for i in range(0, int(height)):
|
||||||
for j in range(0, int(width)):
|
# for j in range(0, int(width)):
|
||||||
if ((i // 2) % 2 == 0) == ((j // 2) % 2 == 0):
|
# if ((i // 2) % 2 == 0) == ((j // 2) % 2 == 0):
|
||||||
pixels[4*i:4*(i+1), 4*j:4*(j+1)] = (225, 225, 225, 255)
|
# pixels[4*i:4*(i+1), 4*j:4*(j+1)] = (225, 225, 225, 255)
|
||||||
|
|
||||||
self.img = Image.fromarray(pixels, 'RGBA')
|
self.img = Image.fromarray(pixels, 'RGBA')
|
||||||
self.draw = ImageDraw.Draw(self.img)
|
self.draw = ImageDraw.Draw(self.img)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user