Skip to content

Commit

Permalink
Added fps graph (#7)
Browse files Browse the repository at this point in the history
* added docs and fps graph

* changed version
  • Loading branch information
blitzarx1 authored Apr 22, 2023
1 parent f9962f6 commit 019267b
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 29 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "egui_graphs"
version = "0.0.12"
version = "0.0.13"
authors = ["Dmitrii Samsonov <[email protected]>"]
license = "MIT"
homepage = "https://github.com/blitzarx1/egui_graphs"
Expand Down
40 changes: 35 additions & 5 deletions example/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{collections::HashMap, time::Instant};

use eframe::{run_native, App, CreationContext};
use egui::plot::{Line, Plot, PlotPoints};
use egui::{Context, ScrollArea, Vec2};
use egui_graphs::{Changes, Edge, Elements, GraphView, Node, Settings};
use fdg_sim::glam::Vec3;
Expand All @@ -21,10 +22,12 @@ pub struct ExampleApp {

selected: Vec<Node>,

simulation_stopped: bool,

fps: f64,
fps_history: Vec<f64>,
last_update_time: Instant,
frames_last_time_span: usize,
fps: f32,
simulation_stopped: bool,
}

impl ExampleApp {
Expand All @@ -38,10 +41,12 @@ impl ExampleApp {

selected: Default::default(),

simulation_stopped: false,

fps: 0.,
fps_history: Default::default(),
last_update_time: Instant::now(),
frames_last_time_span: 0,
fps: 0.,
simulation_stopped: false,
}
}

Expand Down Expand Up @@ -103,8 +108,13 @@ impl ExampleApp {
let elapsed = now.duration_since(self.last_update_time);
if elapsed.as_secs() >= 1 {
self.last_update_time = now;
self.fps = self.frames_last_time_span as f32 / elapsed.as_secs_f32();
self.fps = self.frames_last_time_span as f64 / elapsed.as_secs_f64();
self.frames_last_time_span = 0;

self.fps_history.push(self.fps);
if self.fps_history.len() > 100 {
self.fps_history.remove(0);
}
}
}

Expand Down Expand Up @@ -218,6 +228,26 @@ impl App for ExampleApp {
egui::TopBottomPanel::bottom("bottom_panel").show_inside(ui, |ui| {
ui.add_space(5.);
ui.label(format!("fps: {:.1}", self.fps));

let sin: PlotPoints = self
.fps_history
.iter()
.enumerate()
.map(|(i, val)| [i as f64, *val])
.collect();
let line = Line::new(sin);
Plot::new("my_plot")
.height(100.)
.show_x(false)
.show_y(false)
.show_background(false)
.show_axes([false, true])
.allow_boxed_zoom(false)
.allow_double_click_reset(false)
.allow_drag(false)
.allow_scroll(false)
.allow_zoom(false)
.show(ui, |plot_ui| plot_ui.line(line));
});
});

Expand Down
4 changes: 2 additions & 2 deletions src/changes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl Changes {
}
}

/// stores deltas to the nodes properties
/// stores changes to the node properties
#[derive(Default, Clone)]
pub struct ChangesNode {
pub location: Option<Vec2>,
Expand Down Expand Up @@ -104,7 +104,7 @@ impl ChangesNode {
}
}

