Skip to content

Commit

Permalink
feat: cubic bezier bounding box
Browse files Browse the repository at this point in the history
  • Loading branch information
Captainfl4me committed May 6, 2024
1 parent 7f2a660 commit 0b1af3e
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 1 deletion.
74 changes: 74 additions & 0 deletions src/bezier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,3 +422,77 @@ pub fn draw_bezier(points: &[impl PointGui], d: &mut RaylibDrawHandle, t: Option
point.draw(d);
}
}

pub fn cubic_bezier_bounding_box(points: &[impl Point]) -> Result<Rectangle, String> {
if points.len() != 4 {
return Err("Cubic Bezier curve needs 4 points".to_string());
}
let p0 = points[0].get_position();
let p1 = points[1].get_position();
let p2 = points[2].get_position();
let p3 = points[3].get_position();

let a = -p0 * 3.0 + p1 * 9.0 - p2 * 9.0 + p3 * 3.0;
let b = p0 * 6.0 - p1 * 12.0 + p2 * 6.0;
let c = -p0 * 3.0 + p1 * 3.0;

let mut bbs = vec![p0, p3]; // Possible bounding box points

if a.x == 0.0 {
if b.x != 0.0 {
let t = -c.x / b.x;
bbs.push(evalute_bezier_curve(points, t));
}
} else {
let x_delta = b.x * b.x - 4.0 * a.x * c.x;
if x_delta >= 0.0 {
let x_t_min = (-b.x - x_delta.sqrt()) / (2.0 * a.x);
let x_t_max = (-b.x + x_delta.sqrt()) / (2.0 * a.x);
bbs.push(evalute_bezier_curve(points, x_t_min.clamp(0.0, 1.0)));
bbs.push(evalute_bezier_curve(points, x_t_max.clamp(0.0, 1.0)));
}
}

if a.y == 0.0 {
if b.y != 0.0 {
let t = -c.y / b.y;
bbs.push(evalute_bezier_curve(points, t));
}
} else {
let y_delta = b.y * b.y - 4.0 * a.y * c.y;
if y_delta >= 0.0 {
let y_t_min = (-b.y - y_delta.sqrt()) / (2.0 * a.y);
let y_t_max = (-b.y + y_delta.sqrt()) / (2.0 * a.y);
bbs.push(evalute_bezier_curve(points, y_t_min.clamp(0.0, 1.0)));
bbs.push(evalute_bezier_curve(points, y_t_max.clamp(0.0, 1.0)));
}
}

let x_min: f32 = bbs
.iter()
.map(|bb| bb.x)
.min_by(|a, b| a.total_cmp(b))
.unwrap();
let x_max: f32 = bbs
.iter()
.map(|bb| bb.x)
.max_by(|a, b| a.total_cmp(b))
.unwrap();
let y_min: f32 = bbs
.iter()
.map(|bb| bb.y)
.min_by(|a, b| a.total_cmp(b))
.unwrap();
let y_max: f32 = bbs
.iter()
.map(|bb| bb.y)
.max_by(|a, b| a.total_cmp(b))
.unwrap();

Ok(Rectangle {
x: x_min,
y: y_min,
width: (x_min - x_max).abs(),
height: (y_min - y_max).abs(),
})
}
26 changes: 25 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ fn bezier_spline_scene(
let mut animation_bounce = false;
let mut has_point_selected = false;
let mut debug_draw = true;
let mut draw_bounding_box = false;
let mut lock_move = true;
let mut clock_divider = 0;
let mut is_closed_loop = false;
Expand All @@ -328,6 +329,7 @@ fn bezier_spline_scene(
let left_slider_text = CStr::from_bytes_with_nul(b"0.0\0").unwrap();
let right_slider_text = CStr::from_bytes_with_nul(b"1.0\0").unwrap();
let animation_toggle_text = CStr::from_bytes_with_nul(b"Animate T value\0").unwrap();
let bounding_box_toggle_text = CStr::from_bytes_with_nul(b"Draw Bouding box\0").unwrap();
let debug_text = CStr::from_bytes_with_nul(b"Activate debug draw\0").unwrap();
let lock_move_text = CStr::from_bytes_with_nul(b"Lock points movement\0").unwrap();
let mut current_draw_time_text = String::new();
Expand Down Expand Up @@ -496,9 +498,14 @@ fn bezier_spline_scene(
Some(animation_toggle_text),
&mut animated,
);
rl_draw_handle.gui_toggle(
Rectangle::new(30.0, 110.0, 200.0, 25.0),
Some(bounding_box_toggle_text),
&mut draw_bounding_box,
);
}
rl_draw_handle.gui_toggle(
Rectangle::new(30.0, if debug_draw { 110.0 } else { 50.0 }, 200.0, 25.0),
Rectangle::new(30.0, if debug_draw { 140.0 } else { 50.0 }, 200.0, 25.0),
Some(lock_move_text),
&mut lock_move,
);
Expand All @@ -514,6 +521,14 @@ fn bezier_spline_scene(
&mut rl_draw_handle,
if debug_draw { Some(t) } else { None },
);
if draw_bounding_box {
if let Ok(bb) = cubic_bezier_bounding_box(&cubic_bezier_points) {
rl_draw_handle.draw_line_v(Vector2::new(bb.x, bb.y), Vector2::new(bb.x + bb.width, bb.y), COLOR_RED);
rl_draw_handle.draw_line_v(Vector2::new(bb.x + bb.width, bb.y), Vector2::new(bb.x + bb.width, bb.y + bb.height), COLOR_RED);
rl_draw_handle.draw_line_v(Vector2::new(bb.x + bb.width, bb.y + bb.height), Vector2::new(bb.x, bb.y + bb.height), COLOR_RED);
rl_draw_handle.draw_line_v(Vector2::new(bb.x, bb.y + bb.height), Vector2::new(bb.x, bb.y), COLOR_RED);
}
}
}
if is_closed_loop {
let cubic_bezier_points = [
Expand All @@ -530,6 +545,15 @@ fn bezier_spline_scene(
&mut rl_draw_handle,
if debug_draw { Some(t) } else { None },
);

if draw_bounding_box {
if let Ok(bb) = cubic_bezier_bounding_box(&cubic_bezier_points) {
rl_draw_handle.draw_line_v(Vector2::new(bb.x, bb.y), Vector2::new(bb.x + bb.width, bb.y), COLOR_RED);
rl_draw_handle.draw_line_v(Vector2::new(bb.x + bb.width, bb.y), Vector2::new(bb.x + bb.width, bb.y + bb.height), COLOR_RED);
rl_draw_handle.draw_line_v(Vector2::new(bb.x + bb.width, bb.y + bb.height), Vector2::new(bb.x, bb.y + bb.height), COLOR_RED);
rl_draw_handle.draw_line_v(Vector2::new(bb.x, bb.y + bb.height), Vector2::new(bb.x, bb.y), COLOR_RED);
}
}
}

if clock_divider == 0 {
Expand Down

0 comments on commit 0b1af3e

Please sign in to comment.