summaryrefslogtreecommitdiff
path: root/src/model
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-05-26 13:49:44 +0200
committerLaurenz <laurmaedje@gmail.com>2022-05-26 13:49:44 +0200
commita9869c212f7c1bc77a52e301ad014641b014e834 (patch)
tree97b5d6c71940e903482ba2f30cbcedd8f1c17ea3 /src/model
parent66d8f4569a9f13270c5f477e0730f127a22333e2 (diff)
Locatable groups
Diffstat (limited to 'src/model')
-rw-r--r--src/model/content.rs2
-rw-r--r--src/model/locate.rs224
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;
+ }
+
+ _ => {}
+ }
+ }
}