Skip to content

Commit

Permalink
feat: Full animation for kps, upper and lower hull (bug: in lower hul…
Browse files Browse the repository at this point in the history
…l the animation is little messed up_
  • Loading branch information
Saphereye committed Mar 24, 2024
1 parent 59228d3 commit b1b4a75
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 47 deletions.
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,11 @@ bevy_egui = "0.25.0"
bevy_pancam = "0.11.0"
egui_extras = "0.26.2"
rand = "0.8.5"

# Enable a small amount of optimization in debug mode
[profile.dev]
opt-level = 1

# Enable high optimizations for dependencies (incl. Bevy), but not for our code:
[profile.dev.package."*"]
opt-level = 3
140 changes: 120 additions & 20 deletions src/algorithms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub enum LineType {
PartOfHull(Vec2, Vec2),
Temporary(Vec2, Vec2),
TextComment(String),
VerticalLine(f32), // Draws a vertical line at x
}

/// # Implementation of the [Jarvis March](https://en.wikipedia.org/wiki/Gift_wrapping_algorithm) algorithm (Gift-Wrapping algorithm)
Expand Down Expand Up @@ -110,7 +111,7 @@ pub fn jarvis_march(points: Vec<Vec2>, drawing_history: &mut Vec<Vec<LineType>>)

temp.push(LineType::TextComment(format!(
"Checking all points starting from {} that are least counter clockwise",
points[p].to_string()
points[p]
)));
drawing_history.push(temp);
}
Expand Down Expand Up @@ -244,21 +245,48 @@ pub fn kirk_patrick_seidel(
drawing_history: &mut Vec<Vec<LineType>>,
) -> Vec<Vec2> {
let upper_hull_vec = upper_hull(points.clone(), drawing_history);
let mut upper_hull_history = vec![];
for (index, _) in upper_hull_vec.iter().enumerate() {
let j = (index + 1) % upper_hull_vec.len();
if j == 0 {
continue;
};
upper_hull_history.push(LineType::PartOfHull(
upper_hull_vec[index],
upper_hull_vec[j],
));
}
upper_hull_history.push(LineType::TextComment(
"Upper Hull has been found".to_string(),
));
drawing_history.push(upper_hull_history);

let mirrored_points: Vec<Vec2> = points.into_iter().map(|p| Vec2::new(p.x, -p.y)).collect();
let mut lower_hull = upper_hull(mirrored_points, drawing_history);
lower_hull.iter_mut().for_each(|p| p.y = -p.y);
let mut lower_hull_history = vec![];
let lower_hull = lower_hull(mirrored_points, drawing_history);
// lower_hull.iter_mut().for_each(|p| p.y = -p.y);
for (index, _) in lower_hull.iter().enumerate() {
let j = (index + 1) % lower_hull.len();
if j == 0 {
continue;
};
lower_hull_history.push(LineType::PartOfHull(lower_hull[index], lower_hull[j]));
}
lower_hull_history.push(LineType::TextComment(
"Lower Hull has been found".to_string(),
));
drawing_history.push(lower_hull_history);

let mut convex_hull = upper_hull_vec;
convex_hull.extend(lower_hull.into_iter().rev());
drawing_history.push(vec![LineType::TextComment(
"Merging the upper and lower hulls".to_string(),
)]);

let mut temp = vec![];
for i in 0..convex_hull.len() {
let j = (i + 1) % convex_hull.len();
temp.push(LineType::PartOfHull(convex_hull[i], convex_hull[j]));
}
drawing_history.push(vec![LineType::TextComment(
"Convex Hull has been found".to_string(),
)]);

drawing_history.push(temp);
let mut convex_hull = upper_hull_vec;
convex_hull.extend(lower_hull.into_iter().rev());

convex_hull
}
Expand All @@ -269,24 +297,33 @@ fn upper_hull(points: Vec<Vec2>, drawing_history: &mut Vec<Vec<LineType>>) -> Ve
}

let x_cor_median = median_of_medians(&points).x;
drawing_history.push(vec![
LineType::VerticalLine(x_cor_median),
LineType::TextComment(format!("Median of x-coordinates is {}", x_cor_median)),
]);
let (left, right): (Vec<Vec2>, Vec<Vec2>) = points
.clone()
.into_iter()
.partition(|point| point.x <= x_cor_median);

println!(
"Points:{:?}, Left: {:?}, Right: {:?}, Median: {}",
points, left, right, x_cor_median
);
// println!(
// "Points:{:?}, Left: {:?}, Right: {:?}, Median: {}",
// points, left, right, x_cor_median
// );

