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,
|
||||
// Edmonds-Karp (also Ford-Fulkerson mit BFS statt DFS),
|
||||
// Dinic
|
||||
// Goldberg-Tarjan (auch Preflow-Push oder Push-Relabel genannt).
|
||||
use std::cmp::min;
|
||||
use std::collections::VecDeque;
|
||||
use egui_graphs::{Graph, DefaultNodeShape, DefaultEdgeShape};
|
||||
use petgraph::adj::EdgeIndex;
|
||||
use petgraph::data::Build;
|
||||
@@ -15,7 +13,6 @@ use petgraph::visit::{Dfs, EdgeRef, NodeRef, VisitMap, Visitable};
|
||||
use petgraph::stable_graph::{StableGraph, NodeIndex};
|
||||
|
||||
use crate::random_generator::MaxflowProblem;
|
||||
use crate::graph::{MaxflowNode, MaxflowEdge};
|
||||
//mod random_generator;
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
pub struct FordFulkerson {
|
||||
problem: MaxflowProblem,
|
||||
flows: Graph<(), u64, Directed, u32, DefaultNodeShape, DefaultEdgeShape>,
|
||||
//pub struct FordFulkerson {
|
||||
// problem: MaxflowProblem,
|
||||
// flows: Graph<(), u64, Directed, u32, DefaultNodeShape, DefaultEdgeShape>,
|
||||
// alertnatively, use a map from EdgeIndex to flow value
|
||||
//dfs: Dfs<N, VM>,
|
||||
}
|
||||
//}
|
||||
|
||||
/* impl FordFulkerson {
|
||||
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 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 layout::CustomEdgeShape;
|
||||
use crate::algorithms::ford_fulkerson;
|
||||
|
||||
mod random_generator;
|
||||
mod algorithms;
|
||||
mod graph;
|
||||
mod layout;
|
||||
|
||||
pub struct MaxflowApp {
|
||||
g: Graph<(f32, f32), (u64, u64)>,
|
||||
g: Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>,
|
||||
p: MaxflowProblem,
|
||||
node_count: u64,
|
||||
max_capacity: u64,
|
||||
@@ -26,7 +29,7 @@ impl App for MaxflowApp {
|
||||
fn update(&mut self, ctx: &Context, _: &mut Frame) {
|
||||
ctx.set_theme(egui::Theme::Light);
|
||||
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")
|
||||
.min_width(200.)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use petgraph::stable_graph::{StableGraph, NodeIndex};
|
||||
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 rand::thread_rng;
|
||||
use rand::Rng;
|
||||
@@ -9,15 +9,18 @@ use itertools::Itertools;
|
||||
use geo::{Line, coord, line_intersection::{line_intersection, LineIntersection}};
|
||||
use egui::Color32;
|
||||
|
||||
use crate::layout::CustomEdgeShape;
|
||||
|
||||
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 t: NodeIndex,
|
||||
}
|
||||
|
||||
impl MaxflowProblem {
|
||||
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 rng = thread_rng();
|
||||
|
||||
@@ -25,30 +28,27 @@ impl MaxflowProblem {
|
||||
for _i in 0..num_nodes {
|
||||
let x: f32 = rng.gen_range(0.0..=400.0);
|
||||
let y: f32 = rng.gen_range(0.0..=300.0);
|
||||
let pos = Pos2::new(x,y);
|
||||
let n = graph.add_node_with_label_and_location((), String::new(), pos);
|
||||
let n = graph.add_node((x,y));
|
||||
nodes.push(n);
|
||||
}
|
||||
|
||||
// select source (s) and sink (t)
|
||||
let s = *nodes.choose(&mut rng).expect("no nodes found");
|
||||
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);
|
||||
let (s,t) = nodes.choose_multiple(&mut rng, 2).copied().collect_tuple::<(NodeIndex, NodeIndex)>().expect("not enough nodes");
|
||||
|
||||
// iterate over all possible edges
|
||||
let mut possible_edges: Vec<(NodeIndex, NodeIndex, f32)> = Vec::new();
|
||||
for pair in nodes.clone().into_iter().combinations(2) {
|
||||
// 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));
|
||||
}
|
||||
|
||||
// sort mapping by distance
|
||||
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
|
||||
let mut inserted_edges: Vec<(NodeIndex, NodeIndex)> = Vec::new();
|
||||
@@ -57,10 +57,10 @@ impl MaxflowProblem {
|
||||
let mut intersects = false;
|
||||
for (node3, node4) in &inserted_edges {
|
||||
if Self::intersects(
|
||||
graph.node(node1).expect("node index not found").location(),
|
||||
graph.node(node2).expect("node index not found").location(),
|
||||
graph.node(*node3).expect("node index not found").location(),
|
||||
graph.node(*node4).expect("node index not found").location(),
|
||||
*graph.node_weight(node1).expect("node index not found"),
|
||||
*graph.node_weight(node2).expect("node index not found"),
|
||||
*graph.node_weight(*node3).expect("node index not found"),
|
||||
*graph.node_weight(*node4).expect("node index not found"),
|
||||
) {
|
||||
intersects = true;
|
||||
break;
|
||||
@@ -71,29 +71,54 @@ impl MaxflowProblem {
|
||||
inserted_edges.push((node1, node2));
|
||||
inserted_edges.push((node2, node1));
|
||||
let capacity: u64 = rng.gen_range(1..=max_capacity);
|
||||
graph.add_edge_with_label(node1, node2, capacity, capacity.to_string());
|
||||
graph.add_edge_with_label(node2, node1, capacity, capacity.to_string());
|
||||
graph.add_edge(node1, node2, (0, capacity));
|
||||
graph.add_edge(node2, node1, (0, capacity));
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
fn distance(a: Pos2, b: Pos2) -> f32 {
|
||||
let delta_x = a.x - b.x;
|
||||
let delta_y = a.y - b.y;
|
||||
fn distance(a: (f32, f32), b: (f32, f32)) -> f32 {
|
||||
let delta_x = a.0 - b.0;
|
||||
let delta_y = a.1 - b.1;
|
||||
return f32::sqrt(delta_x.powf(2.) + delta_y.powf(2.));
|
||||
}
|
||||
|
||||
// checks if the lines between a-b and c-d intersect
|
||||
fn intersects(a: Pos2, b: Pos2, c: Pos2, d: Pos2) -> bool {
|
||||
let line1 = Line::new(coord! {x: a.x, y: a.y }, coord! {x: b.x, y: b.y } );
|
||||
let line2 = Line::new(coord! {x: c.x, y: c.y }, coord! {x: d.x, y: d.y } );
|
||||
fn intersects(a: (f32, f32), b: (f32, f32), c: (f32, f32), d: (f32, f32)) -> bool {
|
||||
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.0, y: c.1 }, coord! {x: d.0, y: d.1 } );
|
||||
match line_intersection(line1, line2) {
|
||||
// only return true for 'proper' intersections which are not at the end of a line
|
||||
Some(LineIntersection::SinglePoint { is_proper: true, .. }) => true,
|
||||
_ => 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