blitzarx1 / egui_graphs

Interactive graph visualization widget for rust powered by egui and petgraph
https://docs.rs/crate/egui_graphs
MIT License
388 stars 29 forks source link

Render order of nodes and edges causes a 1 frame graphical glitch. #172

Open XertroV opened 7 months ago

XertroV commented 7 months ago

https://github.com/blitzarx1/egui_graphs/blob/416a211ffdfd46aabb3d8bb73a69bbce2b004985/src/draw/drawer.rs#L60-L63

Here, edges are drawn first, then nodes. In fill_layers_nodes, the nodes position is updated based on the node props. So, if you add a node to the graph, then for the first frame the edge that gets drawn thinks the node is at 0,0 because the position has not yet been updated. (I think)

This might be related to the fact I'm adding the node with a custom shape and a position, or extending the graph live.

Swapping the order of these two calls fixed the issue for me, except that edges are now drawn over nodes.

alternative soln 1

I patched the code like this:

    pub fn draw(mut self) {
        self.update_nodes();
        self.draw_edges();
        self.fill_layers_nodes();
    }

    fn update_nodes(&mut self) {
        self.g
            .g
            .node_indices()
            .collect::<Vec<_>>()
            .into_iter()
            .for_each(|idx| {
                let n = self.g.node_mut(idx).unwrap();
                // note: added this simple function (body is just: `self.display.update(&self.props)`)
                n.update_display_from_props();
            });
    }

    fn fill_layers_nodes(&mut self) {
        self.g
            .g
            .node_indices()
            .collect::<Vec<_>>()
            .into_iter()
            .for_each(|idx| {
                let n = self.g.node_mut(idx).unwrap();
                // note: no update here anymore
                let display = n.display_mut();
                let shapes = display.shapes(self.ctx);

                if n.selected() || n.dragged() {
                    shapes.into_iter().for_each(|s| {
                        self.layer_top.add(s);
                    });
                } else {
                    shapes.into_iter().for_each(|s| {
                        self.layer_bot.add(s);
                    });
                }
            });
    }

alternative 2

The user can fix this themselves, technically. (the node.update_display_from_props() function helps)

Basically I added 1 line to my code (the call to update from props) and restored the original fn draw and fn fill_layers_nodes

My code is now like this (in the part that adds nodes)

    let node_ix = add_node_custom(g, tip, |ix, hp| {
        let mut n = Node::new(hp.clone());
        n.set_label(format!("↑{}", hp.0.height));
        n.bind(
            ix,
            Pos2::new(
                NODE_SPACING * hp.0.category as f32,
                hp.0.height as f32 * -NODE_SPACING,
            ),
        );
        // added this line
        n.update_display_from_props();
        n
    });
blitzarx1 commented 1 month ago

This needs further investigation. Will return to it soon