diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-05-26 13:49:44 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-05-26 13:49:44 +0200 |
| commit | a9869c212f7c1bc77a52e301ad014641b014e834 (patch) | |
| tree | 97b5d6c71940e903482ba2f30cbcedd8f1c17ea3 /src/model | |
| parent | 66d8f4569a9f13270c5f477e0730f127a22333e2 (diff) | |
Locatable groups
Diffstat (limited to 'src/model')
| -rw-r--r-- | src/model/content.rs | 2 | ||||
| -rw-r--r-- | src/model/locate.rs | 224 |
2 files changed, 173 insertions, 53 deletions
diff --git a/src/model/content.rs b/src/model/content.rs index effe84ae..dad212c8 100644 --- a/src/model/content.rs +++ b/src/model/content.rs @@ -44,6 +44,8 @@ pub fn layout(ctx: &mut Context, content: &Content) -> TypResult<Vec<Arc<Frame>> } } + // println!("Took {pass} passes"); + Ok(frames) } diff --git a/src/model/locate.rs b/src/model/locate.rs index 9b0d13e7..fd48e5ad 100644 --- a/src/model/locate.rs +++ b/src/model/locate.rs @@ -1,51 +1,140 @@ +use std::fmt::{self, Debug, Formatter}; use std::sync::Arc; use super::Content; use crate::diag::TypResult; -use crate::eval::{Args, Func, Value}; +use crate::eval::{Args, Dict, Func, Value}; use crate::frame::{Element, Frame}; use crate::geom::{Point, Transform}; use crate::syntax::Spanned; +use crate::util::EcoString; use crate::Context; +/// A group of locatable elements. +#[derive(Clone, Eq, PartialEq, Hash)] +pub struct Group(EcoString); + +impl Group { + /// Create a group of elements that is identified by a string key. + pub fn new(key: EcoString) -> Self { + Self(key) + } + + /// Add an entry to the group. + pub fn entry(&self, recipe: Spanned<Func>) -> LocateNode { + LocateNode { recipe, group: Some(self.clone()) } + } +} + +impl Debug for Group { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "group({:?})", self.0) + } +} + /// A node that can realize itself with its own location. #[derive(Debug, Clone, PartialEq, Hash)] -pub struct LocateNode(Spanned<Func>); +pub struct LocateNode { + recipe: Spanned<Func>, + group: Option<Group>, +} impl LocateNode { /// Create a new locate node. pub fn new(recipe: Spanned<Func>) -> Self { - Self(recipe) + Self { recipe, group: None } } /// Realize the node. pub fn realize(&self, ctx: &mut Context) -> TypResult<Content> { let idx = ctx.pins.cursor(); - let location = ctx.pins.next(); - let dict = dict! { - "page" => Value::Int(location.page as i64), - "x" => Value::Length(location.pos.x.into()), - "y" => Value::Length(location.pos.y.into()), - }; + let pin = ctx.pins.next(self.group.clone()); + + // Determine the index among the peers. + let index = self.group.as_ref().map(|_| { + ctx.pins + .iter() + .filter(|other| { + other.group == self.group && other.loc.flow < pin.loc.flow + }) + .count() + }); + + let dict = pin.encode(index); + let mut args = Args::new(self.recipe.span, [Value::Dict(dict)]); + + // Collect all members if requested. + if self.group.is_some() && self.recipe.v.argc() == Some(2) { + let mut all: Vec<_> = + ctx.pins.iter().filter(|other| other.group == self.group).collect(); + + all.sort_by_key(|pin| pin.loc.flow); + + let array = all + .iter() + .enumerate() + .map(|(index, member)| Value::Dict(member.encode(Some(index)))) + .collect(); + + args.push(self.recipe.span, Value::Array(array)) + } - let args = Args::new(self.0.span, [Value::Dict(dict)]); - Ok(Content::Pin(idx) + self.0.v.call_detached(ctx, args)?.display()) + Ok(Content::Pin(idx) + self.recipe.v.call_detached(ctx, args)?.display()) } } -/// Manages ordered pins. -#[derive(Debug, Clone, PartialEq, Hash)] +/// Manages pins. +#[derive(Debug, Clone, Hash)] pub struct PinBoard { - /// All currently pinned locations. - pins: Vec<Location>, + /// All currently active pins. + pins: Vec<Pin>, /// The index of the next pin in order. cursor: usize, + /// If larger than zero, the board is frozen. + frozen: usize, +} + +/// A document pin. +#[derive(Debug, Default, Clone, PartialEq, Hash)] +pub struct Pin { + /// The physical location of the pin in the document. + loc: Location, + /// The group the pin belongs to, if any. + group: Option<Group>, +} + +impl Pin { + /// Encode into a user-facing dictionary. + fn encode(&self, index: Option<usize>) -> Dict { + let mut dict = dict! { + "page" => Value::Int(self.loc.page as i64), + "x" => Value::Length(self.loc.pos.x.into()), + "y" => Value::Length(self.loc.pos.y.into()), + }; + + if let Some(index) = index { + dict.insert("index".into(), Value::Int(index as i64)); + } + + dict + } +} + +/// A physical location in a document. +#[derive(Debug, Default, Copy, Clone, PartialEq, Hash)] +pub struct Location { + /// The page, starting at 1. + pub page: usize, + /// The exact coordinates on the page (from the top left, as usual). + pub pos: Point, + /// The flow index. + pub flow: usize, } impl PinBoard { /// Create an empty pin board. pub fn new() -> Self { - Self { pins: vec![], cursor: 0 } + Self { pins: vec![], cursor: 0, frozen: 0 } } /// The number of pins on the board. @@ -53,16 +142,31 @@ impl PinBoard { self.pins.len() } - /// How many pins are resolved in comparison to an earlier snapshot. - pub fn resolved(&self, prev: &Self) -> usize { - self.pins.iter().zip(&prev.pins).filter(|(a, b)| a == b).count() + /// Iterate over all pins on the board. + pub fn iter(&self) -> std::slice::Iter<Pin> { + self.pins.iter() + } + + /// Freeze the board to prevent modifications. + pub fn freeze(&mut self) { + self.frozen += 1; + } + + /// Freeze the board to prevent modifications. + pub fn unfreeze(&mut self) { + self.frozen -= 1; } - /// Access the next pin location. - pub fn next(&mut self) -> Location { + /// Access the next pin. + pub fn next(&mut self, group: Option<Group>) -> Pin { + if self.frozen > 0 { + return Pin::default(); + } + let cursor = self.cursor; self.jump(self.cursor + 1); - self.pins[cursor] + self.pins[cursor].group = group; + self.pins[cursor].clone() } /// The current cursor. @@ -72,11 +176,14 @@ impl PinBoard { /// Set the current cursor. pub fn jump(&mut self, cursor: usize) { - if cursor >= self.pins.len() { - let loc = self.pins.last().copied().unwrap_or_default(); - self.pins.resize(cursor + 1, loc); + if self.frozen > 0 { + return; } + self.cursor = cursor; + if cursor >= self.pins.len() { + self.pins.resize(cursor, Pin::default()); + } } /// Reset the cursor and remove all unused pins. @@ -87,39 +194,50 @@ impl PinBoard { /// Locate all pins in the frames. pub fn locate(&mut self, frames: &[Arc<Frame>]) { + let mut flow = 0; for (i, frame) in frames.iter().enumerate() { - self.locate_impl(1 + i, frame, Transform::identity()); + locate_impl( + &mut self.pins, + &mut flow, + 1 + i, + frame, + Transform::identity(), + ); } } - /// Locate all pins in a frame. - fn locate_impl(&mut self, page: usize, frame: &Frame, ts: Transform) { - for &(pos, ref element) in &frame.elements { - match element { - Element::Group(group) => { - let ts = ts - .pre_concat(Transform::translate(pos.x, pos.y)) - .pre_concat(group.transform); - self.locate_impl(page, &group.frame, ts); - } - - Element::Pin(idx) => { - let pin = &mut self.pins[*idx]; - pin.page = page; - pin.pos = pos.transform(ts); - } - - _ => {} - } - } + /// How many pins are resolved in comparison to an earlier snapshot. + pub fn resolved(&self, prev: &Self) -> usize { + self.pins.iter().zip(&prev.pins).filter(|(a, b)| a == b).count() } } -/// A physical location in a document. -#[derive(Debug, Default, Copy, Clone, PartialEq, Hash)] -pub struct Location { - /// The page, starting at 1. - pub page: usize, - /// The exact coordinates on the page (from the top left, as usual). - pub pos: Point, +/// Locate all pins in a frame. +fn locate_impl( + pins: &mut [Pin], + flow: &mut usize, + page: usize, + frame: &Frame, + ts: Transform, +) { + for &(pos, ref element) in &frame.elements { + match element { + Element::Group(group) => { + let ts = ts + .pre_concat(Transform::translate(pos.x, pos.y)) + .pre_concat(group.transform); + locate_impl(pins, flow, page, &group.frame, ts); + } + + Element::Pin(idx) => { + let loc = &mut pins[*idx].loc; + loc.page = page; + loc.pos = pos.transform(ts); + loc.flow = *flow; + *flow += 1; + } + + _ => {} + } + } } |
