diff options
Diffstat (limited to 'src/rasterizer')
-rw-r--r-- | src/rasterizer/circle.rs | 342 | ||||
-rw-r--r-- | src/rasterizer/line.rs | 123 | ||||
-rw-r--r-- | src/rasterizer/mod.rs | 32 | ||||
-rw-r--r-- | src/rasterizer/path.rs | 115 | ||||
-rw-r--r-- | src/rasterizer/polygon.rs | 242 | ||||
-rw-r--r-- | src/rasterizer/rect.rs | 57 |
6 files changed, 911 insertions, 0 deletions
diff --git a/src/rasterizer/circle.rs b/src/rasterizer/circle.rs new file mode 100644 index 0000000..779f095 --- /dev/null +++ b/src/rasterizer/circle.rs @@ -0,0 +1,342 @@ +use crate::{BackendCoord, BackendStyle, DrawingBackend, DrawingErrorKind}; + +fn draw_part_a< + B: DrawingBackend, + Draw: FnMut(i32, (f64, f64)) -> Result<(), DrawingErrorKind<B::ErrorType>>, +>( + height: f64, + radius: u32, + mut draw: Draw, +) -> Result<(), DrawingErrorKind<B::ErrorType>> { + let half_width = (radius as f64 * radius as f64 + - (radius as f64 - height) * (radius as f64 - height)) + .sqrt(); + + let x0 = (-half_width).ceil() as i32; + let x1 = half_width.floor() as i32; + + let y0 = (radius as f64 - height).ceil(); + + for x in x0..=x1 { + let y1 = (radius as f64 * radius as f64 - x as f64 * x as f64).sqrt(); + check_result!(draw(x, (y0, y1))); + } + + Ok(()) +} + +fn draw_part_b< + B: DrawingBackend, + Draw: FnMut(i32, (f64, f64)) -> Result<(), DrawingErrorKind<B::ErrorType>>, +>( + from: f64, + size: f64, + mut draw: Draw, +) -> Result<(), DrawingErrorKind<B::ErrorType>> { + let from = from.floor(); + for x in (from - size).floor() as i32..=from as i32 { + check_result!(draw(x, (-x as f64, x as f64))); + } + Ok(()) +} + +fn draw_part_c< + B: DrawingBackend, + Draw: FnMut(i32, (f64, f64)) -> Result<(), DrawingErrorKind<B::ErrorType>>, +>( + r: i32, + r_limit: i32, + mut draw: Draw, +) -> Result<(), DrawingErrorKind<B::ErrorType>> { + let half_size = r as f64 / (2f64).sqrt(); + + let (x0, x1) = ((-half_size).ceil() as i32, half_size.floor() as i32); + + for x in x0..x1 { + let outter_y0 = ((r_limit as f64) * (r_limit as f64) - x as f64 * x as f64).sqrt(); + let inner_y0 = r as f64 - 1.0; + let mut y1 = outter_y0.min(inner_y0); + let y0 = ((r as f64) * (r as f64) - x as f64 * x as f64).sqrt(); + + if y0 > y1 { + y1 = y0.ceil(); + if y1 >= r as f64 { + continue; + } + } + + check_result!(draw(x, (y0, y1))); + } + + for x in x1 + 1..r { + let outter_y0 = ((r_limit as f64) * (r_limit as f64) - x as f64 * x as f64).sqrt(); + let inner_y0 = r as f64 - 1.0; + let y0 = outter_y0.min(inner_y0); + let y1 = x as f64; + + if y1 < y0 { + check_result!(draw(x, (y0, y1 + 1.0))); + check_result!(draw(-x, (y0, y1 + 1.0))); + } + } + + Ok(()) +} + +fn draw_sweep_line<B: DrawingBackend, S: BackendStyle>( + b: &mut B, + style: &S, + (x0, y0): BackendCoord, + (dx, dy): (i32, i32), + p0: i32, + (s, e): (f64, f64), +) -> Result<(), DrawingErrorKind<B::ErrorType>> { + let mut s = if dx < 0 || dy < 0 { -s } else { s }; + let mut e = if dx < 0 || dy < 0 { -e } else { e }; + if s > e { + std::mem::swap(&mut s, &mut e); + } + + let vs = s.ceil() - s; + let ve = e - e.floor(); + + if dx == 0 { + check_result!(b.draw_line( + (p0 + x0, s.ceil() as i32 + y0), + (p0 + x0, e.floor() as i32 + y0), + &style.color() + )); + check_result!(b.draw_pixel((p0 + x0, s.ceil() as i32 + y0 - 1), style.color().mix(vs))); + check_result!(b.draw_pixel((p0 + x0, e.floor() as i32 + y0 + 1), style.color().mix(ve))); + } else { + check_result!(b.draw_line( + (s.ceil() as i32 + x0, p0 + y0), + (e.floor() as i32 + x0, p0 + y0), + &style.color() + )); + check_result!(b.draw_pixel((s.ceil() as i32 + x0 - 1, p0 + y0), style.color().mix(vs))); + check_result!(b.draw_pixel((e.floor() as i32 + x0 + 1, p0 + y0), style.color().mix(ve))); + } + + Ok(()) +} + +fn draw_annulus<B: DrawingBackend, S: BackendStyle>( + b: &mut B, + center: BackendCoord, + radius: (u32, u32), + style: &S, +) -> Result<(), DrawingErrorKind<B::ErrorType>> { + let a0 = ((radius.0 - radius.1) as f64).min(radius.0 as f64 * (1.0 - 1.0 / (2f64).sqrt())); + let a1 = (radius.0 as f64 - a0 - radius.1 as f64).max(0.0); + + check_result!(draw_part_a::<B, _>(a0, radius.0, |p, r| draw_sweep_line( + b, + style, + center, + (0, 1), + p, + r + ))); + check_result!(draw_part_a::<B, _>(a0, radius.0, |p, r| draw_sweep_line( + b, + style, + center, + (0, -1), + p, + r + ))); + check_result!(draw_part_a::<B, _>(a0, radius.0, |p, r| draw_sweep_line( + b, + style, + center, + (1, 0), + p, + r + ))); + check_result!(draw_part_a::<B, _>(a0, radius.0, |p, r| draw_sweep_line( + b, + style, + center, + (-1, 0), + p, + r + ))); + + if a1 > 0.0 { + check_result!(draw_part_b::<B, _>( + radius.0 as f64 - a0, + a1.floor(), + |h, (f, t)| { + let h = h as i32; + let f = f as i32; + let t = t as i32; + check_result!(b.draw_line( + (center.0 + h, center.1 + f), + (center.0 + h, center.1 + t), + &style.color() + )); + check_result!(b.draw_line( + (center.0 - h, center.1 + f), + (center.0 - h, center.1 + t), + &style.color() + )); + + check_result!(b.draw_line( + (center.0 + f + 1, center.1 + h), + (center.0 + t - 1, center.1 + h), + &style.color() + )); + check_result!(b.draw_line( + (center.0 + f + 1, center.1 - h), + (center.0 + t - 1, center.1 - h), + &style.color() + )); + + Ok(()) + } + )); + } + + check_result!(draw_part_c::<B, _>( + radius.1 as i32, + radius.0 as i32, + |p, r| draw_sweep_line(b, style, center, (0, 1), p, r) + )); + check_result!(draw_part_c::<B, _>( + radius.1 as i32, + radius.0 as i32, + |p, r| draw_sweep_line(b, style, center, (0, -1), p, r) + )); + check_result!(draw_part_c::<B, _>( + radius.1 as i32, + radius.0 as i32, + |p, r| draw_sweep_line(b, style, center, (1, 0), p, r) + )); + check_result!(draw_part_c::<B, _>( + radius.1 as i32, + radius.0 as i32, + |p, r| draw_sweep_line(b, style, center, (-1, 0), p, r) + )); + + let d_inner = ((radius.1 as f64) / (2f64).sqrt()) as i32; + let d_outter = (((radius.0 as f64) / (2f64).sqrt()) as i32).min(radius.1 as i32 - 1); + let d_outter_actually = (radius.1 as i32).min( + (radius.0 as f64 * radius.0 as f64 - radius.1 as f64 * radius.1 as f64 / 2.0) + .sqrt() + .ceil() as i32, + ); + + check_result!(b.draw_line( + (center.0 - d_inner, center.1 - d_inner), + (center.0 - d_outter, center.1 - d_outter), + &style.color() + )); + check_result!(b.draw_line( + (center.0 + d_inner, center.1 - d_inner), + (center.0 + d_outter, center.1 - d_outter), + &style.color() + )); + check_result!(b.draw_line( + (center.0 - d_inner, center.1 + d_inner), + (center.0 - d_outter, center.1 + d_outter), + &style.color() + )); + check_result!(b.draw_line( + (center.0 + d_inner, center.1 + d_inner), + (center.0 + d_outter, center.1 + d_outter), + &style.color() + )); + + check_result!(b.draw_line( + (center.0 - d_inner, center.1 + d_inner), + (center.0 - d_outter_actually, center.1 + d_inner), + &style.color() + )); + check_result!(b.draw_line( + (center.0 + d_inner, center.1 - d_inner), + (center.0 + d_inner, center.1 - d_outter_actually), + &style.color() + )); + check_result!(b.draw_line( + (center.0 + d_inner, center.1 + d_inner), + (center.0 + d_inner, center.1 + d_outter_actually), + &style.color() + )); + check_result!(b.draw_line( + (center.0 + d_inner, center.1 + d_inner), + (center.0 + d_outter_actually, center.1 + d_inner), + &style.color() + )); + + Ok(()) +} + +pub fn draw_circle<B: DrawingBackend, S: BackendStyle>( + b: &mut B, + center: BackendCoord, + mut radius: u32, + style: &S, + mut fill: bool, +) -> Result<(), DrawingErrorKind<B::ErrorType>> { + if style.color().alpha == 0.0 { + return Ok(()); + } + + if !fill && style.stroke_width() != 1 { + let inner_radius = radius - (style.stroke_width() / 2).min(radius); + radius += style.stroke_width() / 2; + if inner_radius > 0 { + return draw_annulus(b, center, (radius, inner_radius), style); + } else { + fill = true; + } + } + + let min = (f64::from(radius) * (1.0 - (2f64).sqrt() / 2.0)).ceil() as i32; + let max = (f64::from(radius) * (1.0 + (2f64).sqrt() / 2.0)).floor() as i32; + + let range = min..=max; + + let (up, down) = ( + range.start() + center.1 - radius as i32, + range.end() + center.1 - radius as i32, + ); + + for dy in range { + let dy = dy - radius as i32; + let y = center.1 + dy; + + let lx = (f64::from(radius) * f64::from(radius) + - (f64::from(dy) * f64::from(dy)).max(1e-5)) + .sqrt(); + + let left = center.0 - lx.floor() as i32; + let right = center.0 + lx.floor() as i32; + + let v = lx - lx.floor(); + + let x = center.0 + dy; + let top = center.1 - lx.floor() as i32; + let bottom = center.1 + lx.floor() as i32; + + if fill { + check_result!(b.draw_line((left, y), (right, y), &style.color())); + check_result!(b.draw_line((x, top), (x, up), &style.color())); + check_result!(b.draw_line((x, down), (x, bottom), &style.color())); + } else { + check_result!(b.draw_pixel((left, y), style.color().mix(1.0 - v))); + check_result!(b.draw_pixel((right, y), style.color().mix(1.0 - v))); + + check_result!(b.draw_pixel((x, top), style.color().mix(1.0 - v))); + check_result!(b.draw_pixel((x, bottom), style.color().mix(1.0 - v))); + } + + check_result!(b.draw_pixel((left - 1, y), style.color().mix(v))); + check_result!(b.draw_pixel((right + 1, y), style.color().mix(v))); + check_result!(b.draw_pixel((x, top - 1), style.color().mix(v))); + check_result!(b.draw_pixel((x, bottom + 1), style.color().mix(v))); + } + + Ok(()) +} diff --git a/src/rasterizer/line.rs b/src/rasterizer/line.rs new file mode 100644 index 0000000..0f24b0a --- /dev/null +++ b/src/rasterizer/line.rs @@ -0,0 +1,123 @@ +use crate::{BackendCoord, BackendStyle, DrawingBackend, DrawingErrorKind}; + +pub fn draw_line<DB: DrawingBackend, S: BackendStyle>( + back: &mut DB, + mut from: BackendCoord, + mut to: BackendCoord, + style: &S, +) -> Result<(), DrawingErrorKind<DB::ErrorType>> { + if style.color().alpha == 0.0 { + return Ok(()); + } + + if style.stroke_width() != 1 { + // If the line is wider than 1px, then we need to make it a polygon + let v = (i64::from(to.0 - from.0), i64::from(to.1 - from.1)); + let l = ((v.0 * v.0 + v.1 * v.1) as f64).sqrt(); + + if l < 1e-5 { + return Ok(()); + } + + let v = (v.0 as f64 / l, v.1 as f64 / l); + + let r = f64::from(style.stroke_width()) / 2.0; + let mut trans = [(v.1 * r, -v.0 * r), (-v.1 * r, v.0 * r)]; + let mut vertices = vec![]; + + for point in [from, to].iter() { + for t in trans.iter() { + vertices.push(( + (f64::from(point.0) + t.0) as i32, + (f64::from(point.1) + t.1) as i32, + )) + } + + trans.swap(0, 1); + } + + return back.fill_polygon(vertices, style); + } + + if from.0 == to.0 { + if from.1 > to.1 { + std::mem::swap(&mut from, &mut to); + } + for y in from.1..=to.1 { + check_result!(back.draw_pixel((from.0, y), style.color())); + } + return Ok(()); + } + + if from.1 == to.1 { + if from.0 > to.0 { + std::mem::swap(&mut from, &mut to); + } + for x in from.0..=to.0 { + check_result!(back.draw_pixel((x, from.1), style.color())); + } + return Ok(()); + } + + let steep = (from.0 - to.0).abs() < (from.1 - to.1).abs(); + + if steep { + from = (from.1, from.0); + to = (to.1, to.0); + } + + let (from, to) = if from.0 > to.0 { + (to, from) + } else { + (from, to) + }; + + let mut size_limit = back.get_size(); + + if steep { + size_limit = (size_limit.1, size_limit.0); + } + + let grad = f64::from(to.1 - from.1) / f64::from(to.0 - from.0); + + let mut put_pixel = |(x, y): BackendCoord, b: f64| { + if steep { + back.draw_pixel((y, x), style.color().mix(b)) + } else { + back.draw_pixel((x, y), style.color().mix(b)) + } + }; + + let y_step_limit = + (f64::from(to.1.min(size_limit.1 as i32 - 1).max(0) - from.1) / grad).floor() as i32; + + let batch_start = (f64::from(from.1.min(size_limit.1 as i32 - 2).max(0) - from.1) / grad) + .abs() + .ceil() as i32 + + from.0; + + let batch_limit = + to.0.min(size_limit.0 as i32 - 2) + .min(from.0 + y_step_limit - 1); + + let mut y = f64::from(from.1) + f64::from(batch_start - from.0) * grad; + + for x in batch_start..=batch_limit { + check_result!(put_pixel((x, y as i32), 1.0 + y.floor() - y)); + check_result!(put_pixel((x, y as i32 + 1), y - y.floor())); + + y += grad; + } + + if to.0 >= batch_limit + 1 && y < f64::from(to.1) { + let x = batch_limit as i32 + 1; + if 1.0 + y.floor() - y > 1e-5 { + check_result!(put_pixel((x, y as i32), 1.0 + y.floor() - y)); + } + if y - y.floor() > 1e-5 && y + 1.0 < f64::from(to.1) { + check_result!(put_pixel((x, y as i32 + 1), y - y.floor())); + } + } + + Ok(()) +} diff --git a/src/rasterizer/mod.rs b/src/rasterizer/mod.rs new file mode 100644 index 0000000..11835f4 --- /dev/null +++ b/src/rasterizer/mod.rs @@ -0,0 +1,32 @@ +/*! # The built-in rasterizers. + + Plotters make a minimal backend ability assumption - which is drawing a pixel on + backend. And this is the rasterizer that utilize this minimal ability to build a + fully functioning backend. + +*/ + +// TODO: ? operator is very slow. See issue #58 for details +macro_rules! check_result { + ($e:expr) => { + let result = $e; + if result.is_err() { + return result; + } + }; +} + +mod line; +pub use line::draw_line; + +mod rect; +pub use rect::draw_rect; + +mod circle; +pub use circle::draw_circle; + +mod polygon; +pub use polygon::fill_polygon; + +mod path; +pub use path::polygonize; diff --git a/src/rasterizer/path.rs b/src/rasterizer/path.rs new file mode 100644 index 0000000..b2db356 --- /dev/null +++ b/src/rasterizer/path.rs @@ -0,0 +1,115 @@ +use crate::BackendCoord; + +fn get_dir_vector(from: BackendCoord, to: BackendCoord, flag: bool) -> ((f64, f64), (f64, f64)) { + let v = (i64::from(to.0 - from.0), i64::from(to.1 - from.1)); + let l = ((v.0 * v.0 + v.1 * v.1) as f64).sqrt(); + + let v = (v.0 as f64 / l, v.1 as f64 / l); + + if flag { + (v, (v.1, -v.0)) + } else { + (v, (-v.1, v.0)) + } +} + +fn compute_polygon_vertex(triple: &[BackendCoord; 3], d: f64) -> BackendCoord { + let (a_t, a_n) = get_dir_vector(triple[0], triple[1], false); + let (b_t, b_n) = get_dir_vector(triple[2], triple[1], true); + + let a_p = ( + f64::from(triple[1].0) + d * a_n.0, + f64::from(triple[1].1) + d * a_n.1, + ); + let b_p = ( + f64::from(triple[1].0) + d * b_n.0, + f64::from(triple[1].1) + d * b_n.1, + ); + + // u * a_t + a_p = v * b_t + b_p + // u * a_t.0 - v * b_t.0 = b_p.0 - a_p.0 + // u * a_t.1 - v * b_t.1 = b_p.1 - a_p.1 + if a_p.0 as i32 == b_p.0 as i32 && a_p.1 as i32 == b_p.1 as i32 { + return (a_p.0 as i32, a_p.1 as i32); + } + + let a0 = a_t.0; + let b0 = -b_t.0; + let c0 = b_p.0 - a_p.0; + let a1 = a_t.1; + let b1 = -b_t.1; + let c1 = b_p.1 - a_p.1; + + // This is the coner case that + if (a0 * b1 - a1 * b0).abs() < 1e-10 { + return (a_p.0 as i32, a_p.1 as i32); + } + + let u = (c0 * b1 - c1 * b0) / (a0 * b1 - a1 * b0); + + let x = a_p.0 + u * a_t.0; + let y = a_p.1 + u * a_t.1; + + (x.round() as i32, y.round() as i32) +} + +fn traverse_vertices<'a>( + mut vertices: impl Iterator<Item = &'a BackendCoord>, + width: u32, + mut op: impl FnMut(BackendCoord), +) { + let mut a = vertices.next().unwrap(); + let mut b = vertices.next().unwrap(); + + while a == b { + a = b; + if let Some(new_b) = vertices.next() { + b = new_b; + } else { + return; + } + } + + let (_, n) = get_dir_vector(*a, *b, false); + + op(( + (f64::from(a.0) + n.0 * f64::from(width) / 2.0).round() as i32, + (f64::from(a.1) + n.1 * f64::from(width) / 2.0).round() as i32, + )); + + let mut recent = [(0, 0), *a, *b]; + + for p in vertices { + if *p == recent[2] { + continue; + } + recent.swap(0, 1); + recent.swap(1, 2); + recent[2] = *p; + op(compute_polygon_vertex(&recent, f64::from(width) / 2.0)); + } + + let b = recent[1]; + let a = recent[2]; + + let (_, n) = get_dir_vector(a, b, true); + + op(( + (f64::from(a.0) + n.0 * f64::from(width) / 2.0).round() as i32, + (f64::from(a.1) + n.1 * f64::from(width) / 2.0).round() as i32, + )); +} + +/// Covert a path with >1px stroke width into polygon. +pub fn polygonize(vertices: &[BackendCoord], stroke_width: u32) -> Vec<BackendCoord> { + if vertices.len() < 2 { + return vec![]; + } + + let mut ret = vec![]; + + traverse_vertices(vertices.iter(), stroke_width, |v| ret.push(v)); + traverse_vertices(vertices.iter().rev(), stroke_width, |v| ret.push(v)); + + ret +} diff --git a/src/rasterizer/polygon.rs b/src/rasterizer/polygon.rs new file mode 100644 index 0000000..ce33c5c --- /dev/null +++ b/src/rasterizer/polygon.rs @@ -0,0 +1,242 @@ +use crate::{BackendCoord, BackendStyle, DrawingBackend, DrawingErrorKind}; + +use std::cmp::{Ord, Ordering, PartialOrd}; + +#[derive(Clone, Debug)] +struct Edge { + epoch: u32, + total_epoch: u32, + slave_begin: i32, + slave_end: i32, +} + +impl Edge { + fn horizontal_sweep(mut from: BackendCoord, mut to: BackendCoord) -> Option<Edge> { + if from.0 == to.0 { + return None; + } + + if from.0 > to.0 { + std::mem::swap(&mut from, &mut to); + } + + Some(Edge { + epoch: 0, + total_epoch: (to.0 - from.0) as u32, + slave_begin: from.1, + slave_end: to.1, + }) + } + + fn vertical_sweep(from: BackendCoord, to: BackendCoord) -> Option<Edge> { + Edge::horizontal_sweep((from.1, from.0), (to.1, to.0)) + } + + fn get_master_pos(&self) -> i32 { + (self.total_epoch - self.epoch) as i32 + } + + fn inc_epoch(&mut self) { + self.epoch += 1; + } + + fn get_slave_pos(&self) -> f64 { + f64::from(self.slave_begin) + + (i64::from(self.slave_end - self.slave_begin) * i64::from(self.epoch)) as f64 + / f64::from(self.total_epoch) + } +} + +impl PartialOrd for Edge { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + self.get_slave_pos().partial_cmp(&other.get_slave_pos()) + } +} + +impl PartialEq for Edge { + fn eq(&self, other: &Self) -> bool { + self.get_slave_pos() == other.get_slave_pos() + } +} + +impl Eq for Edge {} + +impl Ord for Edge { + fn cmp(&self, other: &Self) -> Ordering { + self.get_slave_pos() + .partial_cmp(&other.get_slave_pos()) + .unwrap() + } +} + +pub fn fill_polygon<DB: DrawingBackend, S: BackendStyle>( + back: &mut DB, + vertices: &[BackendCoord], + style: &S, +) -> Result<(), DrawingErrorKind<DB::ErrorType>> { + if let Some((x_span, y_span)) = + vertices + .iter() + .fold(None, |res: Option<((i32, i32), (i32, i32))>, (x, y)| { + Some( + res.map(|((min_x, max_x), (min_y, max_y))| { + ( + (min_x.min(*x), max_x.max(*x)), + (min_y.min(*y), max_y.max(*y)), + ) + }) + .unwrap_or(((*x, *x), (*y, *y))), + ) + }) + { + // First of all, let's handle the case that all the points is in a same vertical or + // horizontal line + if x_span.0 == x_span.1 || y_span.0 == y_span.1 { + return back.draw_line((x_span.0, y_span.0), (x_span.1, y_span.1), style); + } + + let horizontal_sweep = x_span.1 - x_span.0 > y_span.1 - y_span.0; + + let mut edges: Vec<_> = vertices + .iter() + .zip(vertices.iter().skip(1)) + .map(|(a, b)| (*a, *b)) + .collect(); + edges.push((vertices[vertices.len() - 1], vertices[0])); + edges.sort_by_key(|((x1, y1), (x2, y2))| { + if horizontal_sweep { + *x1.min(x2) + } else { + *y1.min(y2) + } + }); + + for edge in &mut edges.iter_mut() { + if horizontal_sweep { + if (edge.0).0 > (edge.1).0 { + std::mem::swap(&mut edge.0, &mut edge.1); + } + } else if (edge.0).1 > (edge.1).1 { + std::mem::swap(&mut edge.0, &mut edge.1); + } + } + + let (low, high) = if horizontal_sweep { x_span } else { y_span }; + + let mut idx = 0; + + let mut active_edge: Vec<Edge> = vec![]; + + for sweep_line in low..=high { + let mut new_vec = vec![]; + + for mut e in active_edge { + if e.get_master_pos() > 0 { + e.inc_epoch(); + new_vec.push(e); + } + } + + active_edge = new_vec; + + loop { + if idx >= edges.len() { + break; + } + let line = if horizontal_sweep { + (edges[idx].0).0 + } else { + (edges[idx].0).1 + }; + if line > sweep_line { + break; + } + + let edge_obj = if horizontal_sweep { + Edge::horizontal_sweep(edges[idx].0, edges[idx].1) + } else { + Edge::vertical_sweep(edges[idx].0, edges[idx].1) + }; + + if let Some(edge_obj) = edge_obj { + active_edge.push(edge_obj); + } + + idx += 1; + } + + active_edge.sort(); + + let mut first = None; + let mut second = None; + + for edge in active_edge.iter() { + if first.is_none() { + first = Some(edge.clone()) + } else if second.is_none() { + second = Some(edge.clone()) + } + + if let Some(a) = first.clone() { + if let Some(b) = second.clone() { + if a.get_master_pos() == 0 && b.get_master_pos() != 0 { + first = Some(b); + second = None; + continue; + } + + if a.get_master_pos() != 0 && b.get_master_pos() == 0 { + first = Some(a); + second = None; + continue; + } + + let from = a.get_slave_pos(); + let to = b.get_slave_pos(); + + if a.get_master_pos() == 0 && b.get_master_pos() == 0 && to - from > 1.0 { + first = None; + second = None; + continue; + } + + if horizontal_sweep { + check_result!(back.draw_line( + (sweep_line, from.ceil() as i32), + (sweep_line, to.floor() as i32), + &style.color(), + )); + check_result!(back.draw_pixel( + (sweep_line, from.floor() as i32), + style.color().mix(from.ceil() - from), + )); + check_result!(back.draw_pixel( + (sweep_line, to.ceil() as i32), + style.color().mix(to - to.floor()), + )); + } else { + check_result!(back.draw_line( + (from.ceil() as i32, sweep_line), + (to.floor() as i32, sweep_line), + &style.color(), + )); + check_result!(back.draw_pixel( + (from.floor() as i32, sweep_line), + style.color().mix(from.ceil() - from), + )); + check_result!(back.draw_pixel( + (to.ceil() as i32, sweep_line), + style.color().mix(to.floor() - to), + )); + } + + first = None; + second = None; + } + } + } + } + } + + Ok(()) +} diff --git a/src/rasterizer/rect.rs b/src/rasterizer/rect.rs new file mode 100644 index 0000000..cd6c774 --- /dev/null +++ b/src/rasterizer/rect.rs @@ -0,0 +1,57 @@ +use crate::{BackendCoord, BackendStyle, DrawingBackend, DrawingErrorKind}; + +pub fn draw_rect<B: DrawingBackend, S: BackendStyle>( + b: &mut B, + upper_left: BackendCoord, + bottom_right: BackendCoord, + style: &S, + fill: bool, +) -> Result<(), DrawingErrorKind<B::ErrorType>> { + if style.color().alpha == 0.0 { + return Ok(()); + } + let (upper_left, bottom_right) = ( + ( + upper_left.0.min(bottom_right.0), + upper_left.1.min(bottom_right.1), + ), + ( + upper_left.0.max(bottom_right.0), + upper_left.1.max(bottom_right.1), + ), + ); + + if fill { + if bottom_right.0 - upper_left.0 < bottom_right.1 - upper_left.1 { + for x in upper_left.0..=bottom_right.0 { + check_result!(b.draw_line((x, upper_left.1), (x, bottom_right.1), style)); + } + } else { + for y in upper_left.1..=bottom_right.1 { + check_result!(b.draw_line((upper_left.0, y), (bottom_right.0, y), style)); + } + } + } else { + b.draw_line( + (upper_left.0, upper_left.1), + (upper_left.0, bottom_right.1), + style, + )?; + b.draw_line( + (upper_left.0, upper_left.1), + (bottom_right.0, upper_left.1), + style, + )?; + b.draw_line( + (bottom_right.0, bottom_right.1), + (upper_left.0, bottom_right.1), + style, + )?; + b.draw_line( + (bottom_right.0, bottom_right.1), + (bottom_right.0, upper_left.1), + style, + )?; + } + Ok(()) +} |