Created
December 3, 2016 08:28
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Plot a line, | |
/// | |
/// Bresenham's line algorithm, | |
/// See: https://en.wikipedia.org/wiki/Bresenham's_line_algorithm | |
/// StackOverflow: http://stackoverflow.com/a/40902741/432509 | |
fn plot_line_v2v2i<F>( | |
p1: &[i32; 2], | |
p2: &[i32; 2], | |
mut clip_xmin: i32, | |
mut clip_ymin: i32, | |
mut clip_xmax: i32, | |
mut clip_ymax: i32, | |
callback: &mut F) | |
where | |
F: FnMut(i32, i32), | |
{ | |
use ::std::mem::swap; | |
use ::std::cmp::{min, max}; | |
let mut x1 = p1[0]; | |
let mut y1 = p1[1]; | |
let mut x2 = p2[0]; | |
let mut y2 = p2[1]; | |
// Vertical line | |
if x1 == x2 { | |
if x1 < clip_xmin || x1 > clip_xmax { | |
return; | |
} | |
if y1 <= y2 { | |
if y2 < clip_ymin || y1 > clip_ymax { | |
return; | |
} | |
y1 = max(y1, clip_ymin); | |
y2 = min(y2, clip_ymax); | |
for y in y1..(y2 + 1) { | |
callback(x1, y); | |
} | |
} else { | |
if y1 < clip_ymin || y2 > clip_ymax { | |
return; | |
} | |
y1 = min(y1, clip_ymax); | |
y2 = max(y2, clip_ymin); | |
for y in (y2..(y1 + 1)).rev() { | |
callback(x1, y); | |
} | |
} | |
return; | |
} | |
// Horizontal line | |
if y1 == y2 { | |
if y1 < clip_ymin || y1 > clip_ymax { | |
return; | |
} | |
if x1 <= x2 { | |
if x2 < clip_xmin || x1 > clip_xmax { | |
return; | |
} | |
x1 = max(x1, clip_xmin); | |
x2 = min(x2, clip_xmax); | |
for x in x1..(x2 + 1) { | |
callback(x, y1); | |
} | |
} else { | |
if x1 < clip_xmin || x2 > clip_xmax { | |
return; | |
} | |
x1 = min(x1, clip_xmax); | |
x2 = max(x2, clip_xmin); | |
for x in x2..(x1 + 1) { | |
callback(x, y1); | |
} | |
} | |
return; | |
} | |
// Now simple cases are handled, perform clipping checks. | |
let sign_x; | |
let sign_y; | |
if x1 < x2 { | |
if x1 > clip_xmax || x2 < clip_xmin { | |
return; | |
} | |
sign_x = 1; | |
} else { | |
if x2 > clip_xmax || x1 < clip_xmin { | |
return; | |
} | |
sign_x = -1; | |
// Invert sign, invert again right before plotting. | |
x1 = -x1; | |
x2 = -x2; | |
clip_xmin = -clip_xmin; | |
clip_xmax = -clip_xmax; | |
swap(&mut clip_xmin, &mut clip_xmax); | |
} | |
if y1 < y2 { | |
if y1 > clip_ymax || y2 < clip_ymin { | |
return; | |
} | |
sign_y = 1; | |
} else { | |
if y2 > clip_ymax || y1 < clip_ymin { | |
return; | |
} | |
sign_y = -1; | |
// Invert sign, invert again right before plotting. | |
y1 = -y1; | |
y2 = -y2; | |
clip_ymin = -clip_ymin; | |
clip_ymax = -clip_ymax; | |
swap(&mut clip_ymin, &mut clip_ymax); | |
} | |
let delta_x = x2 - x1; | |
let delta_y = y2 - y1; | |
let mut delta_x_step = 2 * delta_x; | |
let mut delta_y_step = 2 * delta_y; | |
// Plotting values | |
let mut x_pos = x1; | |
let mut y_pos = y1; | |
if delta_x >= delta_y { | |
let mut error = delta_y_step - delta_x; | |
let mut set_exit = false; | |
// Line starts below the clip window. | |
if y1 < clip_ymin { | |
let temp = (2 * (clip_ymin - y1) - 1) * delta_x; | |
let msd = temp / delta_y_step; | |
x_pos += msd; | |
// Line misses the clip window entirely. | |
if x_pos > clip_xmax { | |
return; | |
} | |
// Line starts. | |
if x_pos >= clip_xmin { | |
let rem = temp - msd * delta_y_step; | |
y_pos = clip_ymin; | |
error -= rem + delta_x; | |
if rem > 0 { | |
x_pos += 1; | |
error += delta_y_step | |
} | |
set_exit = true; | |
} | |
} | |
// Line starts left of the clip window. | |
if !set_exit && x1 < clip_xmin { | |
let temp = delta_y_step * (clip_xmin - x1); | |
let msd = temp / delta_x_step; | |
y_pos += msd; | |
let rem = temp % delta_x_step; | |
// Line misses clip window entirely. | |
if y_pos > clip_ymax || (y_pos == clip_ymax && rem >= delta_x) { | |
return; | |
} | |
x_pos = clip_xmin; | |
error += rem; | |
if rem >= delta_x { | |
y_pos += 1; | |
error -= delta_x_step; | |
} | |
} | |
let mut x_pos_end = x2; | |
if y2 > clip_ymax { | |
let temp = delta_x_step * (clip_ymax - y1) + delta_x; | |
let msd = temp / delta_y_step; | |
x_pos_end = x1 + msd; | |
if (temp - msd * delta_y_step) == 0 { | |
x_pos_end -= 1; | |
} | |
} | |
x_pos_end = min(x_pos_end, clip_xmax) + 1; | |
if sign_y == -1 { | |
y_pos = -y_pos | |
} | |
if sign_x == -1 { | |
x_pos = -x_pos; | |
x_pos_end = -x_pos_end; | |
} | |
delta_x_step -= delta_y_step; | |
while x_pos != x_pos_end { | |
callback(x_pos, y_pos); | |
if error >= 0 { | |
y_pos += sign_y; | |
error -= delta_x_step; | |
} else { | |
error += delta_y_step; | |
} | |
x_pos += sign_x; | |
} | |
} else { | |
// Line is steep '/' (delta_x < delta_y). | |
// Same as previous block of code with swapped x/y axis. | |
let mut error = delta_x_step - delta_y; | |
let mut set_exit = false; | |
// Line starts left of the clip window. | |
if x1 < clip_xmin { | |
let temp = (2 * (clip_xmin - x1) - 1) * delta_y; | |
let msd = temp / delta_x_step; | |
y_pos += msd; | |
// Line misses the clip window entirely. | |
if y_pos > clip_ymax { | |
return; | |
} | |
// Line starts. | |
if y_pos >= clip_ymin { | |
let rem = temp - msd * delta_x_step; | |
x_pos = clip_xmin; | |
error -= rem + delta_y; | |
if rem > 0 { | |
y_pos += 1; | |
error += delta_x_step; | |
} | |
set_exit = true; | |
} | |
} | |
// Line starts below the clip window. | |
if !set_exit && y1 < clip_ymin { | |
let temp = delta_x_step * (clip_ymin - y1); | |
let msd = temp / delta_y_step; | |
x_pos += msd; | |
let rem = temp % delta_y_step; | |
// Line misses clip window entirely. | |
if x_pos > clip_xmax || (x_pos == clip_xmax && rem >= delta_y) { | |
return; | |
} | |
y_pos = clip_ymin; | |
error += rem; | |
if rem >= delta_y { | |
x_pos += 1; | |
error -= delta_y_step; | |
} | |
} | |
let mut y_pos_end = y2; | |
if x2 > clip_xmax { | |
let temp = delta_y_step * (clip_xmax - x1) + delta_y; | |
let msd = temp / delta_x_step; | |
y_pos_end = y1 + msd; | |
if (temp - msd * delta_x_step) == 0 { | |
y_pos_end -= 1; | |
} | |
} | |
y_pos_end = min(y_pos_end, clip_ymax) + 1; | |
if sign_x == -1 { | |
x_pos = -x_pos; | |
} | |
if sign_y == -1 { | |
y_pos = -y_pos; | |
y_pos_end = -y_pos_end; | |
} | |
delta_y_step -= delta_x_step; | |
while y_pos != y_pos_end { | |
callback(x_pos, y_pos); | |
if error >= 0 { | |
x_pos += sign_x; | |
error -= delta_y_step; | |
} else { | |
error += delta_x_step; | |
} | |
y_pos += sign_y; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment