mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-26 11:46:49 -06:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e919382ac7 | ||
|
|
a6319904d1 | ||
|
|
66ed962ad1 | ||
|
|
42bbf6baa2 | ||
|
|
52cf825395 | ||
|
|
bc881f6c4f |
@@ -927,6 +927,18 @@ impl Host for PassHost<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pops one finished component off Tarjan's stack, labelling its nodes with `comp_index`. The
|
||||||
|
/// component is everything up to and including `root`.
|
||||||
|
fn pop_component(stack: &mut Vec<usize>, on_stack: &mut [bool], comp: &mut [usize], comp_index: usize, root: usize) {
|
||||||
|
while let Some(w) = stack.pop() {
|
||||||
|
on_stack[w] = false;
|
||||||
|
comp[w] = comp_index;
|
||||||
|
if w == root {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Strongly connected components of a graph given as adjacency lists (Tarjan, iterative): each
|
/// Strongly connected components of a graph given as adjacency lists (Tarjan, iterative): each
|
||||||
/// node's component index, and the number of nodes in each component.
|
/// node's component index, and the number of nodes in each component.
|
||||||
fn components(edges: &[Vec<usize>]) -> (Vec<usize>, Vec<usize>) {
|
fn components(edges: &[Vec<usize>]) -> (Vec<usize>, Vec<usize>) {
|
||||||
@@ -965,13 +977,7 @@ fn components(edges: &[Vec<usize>]) -> (Vec<usize>, Vec<usize>) {
|
|||||||
low[u] = low[u].min(low[v]);
|
low[u] = low[u].min(low[v]);
|
||||||
}
|
}
|
||||||
if low[v] == num[v] {
|
if low[v] == num[v] {
|
||||||
while let Some(w) = stack.pop() {
|
pop_component(&mut stack, &mut on_stack, &mut comp, comps, v);
|
||||||
on_stack[w] = false;
|
|
||||||
comp[w] = comps;
|
|
||||||
if w == v {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
comps += 1;
|
comps += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1025,6 +1031,7 @@ impl Calc {
|
|||||||
if circ.is_empty() {
|
if circ.is_empty() {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
|
let n = circ.len();
|
||||||
let index: HashMap<Key, usize> = circ.iter().enumerate().map(|(i, k)| (*k, i)).collect();
|
let index: HashMap<Key, usize> = circ.iter().enumerate().map(|(i, k)| (*k, i)).collect();
|
||||||
// Edges from a cell to the `#CIRC!` formulas that read it; loops are the same either way.
|
// Edges from a cell to the `#CIRC!` formulas that read it; loops are the same either way.
|
||||||
let mut deps = Vec::new();
|
let mut deps = Vec::new();
|
||||||
|
|||||||
@@ -1454,6 +1454,34 @@ fn show_levels(s: &mut Session, p: &Json) -> Result<Json> {
|
|||||||
|
|
||||||
/// Data ▸ Outline ▸ Settings: whether summary rows sit below their detail and summary columns
|
/// Data ▸ Outline ▸ Settings: whether summary rows sit below their detail and summary columns
|
||||||
/// right of it. A changed direction moves each collapsed group's mark (+) to its new summary line.
|
/// right of it. A changed direction moves each collapsed group's mark (+) to its new summary line.
|
||||||
|
/// Moves the collapsed marks of a line axis to the summary line on its new side (`on` is the new
|
||||||
|
/// setting, `was_before` the old one). A line that nested groups share keeps its mark on the outermost
|
||||||
|
/// group whose detail is all hidden, or the outermost when none is.
|
||||||
|
fn move_marks(map: &mut std::collections::BTreeMap<u32, LineInfo>, was_before: bool, on: bool) {
|
||||||
|
let mut marked: Vec<outline::OutlineGroup> =
|
||||||
|
outline::groups_with(map, was_before).into_iter().filter(|g| g.has_summary() && map.get(&g.summary()).is_some_and(|e| e.collapsed)).collect();
|
||||||
|
// A mark on a line that nested groups share moves with the outermost of them whose
|
||||||
|
// detail is all hidden, or with the outermost when none is.
|
||||||
|
let mut owner: std::collections::BTreeMap<u32, (bool, u8)> = std::collections::BTreeMap::new();
|
||||||
|
for g in &marked {
|
||||||
|
let key = (!g.collapsed, g.level);
|
||||||
|
let best = owner.entry(g.summary()).or_insert(key);
|
||||||
|
*best = (*best).min(key);
|
||||||
|
}
|
||||||
|
marked.retain(|g| owner.get(&g.summary()) == Some(&(!g.collapsed, g.level)));
|
||||||
|
for g in &marked {
|
||||||
|
if let Some(e) = map.get_mut(&g.summary()) {
|
||||||
|
e.collapsed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for g in marked {
|
||||||
|
let g = outline::OutlineGroup { summary_before: !on, ..g };
|
||||||
|
if g.has_summary() {
|
||||||
|
map.entry(g.summary()).or_default().collapsed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn outline_settings(s: &mut Session, p: &Json) -> Result<Json> {
|
fn outline_settings(s: &mut Session, p: &Json) -> Result<Json> {
|
||||||
let si = target_sheet(s, p)?;
|
let si = target_sheet(s, p)?;
|
||||||
edit(s, |cx| {
|
edit(s, |cx| {
|
||||||
@@ -1469,31 +1497,7 @@ fn outline_settings(s: &mut Session, p: &Json) -> Result<Json> {
|
|||||||
if was_before != on {
|
if was_before != on {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let map = if rows { &mut sh.rows } else { &mut sh.cols };
|
move_marks(if rows { &mut sh.rows } else { &mut sh.cols }, was_before, on);
|
||||||
let mut marked: Vec<outline::OutlineGroup> = outline::groups_with(map, was_before)
|
|
||||||
.into_iter()
|
|
||||||
.filter(|g| g.has_summary() && map.get(&g.summary()).is_some_and(|e| e.collapsed))
|
|
||||||
.collect();
|
|
||||||
// A mark on a line that nested groups share moves with the outermost of them whose
|
|
||||||
// detail is all hidden, or with the outermost when none is.
|
|
||||||
let mut owner: std::collections::BTreeMap<u32, (bool, u8)> = std::collections::BTreeMap::new();
|
|
||||||
for g in &marked {
|
|
||||||
let key = (!g.collapsed, g.level);
|
|
||||||
let best = owner.entry(g.summary()).or_insert(key);
|
|
||||||
*best = (*best).min(key);
|
|
||||||
}
|
|
||||||
marked.retain(|g| owner.get(&g.summary()) == Some(&(!g.collapsed, g.level)));
|
|
||||||
for g in &marked {
|
|
||||||
if let Some(e) = map.get_mut(&g.summary()) {
|
|
||||||
e.collapsed = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for g in marked {
|
|
||||||
let g = outline::OutlineGroup { summary_before: !on, ..g };
|
|
||||||
if g.has_summary() {
|
|
||||||
map.entry(g.summary()).or_default().collapsed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(json!({"summaryBelow": sh.summary_below, "summaryRight": sh.summary_right}))
|
Ok(json!({"summaryBelow": sh.summary_below, "summaryRight": sh.summary_right}))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -165,6 +165,35 @@ impl Dir<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The band's buttons and brackets for this frame.
|
/// The band's buttons and brackets for this frame.
|
||||||
|
/// The bracket for one group's detail, and its tick, on the lane `across`; `button` is the group's
|
||||||
|
/// summary button when it is on screen.
|
||||||
|
fn group_bracket(out: &mut Layout, dir: &Dir, grp: &OutlineGroup, button: Option<f32>, across: f32, side: f32) {
|
||||||
|
let first = dir.first_visible(grp.start, grp.end);
|
||||||
|
let Some((first_line, (a0, _))) = first else { return };
|
||||||
|
if grp.summary_before {
|
||||||
|
// From the button down (or across) to the last detail line, with the tick there.
|
||||||
|
let a0 = button.map_or(a0, |mid| mid + side / 2.0);
|
||||||
|
let end = dir.start(grp.end) + dir.size(grp.end);
|
||||||
|
let a1 = end.min(dir.hi);
|
||||||
|
if a1 <= a0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let tick = (end <= dir.hi).then(|| [dir.point(a1 - 0.5, across), dir.point(a1 - 0.5, across + side / 2.0)]);
|
||||||
|
out.brackets.push(Bracket { line: [dir.point(a0, across), dir.point(a1 - 0.5, across)], tick });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let a1 = match button {
|
||||||
|
Some(mid) => mid - side / 2.0,
|
||||||
|
None => dir.start(grp.summary()).min(dir.hi),
|
||||||
|
};
|
||||||
|
if a1 <= a0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let start_shown = first_line == grp.start && (a0 - dir.start(grp.start)).abs() < 0.5;
|
||||||
|
let tick = start_shown.then(|| [dir.point(a0 + 0.5, across), dir.point(a0 + 0.5, across + side / 2.0)]);
|
||||||
|
out.brackets.push(Bracket { line: [dir.point(a0 + 0.5, across), dir.point(a1, across)], tick });
|
||||||
|
}
|
||||||
|
|
||||||
pub fn layout(geo: &Geo, sh: &Sheet) -> Layout {
|
pub fn layout(geo: &Geo, sh: &Sheet) -> Layout {
|
||||||
let mut out = Layout::default();
|
let mut out = Layout::default();
|
||||||
let zoom = (geo.z / DISPLAY_SCALE).max(0.1);
|
let zoom = (geo.z / DISPLAY_SCALE).max(0.1);
|
||||||
@@ -212,30 +241,7 @@ pub fn layout(geo: &Geo, sh: &Sheet) -> Layout {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// The bracket, over the part of the detail that is on screen.
|
// The bracket, over the part of the detail that is on screen.
|
||||||
let first = dir.first_visible(grp.start, grp.end);
|
group_bracket(&mut out, dir, grp, button, across, side);
|
||||||
let Some((first_line, (a0, _))) = first else { continue };
|
|
||||||
if grp.summary_before {
|
|
||||||
// From the button down (or across) to the last detail line, with the tick there.
|
|
||||||
let a0 = button.map_or(a0, |mid| mid + side / 2.0);
|
|
||||||
let end = dir.start(grp.end) + dir.size(grp.end);
|
|
||||||
let a1 = end.min(dir.hi);
|
|
||||||
if a1 <= a0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let tick = (end <= dir.hi).then(|| [dir.point(a1 - 0.5, across), dir.point(a1 - 0.5, across + side / 2.0)]);
|
|
||||||
out.brackets.push(Bracket { line: [dir.point(a0, across), dir.point(a1 - 0.5, across)], tick });
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let a1 = match button {
|
|
||||||
Some(mid) => mid - side / 2.0,
|
|
||||||
None => dir.start(grp.summary()).min(dir.hi),
|
|
||||||
};
|
|
||||||
if a1 <= a0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let start_shown = first_line == grp.start && (a0 - dir.start(grp.start)).abs() < 0.5;
|
|
||||||
let tick = start_shown.then(|| [dir.point(a0 + 0.5, across), dir.point(a0 + 0.5, across + side / 2.0)]);
|
|
||||||
out.brackets.push(Bracket { line: [dir.point(a0 + 0.5, across), dir.point(a1, across)], tick });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out
|
out
|
||||||
|
|||||||
Reference in New Issue
Block a user