Modify GUI for better readability, clean up
This commit is contained in:
5
src/algorithms/edmonds_karp.rs
Normal file
5
src/algorithms/edmonds_karp.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// Edmonds-Karp (also Ford-Fulkerson mit BFS statt DFS),
|
||||||
|
|
||||||
|
//pub fn edmonds_karp(mut graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, sink: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||||
|
//
|
||||||
|
//}
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
use std::cmp::min;
|
|
||||||
use std::collections::VecDeque;
|
|
||||||
|
|
||||||
use eframe::glow::FALSE;
|
|
||||||
// Ford-Fulkerson mit DFS zur Berechnung des flusserhöhenden Pfades,
|
// Ford-Fulkerson mit DFS zur Berechnung des flusserhöhenden Pfades,
|
||||||
// Edmonds-Karp (also Ford-Fulkerson mit BFS statt DFS),
|
// Edmonds-Karp (also Ford-Fulkerson mit BFS statt DFS),
|
||||||
// Dinic
|
// Dinic
|
||||||
// Goldberg-Tarjan (auch Preflow-Push oder Push-Relabel genannt).
|
// Goldberg-Tarjan (auch Preflow-Push oder Push-Relabel genannt).
|
||||||
|
use std::cmp::min;
|
||||||
|
use std::collections::VecDeque;
|
||||||
use egui_graphs::{Graph, DefaultNodeShape, DefaultEdgeShape};
|
use egui_graphs::{Graph, DefaultNodeShape, DefaultEdgeShape};
|
||||||
use petgraph::adj::EdgeIndex;
|
use petgraph::adj::EdgeIndex;
|
||||||
use petgraph::data::Build;
|
use petgraph::data::Build;
|
||||||
@@ -15,7 +13,6 @@ use petgraph::visit::{Dfs, EdgeRef, NodeRef, VisitMap, Visitable};
|
|||||||
use petgraph::stable_graph::{StableGraph, NodeIndex};
|
use petgraph::stable_graph::{StableGraph, NodeIndex};
|
||||||
|
|
||||||
use crate::random_generator::MaxflowProblem;
|
use crate::random_generator::MaxflowProblem;
|
||||||
use crate::graph::{MaxflowNode, MaxflowEdge};
|
|
||||||
//mod random_generator;
|
//mod random_generator;
|
||||||
|
|
||||||
fn available_capacity(edge: EdgeReference<'_, (u64, u64)>) -> u64 {
|
fn available_capacity(edge: EdgeReference<'_, (u64, u64)>) -> u64 {
|
||||||
@@ -76,12 +73,12 @@ pub fn ford_fulkerson(mut graph: StableGraph<(f32, f32), (u64, u64)>, source: No
|
|||||||
graph
|
graph
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FordFulkerson {
|
//pub struct FordFulkerson {
|
||||||
problem: MaxflowProblem,
|
// problem: MaxflowProblem,
|
||||||
flows: Graph<(), u64, Directed, u32, DefaultNodeShape, DefaultEdgeShape>,
|
// flows: Graph<(), u64, Directed, u32, DefaultNodeShape, DefaultEdgeShape>,
|
||||||
// alertnatively, use a map from EdgeIndex to flow value
|
// alertnatively, use a map from EdgeIndex to flow value
|
||||||
//dfs: Dfs<N, VM>,
|
//dfs: Dfs<N, VM>,
|
||||||
}
|
//}
|
||||||
|
|
||||||
/* impl FordFulkerson {
|
/* impl FordFulkerson {
|
||||||
fn new(problem: MaxflowProblem) -> S/elf {
|
fn new(problem: MaxflowProblem) -> S/elf {
|
||||||
4
src/algorithms/mod.rs
Normal file
4
src/algorithms/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
mod edmonds_karp;
|
||||||
|
mod ford_fulkerson;
|
||||||
|
|
||||||
|
pub use ford_fulkerson::ford_fulkerson;
|
||||||
33
src/graph.rs
33
src/graph.rs
@@ -1,33 +0,0 @@
|
|||||||
//use egui_graphs::{Node, Edge};
|
|
||||||
use petgraph::graph::{Node, Edge};
|
|
||||||
|
|
||||||
pub trait MaxflowNode {
|
|
||||||
fn visited(&self) -> bool;
|
|
||||||
fn set_visited(&mut self, b: bool);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait MaxflowEdge {
|
|
||||||
fn remaining_capacity(&self) -> u64;
|
|
||||||
fn set_capacity(&mut self, c: u64);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* impl MaxflowNode for Node<bool, (u64, u64)> {
|
|
||||||
fn visited(&self) -> bool {
|
|
||||||
*self.()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_visited(&mut self, b: bool) {
|
|
||||||
let payload = self.payload_mut();
|
|
||||||
*payload = b;
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
|
|
||||||
impl MaxflowEdge for Edge<(u64, u64)> {
|
|
||||||
fn remaining_capacity(&self) -> u64 {
|
|
||||||
self.weight.1 - self.weight.0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_capacity(&mut self, c: u64) {
|
|
||||||
self.weight.0 = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
355
src/layout.rs
Normal file
355
src/layout.rs
Normal file
@@ -0,0 +1,355 @@
|
|||||||
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
|
use egui::{
|
||||||
|
epaint::{CubicBezierShape, QuadraticBezierShape},
|
||||||
|
Color32, Pos2, Stroke, Vec2, Shape, FontId, FontFamily, epaint::{TextShape}
|
||||||
|
};
|
||||||
|
|
||||||
|
use petgraph::{matrix_graph::Nullable, stable_graph::IndexType, EdgeType};
|
||||||
|
|
||||||
|
use egui_graphs::{
|
||||||
|
DefaultEdgeShape, DisplayEdge, EdgeProps,
|
||||||
|
DefaultNodeShape, DisplayNode, NodeProps,
|
||||||
|
Node, DrawContext
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct CustomEdgeShape {
|
||||||
|
pub order: usize,
|
||||||
|
pub selected: bool,
|
||||||
|
|
||||||
|
pub width: f32,
|
||||||
|
pub tip_size: f32,
|
||||||
|
pub tip_angle: f32,
|
||||||
|
pub curve_size: f32,
|
||||||
|
pub loop_size: f32,
|
||||||
|
pub label_text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Clone> From<EdgeProps<E>> for CustomEdgeShape {
|
||||||
|
fn from(props: EdgeProps<E>) -> Self {
|
||||||
|
Self {
|
||||||
|
order: props.order,
|
||||||
|
selected: props.selected,
|
||||||
|
label_text: props.label,
|
||||||
|
|
||||||
|
width: 1.,
|
||||||
|
tip_size: 10.,
|
||||||
|
tip_angle: std::f32::consts::TAU / 30.,
|
||||||
|
curve_size: 15.,
|
||||||
|
loop_size: 3.,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: Clone, E: Clone, Ty: EdgeType, Ix: IndexType, D: DisplayNode<N, E, Ty, Ix>>
|
||||||
|
DisplayEdge<N, E, Ty, Ix, D> for CustomEdgeShape
|
||||||
|
{
|
||||||
|
// helper functions
|
||||||
|
// copied from: https://github.com/SeverinAlexB/pknames/blob/d042b6843ee503d92ae36742bab81b14574d9ad3/cli/src/visualization/edge_vis.rs
|
||||||
|
fn is_inside(
|
||||||
|
&self,
|
||||||
|
start: &Node<N, E, Ty, Ix, D>,
|
||||||
|
end: &Node<N, E, Ty, Ix, D>,
|
||||||
|
pos: egui::Pos2,
|
||||||
|
) -> bool {
|
||||||
|
if start.id() == end.id() {
|
||||||
|
return is_inside_loop(start, self, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
let pos_start = start.location();
|
||||||
|
let pos_end = end.location();
|
||||||
|
|
||||||
|
if self.order == 0 {
|
||||||
|
return is_inside_line(pos_start, pos_end, pos, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
is_inside_curve(start, end, self, pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shapes(
|
||||||
|
&mut self,
|
||||||
|
start: &Node<N, E, Ty, Ix, D>,
|
||||||
|
end: &Node<N, E, Ty, Ix, D>,
|
||||||
|
ctx: &DrawContext,
|
||||||
|
) -> Vec<egui::Shape> {
|
||||||
|
// fixed properties
|
||||||
|
let edge_color = Color32::LIGHT_GRAY;
|
||||||
|
let label_color = Color32::DARK_GRAY;
|
||||||
|
//let label_visible = true;
|
||||||
|
//let style = ctx.ctx.style().visuals.widgets.inactive;
|
||||||
|
|
||||||
|
// calculate coordinates
|
||||||
|
let dir = (end.location() - start.location()).normalized();
|
||||||
|
let start_connector_point = start.display().closest_boundary_point(dir);
|
||||||
|
let end_connector_point = end.display().closest_boundary_point(-dir);
|
||||||
|
let tip_end = end_connector_point;
|
||||||
|
let edge_start = start_connector_point;
|
||||||
|
let edge_end = end_connector_point;
|
||||||
|
|
||||||
|
// draw curved edge
|
||||||
|
let stroke_edge = Stroke::new(self.width * ctx.meta.zoom, edge_color);
|
||||||
|
let dir_perpendicular = Vec2::new(-dir.y, dir.x);
|
||||||
|
let center_point = (edge_start + edge_end.to_vec2()).to_vec2() / 2.;
|
||||||
|
let control_point = (center_point + dir_perpendicular * self.curve_size as f32).to_pos2();
|
||||||
|
let tip_dir = (control_point-tip_end).normalized();
|
||||||
|
//println!("edge_start: {:?} edge_end {:?} center_point {:?} dir_p {:?} control_point {:?}", edge_start, edge_end, center_point, dir_perpendicular, control_point);
|
||||||
|
|
||||||
|
let arrow_tip_dir_1 = rotate_vector(tip_dir, self.tip_angle) * self.tip_size;
|
||||||
|
let arrow_tip_dir_2 = rotate_vector(tip_dir, -self.tip_angle) * self.tip_size;
|
||||||
|
let tip_start_1 = tip_end + arrow_tip_dir_1;
|
||||||
|
let tip_start_2 = tip_end + arrow_tip_dir_2;
|
||||||
|
|
||||||
|
let edge_end_curved = point_between(tip_start_1, tip_start_2);
|
||||||
|
let line_curved = QuadraticBezierShape::from_points_stroke(
|
||||||
|
[
|
||||||
|
ctx.meta.canvas_to_screen_pos(edge_start),
|
||||||
|
ctx.meta.canvas_to_screen_pos(control_point),
|
||||||
|
ctx.meta.canvas_to_screen_pos(edge_end_curved),
|
||||||
|
],
|
||||||
|
false,
|
||||||
|
Color32::TRANSPARENT,
|
||||||
|
stroke_edge,
|
||||||
|
);
|
||||||
|
|
||||||
|
// draw tip/arrow
|
||||||
|
let stroke_tip = Stroke::new(0., edge_color);
|
||||||
|
let line_curved_tip = Shape::convex_polygon(
|
||||||
|
vec![
|
||||||
|
ctx.meta.canvas_to_screen_pos(tip_end),
|
||||||
|
ctx.meta.canvas_to_screen_pos(tip_start_1),
|
||||||
|
ctx.meta.canvas_to_screen_pos(tip_start_2),
|
||||||
|
],
|
||||||
|
edge_color,
|
||||||
|
stroke_tip,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// draw label
|
||||||
|
let size = (::egui_graphs::node_size(start, dir) + egui_graphs::node_size(end, dir)) / 2.;
|
||||||
|
let galley = ctx.ctx.fonts(|f| {
|
||||||
|
f.layout_no_wrap(
|
||||||
|
self.label_text.clone(),
|
||||||
|
FontId::new(ctx.meta.canvas_to_screen_size(size), FontFamily::Monospace),
|
||||||
|
label_color,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let flattened_curve = line_curved.flatten(None);
|
||||||
|
let median = *flattened_curve.get(flattened_curve.len() / 2).unwrap();
|
||||||
|
|
||||||
|
let label_width = galley.rect.width();
|
||||||
|
let label_height = galley.rect.height();
|
||||||
|
let pos = Pos2::new(median.x - label_width / 2., median.y - label_height);
|
||||||
|
|
||||||
|
let label_shape = TextShape::new(pos, galley, label_color);
|
||||||
|
|
||||||
|
vec![line_curved.into(), line_curved_tip, label_shape.into()]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, state: &egui_graphs::EdgeProps<E>) {
|
||||||
|
self.order = state.order;
|
||||||
|
self.selected = state.selected;
|
||||||
|
self.label_text = state.label.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Everything below is copied from:
|
||||||
|
// https://github.com/SeverinAlexB/pknames/blob/d042b6843ee503d92ae36742bab81b14574d9ad3/cli/src/visualization/edge_vis.rs
|
||||||
|
fn shape_looped(
|
||||||
|
node_size: f32,
|
||||||
|
node_center: Pos2,
|
||||||
|
stroke: Stroke,
|
||||||
|
e: &CustomEdgeShape,
|
||||||
|
) -> CubicBezierShape {
|
||||||
|
let center_horizon_angle = PI / 4.;
|
||||||
|
let y_intersect = node_center.y - node_size * center_horizon_angle.sin();
|
||||||
|
|
||||||
|
let edge_start = Pos2::new(
|
||||||
|
node_center.x - node_size * center_horizon_angle.cos(),
|
||||||
|
y_intersect,
|
||||||
|
);
|
||||||
|
let edge_end = Pos2::new(
|
||||||
|
node_center.x + node_size * center_horizon_angle.cos(),
|
||||||
|
y_intersect,
|
||||||
|
);
|
||||||
|
|
||||||
|
let loop_size = node_size * (e.loop_size + e.order as f32);
|
||||||
|
|
||||||
|
let control_point1 = Pos2::new(node_center.x + loop_size, node_center.y - loop_size);
|
||||||
|
let control_point2 = Pos2::new(node_center.x - loop_size, node_center.y - loop_size);
|
||||||
|
|
||||||
|
CubicBezierShape::from_points_stroke(
|
||||||
|
[edge_end, control_point1, control_point2, edge_start],
|
||||||
|
false,
|
||||||
|
Color32::default(),
|
||||||
|
stroke,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shape_curved(
|
||||||
|
pos_start: Pos2,
|
||||||
|
pos_end: Pos2,
|
||||||
|
size_start: f32,
|
||||||
|
size_end: f32,
|
||||||
|
stroke: Stroke,
|
||||||
|
e: &CustomEdgeShape,
|
||||||
|
) -> QuadraticBezierShape {
|
||||||
|
let vec = pos_end - pos_start;
|
||||||
|
let dist: f32 = vec.length();
|
||||||
|
let dir = vec / dist;
|
||||||
|
|
||||||
|
let start_node_radius_vec = Vec2::new(size_start, size_start) * dir;
|
||||||
|
let end_node_radius_vec = Vec2::new(size_end, size_end) * dir;
|
||||||
|
|
||||||
|
let tip_end = pos_start + vec - end_node_radius_vec;
|
||||||
|
|
||||||
|
let edge_start = pos_start + start_node_radius_vec;
|
||||||
|
let edge_end = pos_end + end_node_radius_vec;
|
||||||
|
|
||||||
|
let dir_perpendicular = Vec2::new(-dir.y, dir.x);
|
||||||
|
let center_point = (edge_start + tip_end.to_vec2()).to_vec2() / 2.0;
|
||||||
|
let control_point =
|
||||||
|
(center_point + dir_perpendicular * e.curve_size * e.order as f32).to_pos2();
|
||||||
|
|
||||||
|
QuadraticBezierShape::from_points_stroke(
|
||||||
|
[edge_start, control_point, edge_end],
|
||||||
|
false,
|
||||||
|
stroke.color,
|
||||||
|
stroke,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_inside_loop<E: Clone, N: Clone, Ix: IndexType, Ty: EdgeType, D: DisplayNode<N, E, Ty, Ix>>(
|
||||||
|
node: &Node<N, E, Ty, Ix, D>,
|
||||||
|
e: &CustomEdgeShape,
|
||||||
|
pos: Pos2,
|
||||||
|
) -> bool {
|
||||||
|
let node_size = node_size(node);
|
||||||
|
|
||||||
|
let shape = shape_looped(node_size, node.location(), Stroke::default(), e);
|
||||||
|
is_point_on_cubic_bezier_curve(pos, shape, e.width)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_inside_line(pos_start: Pos2, pos_end: Pos2, pos: Pos2, e: &CustomEdgeShape) -> bool {
|
||||||
|
let distance = distance_segment_to_point(pos_start, pos_end, pos);
|
||||||
|
distance <= e.width
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_inside_curve<
|
||||||
|
N: Clone,
|
||||||
|
E: Clone,
|
||||||
|
Ty: EdgeType,
|
||||||
|
Ix: IndexType,
|
||||||
|
D: DisplayNode<N, E, Ty, Ix>,
|
||||||
|
>(
|
||||||
|
node_start: &Node<N, E, Ty, Ix, D>,
|
||||||
|
node_end: &Node<N, E, Ty, Ix, D>,
|
||||||
|
e: &CustomEdgeShape,
|
||||||
|
pos: Pos2,
|
||||||
|
) -> bool {
|
||||||
|
let pos_start = node_start.location();
|
||||||
|
let pos_end = node_end.location();
|
||||||
|
|
||||||
|
let size_start = node_size(node_start);
|
||||||
|
let size_end = node_size(node_end);
|
||||||
|
|
||||||
|
let shape = shape_curved(
|
||||||
|
pos_start,
|
||||||
|
pos_end,
|
||||||
|
size_start,
|
||||||
|
size_end,
|
||||||
|
Stroke::default(),
|
||||||
|
e,
|
||||||
|
);
|
||||||
|
is_point_on_quadratic_bezier_curve(pos, shape, e.width)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn node_size<N: Clone, E: Clone, Ty: EdgeType, Ix: IndexType, D: DisplayNode<N, E, Ty, Ix>>(
|
||||||
|
node: &Node<N, E, Ty, Ix, D>,
|
||||||
|
) -> f32 {
|
||||||
|
let left_dir = Vec2::new(-1., 0.);
|
||||||
|
let connector_left = node.display().closest_boundary_point(left_dir);
|
||||||
|
let connector_right = node.display().closest_boundary_point(-left_dir);
|
||||||
|
|
||||||
|
(connector_right.x - connector_left.x) / 2.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the distance from line segment `a``b` to point `c`.
|
||||||
|
/// Adapted from https://stackoverflow.com/questions/1073336/circle-line-segment-collision-detection-algorithm
|
||||||
|
fn distance_segment_to_point(a: Pos2, b: Pos2, point: Pos2) -> f32 {
|
||||||
|
let ac = point - a;
|
||||||
|
let ab = b - a;
|
||||||
|
|
||||||
|
let d = a + proj(ac, ab);
|
||||||
|
|
||||||
|
let ad = d - a;
|
||||||
|
|
||||||
|
let k = if ab.x.abs() > ab.y.abs() {
|
||||||
|
ad.x / ab.x
|
||||||
|
} else {
|
||||||
|
ad.y / ab.y
|
||||||
|
};
|
||||||
|
|
||||||
|
if k <= 0.0 {
|
||||||
|
return hypot2(point.to_vec2(), a.to_vec2()).sqrt();
|
||||||
|
} else if k >= 1.0 {
|
||||||
|
return hypot2(point.to_vec2(), b.to_vec2()).sqrt();
|
||||||
|
}
|
||||||
|
|
||||||
|
hypot2(point.to_vec2(), d.to_vec2()).sqrt()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the square of the Euclidean distance between vectors `a` and `b`.
|
||||||
|
fn hypot2(a: Vec2, b: Vec2) -> f32 {
|
||||||
|
(a - b).dot(a - b)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the projection of vector `a` onto vector `b`.
|
||||||
|
fn proj(a: Vec2, b: Vec2) -> Vec2 {
|
||||||
|
let k = a.dot(b) / b.dot(b);
|
||||||
|
Vec2::new(k * b.x, k * b.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_point_on_cubic_bezier_curve(point: Pos2, curve: CubicBezierShape, width: f32) -> bool {
|
||||||
|
is_point_on_bezier_curve(point, curve.flatten(Option::new(10.0)), width)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_point_on_quadratic_bezier_curve(
|
||||||
|
point: Pos2,
|
||||||
|
curve: QuadraticBezierShape,
|
||||||
|
width: f32,
|
||||||
|
) -> bool {
|
||||||
|
is_point_on_bezier_curve(point, curve.flatten(Option::new(0.3)), width)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_point_on_bezier_curve(point: Pos2, curve_points: Vec<Pos2>, width: f32) -> bool {
|
||||||
|
let mut previous_point = None;
|
||||||
|
for p in curve_points {
|
||||||
|
if let Some(pp) = previous_point {
|
||||||
|
let distance = distance_segment_to_point(p, pp, point);
|
||||||
|
if distance < width {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previous_point = Some(p);
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// rotates vector by angle
|
||||||
|
fn rotate_vector(vec: Vec2, angle: f32) -> Vec2 {
|
||||||
|
let cos = angle.cos();
|
||||||
|
let sin = angle.sin();
|
||||||
|
Vec2::new(cos * vec.x - sin * vec.y, sin * vec.x + cos * vec.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// finds point exactly in the middle between 2 points
|
||||||
|
fn point_between(p1: Pos2, p2: Pos2) -> Pos2 {
|
||||||
|
let base = p1 - p2;
|
||||||
|
let base_len = base.length();
|
||||||
|
let dir = base / base_len;
|
||||||
|
p1 - (base_len / 2.) * dir
|
||||||
|
}
|
||||||
13
src/main.rs
13
src/main.rs
@@ -1,15 +1,18 @@
|
|||||||
use eframe::{run_native, App, CreationContext, NativeOptions, Frame};
|
use eframe::{run_native, App, CreationContext, NativeOptions, Frame};
|
||||||
use egui::{CentralPanel, SidePanel, Context};
|
use egui::{CentralPanel, SidePanel, Context};
|
||||||
use egui_graphs::{GraphView, Graph, SettingsStyle, LayoutRandom, LayoutStateRandom};
|
use egui_graphs::{DefaultEdgeShape, DefaultNodeShape, Graph, GraphView, LayoutRandom, LayoutStateRandom, SettingsStyle};
|
||||||
|
|
||||||
use algorithms::ford_fulkerson;
|
use petgraph::Directed;
|
||||||
use random_generator::MaxflowProblem;
|
use random_generator::MaxflowProblem;
|
||||||
|
use layout::CustomEdgeShape;
|
||||||
|
use crate::algorithms::ford_fulkerson;
|
||||||
|
|
||||||
mod random_generator;
|
mod random_generator;
|
||||||
mod algorithms;
|
mod algorithms;
|
||||||
mod graph;
|
mod layout;
|
||||||
|
|
||||||
pub struct MaxflowApp {
|
pub struct MaxflowApp {
|
||||||
g: Graph<(f32, f32), (u64, u64)>,
|
g: Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>,
|
||||||
p: MaxflowProblem,
|
p: MaxflowProblem,
|
||||||
node_count: u64,
|
node_count: u64,
|
||||||
max_capacity: u64,
|
max_capacity: u64,
|
||||||
@@ -26,7 +29,7 @@ impl App for MaxflowApp {
|
|||||||
fn update(&mut self, ctx: &Context, _: &mut Frame) {
|
fn update(&mut self, ctx: &Context, _: &mut Frame) {
|
||||||
ctx.set_theme(egui::Theme::Light);
|
ctx.set_theme(egui::Theme::Light);
|
||||||
CentralPanel::default().show(ctx, |ui| {
|
CentralPanel::default().show(ctx, |ui| {
|
||||||
ui.add(&mut GraphView::<_, _, _, _, _, _, LayoutStateRandom, LayoutRandom>::new(&mut self.g).with_styles(&SettingsStyle::default().with_labels_always(true)));
|
ui.add(&mut GraphView::<_, _, _, _, _, CustomEdgeShape, LayoutStateRandom, LayoutRandom>::new(&mut self.g).with_styles(&SettingsStyle::default().with_labels_always(true)));
|
||||||
});
|
});
|
||||||
SidePanel::right("right_panel")
|
SidePanel::right("right_panel")
|
||||||
.min_width(200.)
|
.min_width(200.)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use petgraph::stable_graph::{StableGraph, NodeIndex};
|
use petgraph::stable_graph::{StableGraph, NodeIndex};
|
||||||
use petgraph::{Directed};
|
use petgraph::{Directed};
|
||||||
use egui_graphs::{Graph};
|
use egui_graphs::{default_edge_transform, default_node_transform, to_graph_custom, DefaultEdgeShape, DefaultNodeShape, Graph};
|
||||||
use egui::Pos2;
|
use egui::Pos2;
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
@@ -9,15 +9,18 @@ use itertools::Itertools;
|
|||||||
use geo::{Line, coord, line_intersection::{line_intersection, LineIntersection}};
|
use geo::{Line, coord, line_intersection::{line_intersection, LineIntersection}};
|
||||||
use egui::Color32;
|
use egui::Color32;
|
||||||
|
|
||||||
|
use crate::layout::CustomEdgeShape;
|
||||||
|
|
||||||
pub struct MaxflowProblem {
|
pub struct MaxflowProblem {
|
||||||
pub g: Graph<(), u64, Directed, u32, egui_graphs::DefaultNodeShape, egui_graphs::DefaultEdgeShape>,
|
pub g: StableGraph<(f32, f32), (u64, u64)>,
|
||||||
pub s: NodeIndex,
|
pub s: NodeIndex,
|
||||||
pub t: NodeIndex,
|
pub t: NodeIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaxflowProblem {
|
impl MaxflowProblem {
|
||||||
pub fn new(num_nodes: u64, max_capacity: u64) -> Self {
|
pub fn new(num_nodes: u64, max_capacity: u64) -> Self {
|
||||||
let mut graph: Graph<_, u64> = Graph::new(StableGraph::default());
|
// Node Type: (x,y) Edge Type: (current_flow, capacity)
|
||||||
|
let mut graph: StableGraph<(f32, f32), (u64, u64)> = StableGraph::default();
|
||||||
let mut nodes: Vec<NodeIndex> = Vec::new();
|
let mut nodes: Vec<NodeIndex> = Vec::new();
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
@@ -25,30 +28,27 @@ impl MaxflowProblem {
|
|||||||
for _i in 0..num_nodes {
|
for _i in 0..num_nodes {
|
||||||
let x: f32 = rng.gen_range(0.0..=400.0);
|
let x: f32 = rng.gen_range(0.0..=400.0);
|
||||||
let y: f32 = rng.gen_range(0.0..=300.0);
|
let y: f32 = rng.gen_range(0.0..=300.0);
|
||||||
let pos = Pos2::new(x,y);
|
let n = graph.add_node((x,y));
|
||||||
let n = graph.add_node_with_label_and_location((), String::new(), pos);
|
|
||||||
nodes.push(n);
|
nodes.push(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
// select source (s) and sink (t)
|
// select source (s) and sink (t)
|
||||||
let s = *nodes.choose(&mut rng).expect("no nodes found");
|
let (s,t) = nodes.choose_multiple(&mut rng, 2).copied().collect_tuple::<(NodeIndex, NodeIndex)>().expect("not enough nodes");
|
||||||
let t = *nodes.choose(&mut rng).expect("no nodes found");
|
|
||||||
graph.node_mut(s).expect("node index not found").set_label(String::from("s"));
|
|
||||||
graph.node_mut(t).expect("node index not found").set_label(String::from("t"));
|
|
||||||
graph.node_mut(s).expect("node index not found").set_color(Color32::RED);
|
|
||||||
graph.node_mut(t).expect("node index not found").set_color(Color32::GREEN);
|
|
||||||
|
|
||||||
// iterate over all possible edges
|
// iterate over all possible edges
|
||||||
let mut possible_edges: Vec<(NodeIndex, NodeIndex, f32)> = Vec::new();
|
let mut possible_edges: Vec<(NodeIndex, NodeIndex, f32)> = Vec::new();
|
||||||
for pair in nodes.clone().into_iter().combinations(2) {
|
for pair in nodes.clone().into_iter().combinations(2) {
|
||||||
// calculate distance & append tuple (node_a, node_b, distance) to possible_edges
|
// calculate distance & append tuple (node_a, node_b, distance) to possible_edges
|
||||||
let distance = Self::distance(graph.node(pair[0]).expect("node index not found").location(), graph.node(pair[1]).expect("node index not found").location());
|
let distance = Self::distance(
|
||||||
|
*graph.node_weight(pair[0]).expect("node index not found"),
|
||||||
|
*graph.node_weight(pair[1]).expect("node index not found")
|
||||||
|
);
|
||||||
|
//let distance = Self::distance(graph.node(pair[0]).expect("node index not found").location(), graph.node(pair[1]).expect("node index not found").location());
|
||||||
possible_edges.push((pair[0], pair[1], distance));
|
possible_edges.push((pair[0], pair[1], distance));
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort mapping by distance
|
// sort mapping by distance
|
||||||
possible_edges.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap());
|
possible_edges.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap());
|
||||||
println!("possible_edges len: {}", possible_edges.len());
|
|
||||||
|
|
||||||
// insert the edge if possible
|
// insert the edge if possible
|
||||||
let mut inserted_edges: Vec<(NodeIndex, NodeIndex)> = Vec::new();
|
let mut inserted_edges: Vec<(NodeIndex, NodeIndex)> = Vec::new();
|
||||||
@@ -57,10 +57,10 @@ impl MaxflowProblem {
|
|||||||
let mut intersects = false;
|
let mut intersects = false;
|
||||||
for (node3, node4) in &inserted_edges {
|
for (node3, node4) in &inserted_edges {
|
||||||
if Self::intersects(
|
if Self::intersects(
|
||||||
graph.node(node1).expect("node index not found").location(),
|
*graph.node_weight(node1).expect("node index not found"),
|
||||||
graph.node(node2).expect("node index not found").location(),
|
*graph.node_weight(node2).expect("node index not found"),
|
||||||
graph.node(*node3).expect("node index not found").location(),
|
*graph.node_weight(*node3).expect("node index not found"),
|
||||||
graph.node(*node4).expect("node index not found").location(),
|
*graph.node_weight(*node4).expect("node index not found"),
|
||||||
) {
|
) {
|
||||||
intersects = true;
|
intersects = true;
|
||||||
break;
|
break;
|
||||||
@@ -71,29 +71,54 @@ impl MaxflowProblem {
|
|||||||
inserted_edges.push((node1, node2));
|
inserted_edges.push((node1, node2));
|
||||||
inserted_edges.push((node2, node1));
|
inserted_edges.push((node2, node1));
|
||||||
let capacity: u64 = rng.gen_range(1..=max_capacity);
|
let capacity: u64 = rng.gen_range(1..=max_capacity);
|
||||||
graph.add_edge_with_label(node1, node2, capacity, capacity.to_string());
|
graph.add_edge(node1, node2, (0, capacity));
|
||||||
graph.add_edge_with_label(node2, node1, capacity, capacity.to_string());
|
graph.add_edge(node2, node1, (0, capacity));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Self { g: graph, s: s, t: t }
|
Self { g: graph, s: s, t: t }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from(g: StableGraph<(f32, f32), (u64, u64)>, s: NodeIndex, t: NodeIndex) -> Self{
|
||||||
|
Self {g: g, s: s, t: t}
|
||||||
|
}
|
||||||
|
|
||||||
// returns the eudclidean distance between two points
|
// returns the eudclidean distance between two points
|
||||||
fn distance(a: Pos2, b: Pos2) -> f32 {
|
fn distance(a: (f32, f32), b: (f32, f32)) -> f32 {
|
||||||
let delta_x = a.x - b.x;
|
let delta_x = a.0 - b.0;
|
||||||
let delta_y = a.y - b.y;
|
let delta_y = a.1 - b.1;
|
||||||
return f32::sqrt(delta_x.powf(2.) + delta_y.powf(2.));
|
return f32::sqrt(delta_x.powf(2.) + delta_y.powf(2.));
|
||||||
}
|
}
|
||||||
|
|
||||||
// checks if the lines between a-b and c-d intersect
|
// checks if the lines between a-b and c-d intersect
|
||||||
fn intersects(a: Pos2, b: Pos2, c: Pos2, d: Pos2) -> bool {
|
fn intersects(a: (f32, f32), b: (f32, f32), c: (f32, f32), d: (f32, f32)) -> bool {
|
||||||
let line1 = Line::new(coord! {x: a.x, y: a.y }, coord! {x: b.x, y: b.y } );
|
let line1 = Line::new(coord! {x: a.0, y: a.1 }, coord! {x: b.0, y: b.1 } );
|
||||||
let line2 = Line::new(coord! {x: c.x, y: c.y }, coord! {x: d.x, y: d.y } );
|
let line2 = Line::new(coord! {x: c.0, y: c.1 }, coord! {x: d.0, y: d.1 } );
|
||||||
match line_intersection(line1, line2) {
|
match line_intersection(line1, line2) {
|
||||||
// only return true for 'proper' intersections which are not at the end of a line
|
// only return true for 'proper' intersections which are not at the end of a line
|
||||||
Some(LineIntersection::SinglePoint { is_proper: true, .. }) => true,
|
Some(LineIntersection::SinglePoint { is_proper: true, .. }) => true,
|
||||||
_ => false
|
_ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_gui_graph(&self) -> Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape> {
|
||||||
|
let mut graph = to_graph_custom(&self.g,
|
||||||
|
|n| {
|
||||||
|
let (x, y) = *n.payload();
|
||||||
|
default_node_transform(n);
|
||||||
|
n.set_location(Pos2::new(x, y));
|
||||||
|
n.set_label(String::from(""));
|
||||||
|
},
|
||||||
|
|e| {
|
||||||
|
let (flow, capacity): (u64, u64) = *e.payload();
|
||||||
|
default_edge_transform(e);
|
||||||
|
e.set_label(format!("{flow}:{capacity}"));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
graph.node_mut(self.s).expect("node index not found").set_label(String::from("s"));
|
||||||
|
graph.node_mut(self.t).expect("node index not found").set_label(String::from("t"));
|
||||||
|
graph.node_mut(self.s).expect("node index not found").set_color(Color32::RED);
|
||||||
|
graph.node_mut(self.t).expect("node index not found").set_color(Color32::GREEN);
|
||||||
|
graph
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user