let upper_bridge = upper_bridge(&left, &right, x_cor_median, drawing_history);
drawing_history.push(vec![
LineType::PartOfHull(upper_bridge.0, upper_bridge.1),
LineType::TextComment("Upper Bridge has been found".to_string()),
]);

let mut left_hull = upper_hull(
left.into_iter()
.filter(|point| point.x <= upper_bridge.0.x)
.collect(),
drawing_history,
);

let right_hull = upper_hull(
right
.into_iter()
Expand All @@ -305,7 +342,7 @@ fn upper_bridge(
left: &[Vec2],
right: &[Vec2],
reference_median: f32,
drawing_history: &mut Vec<Vec<LineType>>,
drawing_history: &mut [Vec<LineType>],
) -> (Vec2, Vec2) {
let mut max_intersection = f32::MIN;
let mut max_points = (Vec2::default(), Vec2::default());
Expand All @@ -324,6 +361,71 @@ fn upper_bridge(
max_points
}

pub fn lower_hull(points: Vec<Vec2>, drawing_history: &mut Vec<Vec<LineType>>) -> Vec<Vec2> {
if points.len() < 3 {
return points;
}

let x_cor_median = median_of_medians(&points).x;
drawing_history.push(vec![
LineType::VerticalLine(x_cor_median),
LineType::TextComment(format!("Median of x-coordinates is {}", x_cor_median)),
]);
let (left, right): (Vec<Vec2>, Vec<Vec2>) = points
.clone()
.into_iter()
.partition(|point| point.x <= x_cor_median);

let lower_bridge = lower_bridge(&left, &right, x_cor_median, drawing_history);
drawing_history.push(vec![
LineType::PartOfHull(lower_bridge.0, lower_bridge.1),
LineType::TextComment("Lower Bridge has been found".to_string()),
]);

let mut left_hull = lower_hull(
left.into_iter()
.filter(|point| point.x <= lower_bridge.0.x)
.collect(),
drawing_history,
);

let right_hull = lower_hull(
right
.into_iter()
.filter(|point| point.x >= lower_bridge.1.x)
.collect(),
drawing_history,
);

// Merge the left and right hulls
left_hull.extend(right_hull);

left_hull
}

fn lower_bridge(
left: &[Vec2],
right: &[Vec2],
reference_median: f32,
drawing_history: &mut [Vec<LineType>],
) -> (Vec2, Vec2) {
let mut min_intersection = f32::MAX;
let mut min_points = (Vec2::default(), Vec2::default());

for left_point in left {
for right_point in right {
let intersection = calculate_intersection(left_point, right_point, reference_median);

if intersection < min_intersection {
min_intersection = intersection;
min_points = (*left_point, *right_point);
}
}
}

min_points
}

fn calculate_intersection(a: &Vec2, b: &Vec2, reference_median: f32) -> f32 {
// Calculate the slope of the line
let slope = (b.y - a.y) / (b.x - a.x);
Expand All @@ -332,13 +434,11 @@ fn calculate_intersection(a: &Vec2, b: &Vec2, reference_median: f32) -> f32 {
let y_intercept = a.y - slope * a.x;

// Calculate the y-coordinate of the intersection of the line with the vertical line at reference_median
let intersection = slope * reference_median + y_intercept;

intersection
slope * reference_median + y_intercept
}

pub fn median_of_medians<T: AsRef<[Vec2]>>(nums: T) -> Vec2 {
warn!("Median of medians is wrongly implemented");
warn_once!("Median of medians is wrongly implemented");
let mut nums = nums.as_ref().to_vec();
nums.sort_by(|a, b| a.x.partial_cmp(&b.x).unwrap());
nums[nums.len() / 2]
Expand Down
85 changes: 58 additions & 27 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! # Convex Hull Simulation
//! A rust based step by step simulation of Jarvis March and Kirk Patrick Seidel algorithms for convex hull generation.
//! The program uses [Bevy](https://bevyengine.org) as the game engine and [egui](https://github.com/emilk/egui) for the ui library.
//!
//!
//! ## What is a convex hull?
//! The convex hull of a finite point set S in the plane is the smallest
//! convex polygon containing the set. The vertices (corners) of this polygon must be
Expand All @@ -16,9 +16,14 @@
use std::fmt::Debug;

use bevy::{
prelude::*, sprite::{MaterialMesh2dBundle, Mesh2dHandle}
prelude::*,
sprite::{MaterialMesh2dBundle, Mesh2dHandle},
window::{PrimaryWindow, Window},
};
use bevy_egui::{
egui::{self},
EguiContexts, EguiPlugin,
};
use bevy_egui::{egui::{self}, EguiContexts, EguiPlugin};
use bevy_pancam::{PanCam, PanCamPlugin};
use egui_extras::{Column, TableBuilder};

Expand Down Expand Up @@ -87,6 +92,7 @@ fn main() {
)))
.insert_resource(DrawingHistory(vec![], 0))
.insert_resource(Algorithm(AlgorithmType::JarvisMarch))
.insert_resource(TextComment)
.run();
}

Expand All @@ -111,22 +117,13 @@ use bevy::render::render_asset::RenderAssetUsages;
use bevy::render::render_resource::PrimitiveTopology;

macro_rules! draw_lines {
($commands:expr, $meshes:expr, $materials:expr, $line:expr, Gizmo) => {
$commands.spawn((
MaterialMesh2dBundle {
mesh: Mesh2dHandle(
$meshes.add(
Mesh::new(PrimitiveTopology::LineStrip, RenderAssetUsages::default())
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, $line),
),
),
material: $materials.add(Color::rgb(0.44, 0.44, 0.44)),
..default()
},
Gizmo,
));
};
($commands:expr, $meshes:expr, $materials:expr, $line:expr, ConvexHull) => {
($commands:expr, $meshes:expr, $materials:expr, $line:expr, $line_type:tt) => {
let color = match stringify!($line_type) {
"Gizmo" => Color::rgb(0.44, 0.44, 0.44),
"ConvexHull" => Color::rgb(1.0, 1.0, 1.0),
_ => Color::rgb(0.5, 0.5, 0.5), // Default color
};

$commands.spawn((
MaterialMesh2dBundle {
mesh: Mesh2dHandle(
Expand All @@ -135,10 +132,10 @@ macro_rules! draw_lines {
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, $line),
),
),
material: $materials.add(Color::rgb(1.0, 1.0, 1.0)),
material: $materials.add(color),
..default()
},
ConvexHull,
$line_type,
));
};
}
Expand All @@ -151,8 +148,10 @@ fn graphics_drawing(
mut simulation_timer: ResMut<SimulationTimer>,
gizmo_query: Query<Entity, With<Gizmo>>,
text_query: Query<Entity, With<ColorText>>,
mut drawing_history: ResMut<DrawingHistory>
mut drawing_history: ResMut<DrawingHistory>,
mut window: Query<&mut Window, With<PrimaryWindow>>,
) {
let window = window.single();
if drawing_history.0.is_empty() || drawing_history.0.len() == drawing_history.1 {
// despawn_entities(&mut commands, &gizmo_query);
return;
Expand All @@ -169,17 +168,29 @@ fn graphics_drawing(
for i in &drawing_history.0[drawing_history.1] {
match i {
LineType::PartOfHull(a, b) => {
draw_lines!(commands, meshes, materials, vec![[a.x, a.y, 0.0], [b.x, b.y, 0.0]], ConvexHull);
},
draw_lines!(
commands,
meshes,
materials,
vec![[a.x, a.y, 0.0], [b.x, b.y, 0.0]],
ConvexHull
);
}
LineType::Temporary(a, b) => {
draw_lines!(commands, meshes, materials, vec![[a.x, a.y, 0.0], [b.x, b.y, 0.0]], Gizmo);
draw_lines!(
commands,
meshes,
materials,
vec![[a.x, a.y, 0.0], [b.x, b.y, 0.0]],
Gizmo
);
}
LineType::TextComment(comment) => {
commands.spawn((
TextBundle::from_section(
comment,
TextStyle {
font_size: 50.0,
font_size: 20.0,
..default()
},
)
Expand All @@ -193,6 +204,27 @@ fn graphics_drawing(
ColorText,
));
}
LineType::VerticalLine(x) => {
commands.spawn((
MaterialMesh2dBundle {
mesh: Mesh2dHandle(
meshes.add(
Mesh::new(
PrimitiveTopology::LineStrip,
RenderAssetUsages::default(),
)
.with_inserted_attribute(
Mesh::ATTRIBUTE_POSITION,
vec![[*x, -window.height(), 0.0], [*x, window.height(), 0.0]],
),
),
),
material: materials.add(Color::rgb(0.5, 0.5, 0.5)),
..default()
},
Gizmo,
));
}
}
}

Expand Down Expand Up @@ -310,7 +342,6 @@ fn ui(
despawn_entities(&mut commands, &convex_hull_query);
despawn_entities(&mut commands, &gizmo_query);
let points = point_data.0.clone();

match algorithm.0 {
AlgorithmType::JarvisMarch => jarvis_march(points, &mut drawing_history.0),
AlgorithmType::KirkPatrickSeidel => kirk_patrick_seidel(points, &mut drawing_history.0),
Expand Down

0 comments on commit b1b4a75

Please sign in to comment.