aboutsummaryrefslogtreecommitdiff
path: root/server/data/src/book/diagram_layout.rs
blob: cd929f44bc1c78396be12f73345a246a95156656 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/*
    Hurry Curry! - a game about cooking
    Copyright (C) 2025 Hurry Curry! Contributors

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, version 3 of the License only.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

*/

use anyhow::Result;
use hurrycurry_protocol::{
    book::Diagram,
    glam::{Vec2, vec2},
};
use std::collections::{BTreeMap, BTreeSet};

pub struct Layout {
    pub nodes: BTreeMap<String, Vec2>,
    pub edges: Vec<(usize, usize)>,
}

pub fn diagram_layout(diagram: &mut Diagram) -> Result<()> {
    let mut graph = Graph {
        vertices: (0..diagram.nodes.len())
            .map(|i| vec2(i as f32, i as f32) * 30.)
            .collect(),
        edges: diagram.edges.iter().map(|e| (e.src, e.dst)).collect(),
    };

    graph.layout();

    diagram
        .nodes
        .iter_mut()
        .zip(graph.vertices.iter())
        .for_each(|(n, v)| n.position = *v);

    Ok(())
}

pub struct Graph {
    pub vertices: Vec<Vec2>,
    pub edges: Vec<(usize, usize)>,
}
impl Graph {
    pub fn layout(&mut self) {
        let mut layers = Vec::new();
        let mut previous_layers = BTreeSet::new();

        let mut to_insert = Vec::new();
        to_insert.extend(0..self.vertices.len());

        while !to_insert.is_empty() {
            let mut next_layer = Vec::new();
            to_insert.retain(|&vi| {
                for &(src, dst) in &self.edges {
                    if src == vi && !previous_layers.contains(&dst) {
                        return true;
                    }
                }
                next_layer.push(vi);
                false
            });
            previous_layers.extend(next_layer.iter().copied());
            layers.push(next_layer);
        }

        for y in 0..layers.iter().len() {
            let mut layer = layers[y].clone();
            layer.sort_by_cached_key(|&vi| {
                self.edges
                    .iter()
                    .find(|&&(src, _)| src == vi)
                    .map_or(0, |&(_, dst)| (self.vertices[dst].x * 100.) as i64)
            });
            for (x, &vi) in layer.iter().enumerate() {
                self.vertices[vi] = vec2(x as f32 - layer.len() as f32 / 2., y as f32) * 100.;
            }
        }
    }
}