/// stores deltas to the edges properties
/// stores changes to the edge properties
#[derive(Default, Clone)]
pub struct ChangesEdge {
pub color: Option<Color32>,
Expand Down
33 changes: 31 additions & 2 deletions src/elements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,45 @@ use std::collections::HashMap;

use egui::{Color32, Vec2};

/// Elements represents the collection of all nodes and edges in a graph.
/// It is passed to the GraphView widget and is used to draw the graph.
pub struct Elements {
pub(crate) nodes: HashMap<usize, Node>,
pub(crate) edges: HashMap<(usize, usize), Vec<Edge>>,
nodes: HashMap<usize, Node>,
edges: HashMap<(usize, usize), Vec<Edge>>,
}

impl Elements {
pub fn new(nodes: HashMap<usize, Node>, edges: HashMap<(usize, usize), Vec<Edge>>) -> Self {
Self { nodes, edges }
}

pub fn get_node(&self, idx: &usize) -> Option<&Node> {
self.nodes.get(idx)
}

pub fn get_nodes(&self) -> &HashMap<usize, Node> {
&self.nodes
}

pub fn get_edges(&self) -> &HashMap<(usize, usize), Vec<Edge>> {
&self.edges
}

pub fn get_edge(&self, idx: &(usize, usize, usize)) -> Option<&Edge> {
self.edges.get(&(idx.0, idx.1))?.get(idx.2)
}

/// deletes node and all edges connected to it
pub fn delete_node(&mut self, idx: &usize) {
self.nodes.remove(idx);
self.edges.retain(|k, _| k.0 != *idx && k.1 != *idx);
}

/// deletes edge
pub fn delete_edge(&mut self, idx: &(usize, usize, usize)) {
self.edges.get_mut(&(idx.0, idx.1)).unwrap().remove(idx.2);
}

pub fn get_node_mut(&mut self, idx: &usize) -> Option<&mut Node> {
self.nodes.get_mut(idx)
}
Expand Down
39 changes: 21 additions & 18 deletions src/graph_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ impl<'a> GraphView<'a> {
self.changes.borrow().clone()
}

/// should be called to clear cached graph metadata, for example
/// in case when you want to show completely different graph from the one
/// in the last frame
pub fn reset_state(ui: &mut Ui) {
State::default().store(ui);
}
Expand Down Expand Up @@ -96,7 +99,7 @@ fn get_bounds(elements: &Elements) -> (Vec2, Vec2) {
let mut max_x = MIN;
let mut max_y = MIN;

elements.nodes.iter().for_each(|(_, n)| {
elements.get_nodes().iter().for_each(|(_, n)| {
if n.location.x < min_x {
min_x = n.location.x;
};
Expand All @@ -122,27 +125,25 @@ fn rotate_vector(vec: Vec2, angle: f32) -> Vec2 {

fn draw(p: &Painter, elements: &Elements, zoom: f32, pan: Vec2) {
draw_edges(p, elements, zoom, pan);
draw_nodes(p, &elements.nodes, zoom, pan);
draw_nodes(p, elements, zoom, pan);
}

fn draw_edges(p: &Painter, elements: &Elements, zoom: f32, pan: Vec2) {
let angle = std::f32::consts::TAU / 50.;

elements.edges.iter().for_each(|(_, edges)| {
elements.get_edges().iter().for_each(|(_, edges)| {
let edges_count = edges.len();
let mut sames = HashMap::with_capacity(edges_count);

edges.iter().for_each(|e| {
let edge = e.screen_transform(zoom);

let start_node = elements
.nodes
.get(&edge.start)
.get_node(&edge.start)
.unwrap()
.screen_transform(zoom, pan);
let end_node = elements
.nodes
.get(&edge.end)
.get_node(&edge.end)
.unwrap()
.screen_transform(zoom, pan);

Expand Down Expand Up @@ -235,8 +236,8 @@ fn draw_edges(p: &Painter, elements: &Elements, zoom: f32, pan: Vec2) {
});
}

fn draw_nodes(p: &Painter, nodes: &HashMap<usize, Node>, zoom: f32, pan: Vec2) {
nodes.iter().for_each(|(_, n)| {
fn draw_nodes(p: &Painter, elements: &Elements, zoom: f32, pan: Vec2) {
elements.get_nodes().iter().for_each(|(_, n)| {
let node = n.screen_transform(zoom, pan);
draw_node(p, &node)
});
Expand Down Expand Up @@ -324,9 +325,10 @@ fn handle_zoom_and_pan(ui: &Ui, response: &Response, state: &State) -> (f32, Vec
(new_zoom, new_pan)
}

// TODO: optimize this full scan run
// TODO: optimize this full scan run with quadtree or similar
// need to modify `crate::elements::Elements` to store nodes in a quadtree
fn node_by_pos<'a>(elements: &'a Elements, state: &State, pos: Pos2) -> Option<(usize, &'a Node)> {
let node_props = elements.nodes.iter().find(|(_, n)| {
let node_props = elements.get_nodes().iter().find(|(_, n)| {
let node = n.screen_transform(state.zoom, state.pan);
(node.location - pos.to_vec2()).length() <= node.radius
});
Expand All @@ -346,22 +348,22 @@ fn handle_drags(
) {
if response.drag_started() {
if let Some((idx, _)) = node_by_pos(elements, state, response.hover_pos().unwrap()) {
changes.select_node(&idx, elements.nodes.get(&idx).unwrap());
changes.select_node(&idx, elements.get_node(&idx).unwrap());
state.set_dragged_node(idx);
}
}

if response.dragged() && state.get_dragged_node().is_some() {
let node_idx_dragged = state.get_dragged_node().unwrap();
let node_dragged = elements.nodes.get(&node_idx_dragged).unwrap();
let node_dragged = elements.get_node(&node_idx_dragged).unwrap();

let delta_in_graph_coords = response.drag_delta() / state.zoom;
changes.move_node(&node_idx_dragged, node_dragged, delta_in_graph_coords);
}

if response.drag_released() && state.get_dragged_node().is_some() {
let idx = &state.get_dragged_node().unwrap();
changes.deselect_node(idx, elements.nodes.get(idx).unwrap());
changes.deselect_node(idx, elements.get_node(idx).unwrap());
state.unset_dragged_node();
}
}
Expand All @@ -378,16 +380,17 @@ fn handle_clicks(

let node = node_by_pos(elements, state, response.hover_pos().unwrap());
if node.is_none() {
elements.nodes.iter().for_each(|(idx, n)| {
// TODO: optimize this. keep selected nodes in state to quickly manage them
elements.get_nodes().iter().for_each(|(idx, n)| {
if !n.selected {
return;
}

changes.deselect_node(idx, elements.nodes.get(idx).unwrap());
changes.deselect_node(idx, n);
});
return;
}

let (idx, _) = node.unwrap();
changes.select_node(&idx, elements.nodes.get(&idx).unwrap());
let (idx, n) = node.unwrap();
changes.select_node(&idx, n);
}

0 comments on commit 019267b

Please sign in to comment.