diff options
Diffstat (limited to 'src/rasterizer/path.rs')
-rw-r--r-- | src/rasterizer/path.rs | 62 |
1 files changed, 49 insertions, 13 deletions
diff --git a/src/rasterizer/path.rs b/src/rasterizer/path.rs index b2db356..35ae604 100644 --- a/src/rasterizer/path.rs +++ b/src/rasterizer/path.rs @@ -1,5 +1,6 @@ use crate::BackendCoord; +// Compute the tanginal and normal vectors of the given straight line. 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(); @@ -13,10 +14,17 @@ fn get_dir_vector(from: BackendCoord, to: BackendCoord, flag: bool) -> ((f64, f6 } } -fn compute_polygon_vertex(triple: &[BackendCoord; 3], d: f64) -> BackendCoord { +// Compute the polygonized vertex of the given angle +// d is the distance between the polygon edge and the actual line. +// d can be negative, this will emit a vertex on the other side of the line. +fn compute_polygon_vertex(triple: &[BackendCoord; 3], d: f64, buf: &mut Vec<BackendCoord>) { + buf.clear(); + + // Compute the tanginal and normal vectors of the given straight line. 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); + // Compute a point that is d away from the line for line a and line b. let a_p = ( f64::from(triple[1].0) + d * a_n.0, f64::from(triple[1].1) + d * a_n.1, @@ -26,12 +34,25 @@ fn compute_polygon_vertex(triple: &[BackendCoord; 3], d: f64) -> BackendCoord { f64::from(triple[1].1) + d * b_n.1, ); + // If they are actually the same point, then the 3 points are colinear, so just emit the point. + if a_p.0 as i32 == b_p.0 as i32 && a_p.1 as i32 == b_p.1 as i32 { + buf.push((a_p.0 as i32, a_p.1 as i32)); + return; + } + + // So we are actually computing the intersection of two lines: + // a_p + u * a_t and b_p + v * b_t. + // We can solve the following vector equation: // u * a_t + a_p = v * b_t + b_p + // + // which is actually a equation system: // 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); - } + + // The following vars are coefficients of the linear equation system. + // a0*u + b0*v = c0 + // a1*u + b1*v = c1 + // in which x and y are the coordinates that two polygon edges intersect. let a0 = a_t.0; let b0 = -b_t.0; @@ -40,17 +61,30 @@ fn compute_polygon_vertex(triple: &[BackendCoord; 3], d: f64) -> BackendCoord { 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 mut x = f64::INFINITY; + let mut y = f64::INFINITY; + + // Well if the determinant is not 0, then we can actuall get a intersection point. + if (a0 * b1 - a1 * b0).abs() > f64::EPSILON { + let u = (c0 * b1 - c1 * b0) / (a0 * b1 - a1 * b0); - let u = (c0 * b1 - c1 * b0) / (a0 * b1 - a1 * b0); + x = a_p.0 + u * a_t.0; + y = a_p.1 + u * a_t.1; + } - let x = a_p.0 + u * a_t.0; - let y = a_p.1 + u * a_t.1; + let cross_product = a_t.0 * b_t.1 - a_t.1 * b_t.0; + if (cross_product < 0.0 && d < 0.0) || (cross_product > 0.0 && d > 0.0) { + // Then we are at the outter side of the angle, so we need to consider a cap. + let dist_square = (x - triple[1].0 as f64).powi(2) + (y - triple[1].1 as f64).powi(2); + // If the point is too far away from the line, we need to cap it. + if dist_square > d * d * 16.0 { + buf.push((a_p.0.round() as i32, a_p.1.round() as i32)); + buf.push((b_p.0.round() as i32, b_p.1.round() as i32)); + return; + } + } - (x.round() as i32, y.round() as i32) + buf.push((x.round() as i32, y.round() as i32)); } fn traverse_vertices<'a>( @@ -78,6 +112,7 @@ fn traverse_vertices<'a>( )); let mut recent = [(0, 0), *a, *b]; + let mut vertex_buf = Vec::with_capacity(3); for p in vertices { if *p == recent[2] { @@ -86,7 +121,8 @@ fn traverse_vertices<'a>( recent.swap(0, 1); recent.swap(1, 2); recent[2] = *p; - op(compute_polygon_vertex(&recent, f64::from(width) / 2.0)); + compute_polygon_vertex(&recent, f64::from(width) / 2.0, &mut vertex_buf); + vertex_buf.iter().cloned().for_each(&mut op); } let b = recent[1]; |